summaryrefslogtreecommitdiff
path: root/cipher/cipher.c
diff options
context:
space:
mode:
authorJussi Kivilinna <jussi.kivilinna@iki.fi>2021-07-28 12:26:00 +0300
committerJussi Kivilinna <jussi.kivilinna@iki.fi>2021-08-26 20:30:31 +0300
commit659a208cb065d686f60e2c4f51856f460d6b44f5 (patch)
tree92981640db56d3f0f015c16b8412ee5013952f50 /cipher/cipher.c
parent9e3b0446653fda6912e91fae84883cdbefdc2195 (diff)
downloadlibgcrypt-659a208cb065d686f60e2c4f51856f460d6b44f5.tar.gz
Add SIV mode (RFC 5297)
* cipher/Makefile.am: Add 'cipher-siv.c'. * cipher/cipher-ctr.c (_gcry_cipher_ctr_encrypt): Rename to _gcry_cipher_ctr_encrypt_ctx and add algo context parameter. (_gcry_cipher_ctr_encrypt): New using _gcry_cipher_ctr_encrypt_ctx. * cipher/cipher-internal.h (gcry_cipher_handle): Add 'u_mode.siv'. (_gcry_cipher_ctr_encrypt_ctx, _gcry_cipher_siv_encrypt) (_gcry_cipher_siv_decrypt, _gcry_cipher_siv_set_nonce) (_gcry_cipher_siv_authenticate, _gcry_cipher_siv_set_decryption_tag) (_gcry_cipher_siv_get_tag, _gcry_cipher_siv_check_tag) (_gcry_cipher_siv_setkey): New. * cipher/cipher-siv.c: New. * cipher/cipher.c (_gcry_cipher_open_internal, cipher_setkey) (cipher_reset, _gcry_cipher_setup_mode_ops, _gcry_cipher_info): Add GCRY_CIPHER_MODE_SIV handling. (_gcry_cipher_ctl): Add GCRYCTL_SET_DECRYPTION_TAG handling. * doc/gcrypt.texi: Add documentation for SIV mode. * src/gcrypt.h.in (GCRYCTL_SET_DECRYPTION_TAG): New. (GCRY_CIPHER_MODE_SIV): New. (gcry_cipher_set_decryption_tag): New. * tests/basic.c (check_siv_cipher): New. (check_cipher_modes): Add call for 'check_siv_cipher'. * tests/bench-slope.c (bench_encrypt_init): Use double size key for SIV mode. (bench_aead_encrypt_do_bench, bench_aead_decrypt_do_bench) (bench_aead_authenticate_do_bench): Reset cipher context on each run. (bench_aead_authenticate_do_bench): Support nonce-less operation. (bench_siv_encrypt_do_bench, bench_siv_decrypt_do_bench) (bench_siv_authenticate_do_bench, siv_encrypt_ops) (siv_decrypt_ops, siv_authenticate_ops): New. (cipher_modes): Add SIV mode benchmarks. (cipher_bench_one): Restrict SIV mode testing to 16 byte block-size. -- GnuPG-bug-id: T4486 Signed-off-by: Jussi Kivilinna <jussi.kivilinna@iki.fi>
Diffstat (limited to 'cipher/cipher.c')
-rw-r--r--cipher/cipher.c93
1 files changed, 90 insertions, 3 deletions
diff --git a/cipher/cipher.c b/cipher/cipher.c
index 1039dff7..a274466f 100644
--- a/cipher/cipher.c
+++ b/cipher/cipher.c
@@ -550,6 +550,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
case GCRY_CIPHER_MODE_CMAC:
case GCRY_CIPHER_MODE_EAX:
case GCRY_CIPHER_MODE_GCM:
+ case GCRY_CIPHER_MODE_SIV:
if (!spec->encrypt || !spec->decrypt)
err = GPG_ERR_INV_CIPHER_MODE;
break;
@@ -609,6 +610,7 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
switch (mode)
{
case GCRY_CIPHER_MODE_XTS:
+ case GCRY_CIPHER_MODE_SIV:
/* Additional cipher context for tweak. */
size += 2 * spec->contextsize + 15;
break;
@@ -661,7 +663,12 @@ _gcry_cipher_open_internal (gcry_cipher_hd_t *handle,
tc = h->context.c + spec->contextsize * 2;
tc += (16 - (uintptr_t)tc % 16) % 16;
h->u_mode.xts.tweak_context = tc;
+ break;
+ case GCRY_CIPHER_MODE_SIV:
+ tc = h->context.c + spec->contextsize * 2;
+ tc += (16 - (uintptr_t)tc % 16) % 16;
+ h->u_mode.siv.ctr_context = tc;
break;
default:
@@ -731,6 +738,13 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
return GPG_ERR_WEAK_KEY;
}
}
+ else if (c->mode == GCRY_CIPHER_MODE_SIV)
+ {
+ /* SIV uses two keys. */
+ if (keylen % 2)
+ return GPG_ERR_INV_KEYLEN;
+ keylen /= 2;
+ }
rc = c->spec->setkey (&c->context.c, key, keylen, &c->bulk);
if (!rc || (c->marks.allow_weak_key && rc == GPG_ERR_WEAK_KEY))
@@ -777,9 +791,22 @@ cipher_setkey (gcry_cipher_hd_t c, byte *key, size_t keylen)
c->marks.key = 0;
break;
+ case GCRY_CIPHER_MODE_SIV:
+ /* Setup CTR cipher with second part of SIV key. */
+ rc = _gcry_cipher_siv_setkey (c, key + keylen, keylen);
+ if (!rc || (c->marks.allow_weak_key && rc == GPG_ERR_WEAK_KEY))
+ {
+ /* Duplicate initial CTR context. */
+ memcpy (c->u_mode.siv.ctr_context + c->spec->contextsize,
+ c->u_mode.siv.ctr_context, c->spec->contextsize);
+ }
+ else
+ c->marks.key = 0;
+ break;
+
default:
break;
- };
+ }
}
else
c->marks.key = 0;
@@ -876,14 +903,25 @@ cipher_reset (gcry_cipher_hd_t c)
break;
case GCRY_CIPHER_MODE_OCB:
- /* Do not clear precalculated L-values */
{
+ const size_t table_maxblks = 1 << OCB_L_TABLE_SIZE;
byte *u_mode_head_pos = (void *)&c->u_mode.ocb;
byte *u_mode_tail_pos = (void *)&c->u_mode.ocb.tag;
size_t u_mode_head_length = u_mode_tail_pos - u_mode_head_pos;
size_t u_mode_tail_length = sizeof(c->u_mode.ocb) - u_mode_head_length;
- memset (u_mode_tail_pos, 0, u_mode_tail_length);
+ if (c->u_mode.ocb.aad_nblocks < table_maxblks)
+ {
+ /* Precalculated L-values are still ok after reset, no need
+ * to clear. */
+ memset (u_mode_tail_pos, 0, u_mode_tail_length);
+ }
+ else
+ {
+ /* Reinitialize L table. */
+ memset (&c->u_mode.ocb, 0, sizeof(c->u_mode.ocb));
+ _gcry_cipher_ocb_setkey (c);
+ }
/* Setup default taglen. */
c->u_mode.ocb.taglen = 16;
@@ -896,6 +934,24 @@ cipher_reset (gcry_cipher_hd_t c)
c->spec->contextsize);
break;
+ case GCRY_CIPHER_MODE_SIV:
+ /* Only clear head of u_mode, keep s2v_cmac and ctr_context. */
+ {
+ byte *u_mode_pos = (void *)&c->u_mode;
+ byte *tail_pos = (void *)&c->u_mode.siv.s2v_cmac;
+ size_t u_mode_head_length = tail_pos - u_mode_pos;
+
+ memset (&c->u_mode, 0, u_mode_head_length);
+
+ memcpy (c->u_mode.siv.ctr_context,
+ c->u_mode.siv.ctr_context + c->spec->contextsize,
+ c->spec->contextsize);
+
+ memcpy (c->u_mode.siv.s2v_d, c->u_mode.siv.s2v_zero_block,
+ GCRY_SIV_BLOCK_LEN);
+ }
+ break;
+
default:
break; /* u_mode unused by other modes. */
}
@@ -1314,6 +1370,11 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
c->mode_ops.decrypt = _gcry_cipher_xts_decrypt;
break;
+ case GCRY_CIPHER_MODE_SIV:
+ c->mode_ops.encrypt = _gcry_cipher_siv_encrypt;
+ c->mode_ops.decrypt = _gcry_cipher_siv_decrypt;
+ break;
+
default:
c->mode_ops.encrypt = do_encrypt_none_unknown;
c->mode_ops.decrypt = do_decrypt_none_unknown;
@@ -1343,6 +1404,10 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
c->mode_ops.setiv = _gcry_cipher_ocb_set_nonce;
break;
+ case GCRY_CIPHER_MODE_SIV:
+ c->mode_ops.setiv = _gcry_cipher_siv_set_nonce;
+ break;
+
default:
c->mode_ops.setiv = cipher_setiv;
break;
@@ -1388,6 +1453,12 @@ _gcry_cipher_setup_mode_ops(gcry_cipher_hd_t c, int mode)
c->mode_ops.check_tag = _gcry_cipher_ocb_check_tag;
break;
+ case GCRY_CIPHER_MODE_SIV:
+ c->mode_ops.authenticate = _gcry_cipher_siv_authenticate;
+ c->mode_ops.get_tag = _gcry_cipher_siv_get_tag;
+ c->mode_ops.check_tag = _gcry_cipher_siv_check_tag;
+ break;
+
default:
c->mode_ops.authenticate = NULL;
c->mode_ops.get_tag = NULL;
@@ -1462,6 +1533,18 @@ _gcry_cipher_ctl (gcry_cipher_hd_t h, int cmd, void *buffer, size_t buflen)
}
break;
+ case GCRYCTL_SET_DECRYPTION_TAG:
+ {
+ if (!buffer)
+ return GPG_ERR_INV_ARG;
+
+ if (h->mode == GCRY_CIPHER_MODE_SIV)
+ rc = _gcry_cipher_siv_set_decryption_tag (h, buffer, buflen);
+ else
+ rc = GPG_ERR_INV_CIPHER_MODE;
+ }
+ break;
+
case GCRYCTL_SET_TAGLEN:
if (!h || !buffer || buflen != sizeof(int) )
return GPG_ERR_INV_ARG;
@@ -1595,6 +1678,10 @@ _gcry_cipher_info (gcry_cipher_hd_t h, int cmd, void *buffer, size_t *nbytes)
*nbytes = POLY1305_TAGLEN;
break;
+ case GCRY_CIPHER_MODE_SIV:
+ *nbytes = GCRY_SIV_BLOCK_LEN;
+ break;
+
default:
rc = GPG_ERR_INV_CIPHER_MODE;
break;