summaryrefslogtreecommitdiff
path: root/cipher/cipher.c
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@iki.fi>2013-10-22 17:07:53 +0300
committerJussi Kivilinna <jussi.kivilinna@iki.fi>2013-10-22 19:48:19 +0300
commit335d9bf7b035815750b63a3a8334d6ce44dc4449 (patch)
tree75a9ff903f9b7d2bd3f92dc459962ac7741e68c8 /cipher/cipher.c
parent95654041f2aa62f71aac4d8614dafe8433d10f95 (diff)
downloadlibgcrypt-335d9bf7b035815750b63a3a8334d6ce44dc4449.tar.gz
Add Counter with CBC-MAC mode (CCM)
* cipher/Makefile.am: Add 'cipher-ccm.c'. * cipher/cipher-ccm.c: New. * cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_mode'. (_gcry_cipher_ccm_encrypt, _gcry_cipher_ccm_decrypt) (_gcry_cipher_ccm_set_nonce, _gcry_cipher_ccm_authenticate) (_gcry_cipher_ccm_get_tag, _gcry_cipher_ccm_check_tag) (_gcry_cipher_ccm_set_lengths): New prototypes. * cipher/cipher.c (gcry_cipher_open, cipher_encrypt, cipher_decrypt) (_gcry_cipher_setiv, _gcry_cipher_authenticate, _gcry_cipher_gettag) (_gcry_cipher_checktag, gry_cipher_ctl): Add handling for CCM mode. * doc/gcrypt.texi: Add documentation for GCRY_CIPHER_MODE_CCM. * src/gcrypt.h.in (gcry_cipher_modes): Add 'GCRY_CIPHER_MODE_CCM'. (gcry_ctl_cmds): Add 'GCRYCTL_SET_CCM_LENGTHS'. (GCRY_CCM_BLOCK_LEN): New. * tests/basic.c (check_ccm_cipher): New. (check_cipher_modes): Call 'check_ccm_cipher'. * tests/benchmark.c (ccm_aead_init): New. (cipher_bench): Add handling for AEAD modes and add CCM benchmarking. -- Patch adds CCM (Counter with CBC-MAC) mode as defined in RFC 3610 and NIST Special Publication 800-38C. Example for encrypting message (split in two buffers; buf1, buf2) and authenticating additional non-encrypted data (split in two buffers; aadbuf1, aadbuf2) with authentication tag length of eigth bytes: size_t params[3]; taglen = 8; gcry_cipher_setkey(h, key, len(key)); gcry_cipher_setiv(h, nonce, len(nonce)); params[0] = len(buf1) + len(buf2); /* 0: enclen */ params[1] = len(aadbuf1) + len(aadbuf2); /* 1: aadlen */ params[2] = taglen; /* 2: authtaglen */ gcry_cipher_ctl(h, GCRYCTL_SET_CCM_LENGTHS, params, sizeof(size_t) * 3); gcry_cipher_authenticate(h, aadbuf1, len(aadbuf1)); gcry_cipher_authenticate(h, aadbuf2, len(aadbuf2)); gcry_cipher_encrypt(h, buf1, len(buf1), buf1, len(buf1)); gcry_cipher_encrypt(h, buf2, len(buf2), buf2, len(buf2)); gcry_cipher_gettag(h, tag, taglen); Example for decrypting above message and checking authentication tag: size_t params[3]; taglen = 8; gcry_cipher_setkey(h, key, len(key)); gcry_cipher_setiv(h, nonce, len(nonce)); params[0] = len(buf1) + len(buf2); /* 0: enclen */ params[1] = len(aadbuf1) + len(aadbuf2); /* 1: aadlen */ params[2] = taglen; /* 2: authtaglen */ gcry_cipher_ctl(h, GCRYCTL_SET_CCM_LENGTHS, params, sizeof(size_t) * 3); gcry_cipher_authenticate(h, aadbuf1, len(aadbuf1)); gcry_cipher_authenticate(h, aadbuf2, len(aadbuf2)); gcry_cipher_decrypt(h, buf1, len(buf1), buf1, len(buf1)); gcry_cipher_decrypt(h, buf2, len(buf2), buf2, len(buf2)); err = gcry_cipher_checktag(h, tag, taglen); if (gpg_err_code (err) == GPG_ERR_CHECKSUM) { /* Authentication failed. */ } else if (err == 0) { /* Authentication ok. */ } Example for encrypting message without additional authenticated data: size_t params[3]; taglen = 10; gcry_cipher_setkey(h, key, len(key)); gcry_cipher_setiv(h, nonce, len(nonce)); params[0] = len(buf1); /* 0: enclen */ params[1] = 0; /* 1: aadlen */ params[2] = taglen; /* 2: authtaglen */ gcry_cipher_ctl(h, GCRYCTL_SET_CCM_LENGTHS, params, sizeof(size_t) * 3); gcry_cipher_encrypt(h, buf1, len(buf1), buf1, len(buf1)); gcry_cipher_gettag(h, tag, taglen); To reset CCM state for cipher handle, one can either set new nonce or use 'gcry_cipher_reset'. This implementation reuses existing CTR mode code for encryption/decryption and is there for able to process multiple buffers that are not multiple of blocksize. AAD data maybe also be passed into gcry_cipher_authenticate in non-blocksize chunks. [v4]: GCRYCTL_SET_CCM_PARAMS => GCRY_SET_CCM_LENGTHS Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
Diffstat (limited to 'cipher/cipher.c')
-rw-r--r--cipher/cipher.c107
1 files changed, 93 insertions, 14 deletions
diff --git a/cipher/cipher.c b/cipher/cipher.c
index d6d3021c..5214d26c 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -375,6 +375,13 @@ gcry_cipher_open (gcry_cipher_hd_t *handle,
if (! err)
switch (mode)
{
+ case GCRY_CIPHER_MODE_CCM:
+ if (spec->blocksize != GCRY_CCM_BLOCK_LEN)
+ err = GPG_ERR_INV_CIPHER_MODE;
+ if (!spec->encrypt || !spec->decrypt)
+ err = GPG_ERR_INV_CIPHER_MODE;
+ break;
+
case GCRY_CIPHER_MODE_ECB:
case GCRY_CIPHER_MODE_CBC:
case GCRY_CIPHER_MODE_CFB:
@@ -613,6 +620,8 @@ cipher_reset (gcry_cipher_hd_t c)
memset (c->u_iv.iv, 0, c->spec->blocksize);
memset (c->lastiv, 0, c->spec->blocksize);
memset (c->u_ctr.ctr, 0, c->spec->blocksize);
+ memset (&c->u_mode, 0, sizeof c->u_mode);
+ c->unused = 0;
}
@@ -718,6 +727,10 @@ cipher_encrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
inbuf, inbuflen);
break;
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_encrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+ break;
+
case GCRY_CIPHER_MODE_STREAM:
c->spec->stencrypt (&c->context.c,
outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -811,6 +824,10 @@ cipher_decrypt (gcry_cipher_hd_t c, byte *outbuf, unsigned int outbuflen,
inbuf, inbuflen);
break;
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_decrypt (c, outbuf, outbuflen, inbuf, inbuflen);
+ break;
+
case GCRY_CIPHER_MODE_STREAM:
c->spec->stdecrypt (&c->context.c,
outbuf, (byte*)/*arggg*/inbuf, inbuflen);
@@ -885,8 +902,19 @@ _gcry_cipher_setkey (gcry_cipher_hd_t hd, const void *key, size_t keylen)
gcry_error_t
_gcry_cipher_setiv (gcry_cipher_hd_t hd, const void *iv, size_t ivlen)
{
- cipher_setiv (hd, iv, ivlen);
- return 0;
+ gcry_err_code_t rc = GPG_ERR_NO_ERROR;
+
+ switch (hd->mode)
+ {
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_set_nonce (hd, iv, ivlen);
+ break;
+
+ default:
+ cipher_setiv (hd, iv, ivlen);
+ break;
+ }
+ return gpg_error (rc);
}
/* Set counter for CTR mode. (CTR,CTRLEN) must denote a buffer of
@@ -914,34 +942,61 @@ gcry_error_t
_gcry_cipher_authenticate (gcry_cipher_hd_t hd, const void *abuf,
size_t abuflen)
{
- log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
+ gcry_err_code_t rc;
+
+ switch (hd->mode)
+ {
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_authenticate (hd, abuf, abuflen);
+ break;
- (void)abuf;
- (void)abuflen;
+ default:
+ log_error ("gcry_cipher_authenticate: invalid mode %d\n", hd->mode);
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ break;
+ }
- return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+ return gpg_error (rc);
}
gcry_error_t
_gcry_cipher_gettag (gcry_cipher_hd_t hd, void *outtag, size_t taglen)
{
- log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
+ gcry_err_code_t rc;
+
+ switch (hd->mode)
+ {
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_get_tag (hd, outtag, taglen);
+ break;
- (void)outtag;
- (void)taglen;
+ default:
+ log_error ("gcry_cipher_gettag: invalid mode %d\n", hd->mode);
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ break;
+ }
- return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+ return gpg_error (rc);
}
gcry_error_t
_gcry_cipher_checktag (gcry_cipher_hd_t hd, const void *intag, size_t taglen)
{
- log_error ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
+ gcry_err_code_t rc;
+
+ switch (hd->mode)
+ {
+ case GCRY_CIPHER_MODE_CCM:
+ rc = _gcry_cipher_ccm_check_tag (hd, intag, taglen);
+ break;
- (void)intag;
- (void)taglen;
+ default:
+ log_error ("gcry_cipher_checktag: invalid mode %d\n", hd->mode);
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ break;
+ }
- return gpg_error (GPG_ERR_INV_CIPHER_MODE);
+ return gpg_error (rc);
}
@@ -980,6 +1035,30 @@ gcry_cipher_ctl( gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
h->flags &= ~GCRY_CIPHER_CBC_MAC;
break;
+ case GCRYCTL_SET_CCM_LENGTHS:
+ {
+ size_t params[3];
+ size_t encryptedlen;
+ size_t aadlen;
+ size_t authtaglen;
+
+ if (h->mode != GCRY_CIPHER_MODE_CCM)
+ return gcry_error (GPG_ERR_INV_CIPHER_MODE);
+
+ if (!buffer || buflen != 3 * sizeof(size_t))
+ return gcry_error (GPG_ERR_INV_ARG);
+
+ /* This command is used to pass additional length parameters needed
+ by CCM mode to initialize CBC-MAC. */
+ memcpy (params, buffer, sizeof(params));
+ encryptedlen = params[0];
+ aadlen = params[1];
+ authtaglen = params[2];
+
+ rc = _gcry_cipher_ccm_set_lengths (h, encryptedlen, aadlen, authtaglen);
+ }
+ break;
+
case GCRYCTL_DISABLE_ALGO:
/* This command expects NULL for H and BUFFER to point to an
integer with the algo number. */