From 5caf0ad1877170bf4773c5757ccbef9fd97b5c81 Mon Sep 17 00:00:00 2001 From: Miroslav Lichvar Date: Mon, 3 Oct 2022 17:28:39 +0200 Subject: [PATCH] siv: add support for AES-128-GCM-SIV in Nettle This is a newer nonce misuse-resistant cipher specified in RFC 8452, which is now supported in the development code of the Nettle library. The advantages over AES-SIV-CMAC-256 are shorter keys and better performance. --- configure | 11 ++++- siv_nettle.c | 123 +++++++++++++++++++++++++++++++++++++++--------- test/unit/siv.c | 104 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 210 insertions(+), 28 deletions(-) diff --git a/configure b/configure index fd777c8..e8d7556 100755 --- a/configure +++ b/configure @@ -979,15 +979,22 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ]; then gnutls_priority_init2((void *)1, "", NULL, GNUTLS_PRIORITY_INIT_DEF_APPEND) + gnutls_prf_rfc5705((void *)1, 0, "", 0, "", 16, (void *)2);' then - if test_code 'SIV in nettle' \ + if test_code 'AES-SIV-CMAC in nettle' \ 'nettle/siv-cmac.h' "" "$LIBS" \ 'siv_cmac_aes128_set_key((void *)1, (void *)2);' then EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o" add_def HAVE_SIV add_def HAVE_NETTLE_SIV_CMAC + if test_code 'AES-GCM-SIV in nettle' \ + 'nettle/siv-gcm.h' "" "$LIBS" \ + 'siv_gcm_aes128_encrypt_message((void *)1, 0, NULL, 0, (void *)2, 16, (void *)3, + (void *)4);' + then + add_def HAVE_NETTLE_SIV_GCM + fi else - if test_code 'SIV in gnutls' 'gnutls/crypto.h' \ + if test_code 'AES-SIV-CMAC in gnutls' 'gnutls/crypto.h' \ "$test_cflags" "$test_link $LIBS" ' return gnutls_aead_cipher_init((void *)1, GNUTLS_CIPHER_AES_128_SIV, (void *)2);' then diff --git a/siv_nettle.c b/siv_nettle.c index d8f8b23..04bc9ad 100644 --- a/siv_nettle.c +++ b/siv_nettle.c @@ -34,12 +34,25 @@ #include "siv_nettle_int.c" #endif +#ifdef HAVE_NETTLE_SIV_GCM +#include +#endif + #include "memory.h" #include "siv.h" struct SIV_Instance_Record { - struct siv_cmac_aes128_ctx siv; + SIV_Algorithm algorithm; int key_set; + int min_nonce_length; + int max_nonce_length; + int tag_length; + union { + struct siv_cmac_aes128_ctx cmac_aes128; +#ifdef HAVE_NETTLE_SIV_GCM + struct aes128_ctx aes128; +#endif + } ctx; }; /* ================================================== */ @@ -49,12 +62,30 @@ SIV_CreateInstance(SIV_Algorithm algorithm) { SIV_Instance instance; - if (algorithm != AEAD_AES_SIV_CMAC_256) + if (SIV_GetKeyLength(algorithm) <= 0) return NULL; instance = MallocNew(struct SIV_Instance_Record); + instance->algorithm = algorithm; instance->key_set = 0; + switch (algorithm) { + case AEAD_AES_SIV_CMAC_256: + instance->min_nonce_length = SIV_MIN_NONCE_SIZE; + instance->max_nonce_length = INT_MAX; + instance->tag_length = SIV_DIGEST_SIZE; + break; +#ifdef HAVE_NETTLE_SIV_GCM + case AEAD_AES_128_GCM_SIV: + instance->min_nonce_length = SIV_GCM_NONCE_SIZE; + instance->max_nonce_length = SIV_GCM_NONCE_SIZE; + instance->tag_length = SIV_GCM_DIGEST_SIZE; + break; +#endif + default: + assert(0); + } + return instance; } @@ -71,11 +102,18 @@ SIV_DestroyInstance(SIV_Instance instance) int SIV_GetKeyLength(SIV_Algorithm algorithm) { - assert(32 <= SIV_MAX_KEY_LENGTH); + assert(2 * AES128_KEY_SIZE <= SIV_MAX_KEY_LENGTH); - if (algorithm == AEAD_AES_SIV_CMAC_256) - return 32; - return 0; + switch (algorithm) { + case AEAD_AES_SIV_CMAC_256: + return 2 * AES128_KEY_SIZE; +#ifdef HAVE_NETTLE_SIV_GCM + case AEAD_AES_128_GCM_SIV: + return AES128_KEY_SIZE; +#endif + default: + return 0; + } } /* ================================================== */ @@ -83,10 +121,21 @@ SIV_GetKeyLength(SIV_Algorithm algorithm) int SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length) { - if (length != 32) + if (length != SIV_GetKeyLength(instance->algorithm)) return 0; - siv_cmac_aes128_set_key(&instance->siv, key); + switch (instance->algorithm) { + case AEAD_AES_SIV_CMAC_256: + siv_cmac_aes128_set_key(&instance->ctx.cmac_aes128, key); + break; +#ifdef HAVE_NETTLE_SIV_GCM + case AEAD_AES_128_GCM_SIV: + aes128_set_encrypt_key(&instance->ctx.aes128, key); + break; +#endif + default: + assert(0); + } instance->key_set = 1; @@ -98,9 +147,9 @@ SIV_SetKey(SIV_Instance instance, const unsigned char *key, int length) int SIV_GetTagLength(SIV_Instance instance) { - assert(SIV_DIGEST_SIZE <= SIV_MAX_TAG_LENGTH); - - return SIV_DIGEST_SIZE; + if (instance->tag_length < 1 || instance->tag_length > SIV_MAX_TAG_LENGTH) + assert(0); + return instance->tag_length; } /* ================================================== */ @@ -115,16 +164,31 @@ SIV_Encrypt(SIV_Instance instance, if (!instance->key_set) return 0; - if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || + if (nonce_length < instance->min_nonce_length || + nonce_length > instance->max_nonce_length || assoc_length < 0 || plaintext_length < 0 || plaintext_length > ciphertext_length || - plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) + plaintext_length + SIV_GetTagLength(instance) != ciphertext_length) return 0; assert(assoc && plaintext); - siv_cmac_aes128_encrypt_message(&instance->siv, nonce_length, nonce, - assoc_length, assoc, - ciphertext_length, ciphertext, plaintext); + switch (instance->algorithm) { + case AEAD_AES_SIV_CMAC_256: + siv_cmac_aes128_encrypt_message(&instance->ctx.cmac_aes128, + nonce_length, nonce, assoc_length, assoc, + ciphertext_length, ciphertext, plaintext); + break; +#ifdef HAVE_NETTLE_SIV_GCM + case AEAD_AES_128_GCM_SIV: + siv_gcm_aes128_encrypt_message(&instance->ctx.aes128, + nonce_length, nonce, assoc_length, assoc, + ciphertext_length, ciphertext, plaintext); + break; +#endif + default: + assert(0); + } + return 1; } @@ -140,17 +204,32 @@ SIV_Decrypt(SIV_Instance instance, if (!instance->key_set) return 0; - if (nonce_length < SIV_MIN_NONCE_SIZE || assoc_length < 0 || + if (nonce_length < instance->min_nonce_length || + nonce_length > instance->max_nonce_length || assoc_length < 0 || plaintext_length < 0 || plaintext_length > ciphertext_length || - plaintext_length + SIV_DIGEST_SIZE != ciphertext_length) + plaintext_length + SIV_GetTagLength(instance) != ciphertext_length) return 0; assert(assoc && plaintext); - if (!siv_cmac_aes128_decrypt_message(&instance->siv, nonce_length, nonce, - assoc_length, assoc, - plaintext_length, plaintext, ciphertext)) - return 0; + switch (instance->algorithm) { + case AEAD_AES_SIV_CMAC_256: + if (!siv_cmac_aes128_decrypt_message(&instance->ctx.cmac_aes128, + nonce_length, nonce, assoc_length, assoc, + plaintext_length, plaintext, ciphertext)) + return 0; + break; +#ifdef HAVE_NETTLE_SIV_GCM + case AEAD_AES_128_GCM_SIV: + if (!siv_gcm_aes128_decrypt_message(&instance->ctx.aes128, + nonce_length, nonce, assoc_length, assoc, + plaintext_length, plaintext, ciphertext)) + return 0; + break; +#endif + default: + assert(0); + } return 1; } diff --git a/test/unit/siv.c b/test/unit/siv.c index 76846c8..2465c68 100644 --- a/test/unit/siv.c +++ b/test/unit/siv.c @@ -125,15 +125,94 @@ test_unit(void) "\x8d\x49\x2f\x14\x62\xa4\x7c\x2a\x57\x38\x87\xce\xc6\x72\xd3\x5c" "\xa1", 97 }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "", 0, + "", 0, + "\xba\x05\x1c\x40\xeb\x7e\x5f\xa2\x3f\x6c\xe5\xbe\xfe\x5b\x04\xad", 16 + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "", 0, + "\x8f\x47\xfe\x1f\x26\x4e\xe2\x99\x5f\x35\x3d\x26\x74\x14\xd4\x3b", 16 + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "", 0, + "\xba\x05\x1c\x40\xeb\x7e\x5f\xa2\x3f\x6c\xe5\xbe\xfe\x5b\x04\xad", 16, + "\xa1\xc6\x1b\xf7\x32\x39\x93\x0e\x10\xf8\xa6\x21\x6c\x6e\x26\x83" + "\x5c\xa9\xb0\xdd\x91\x0f\x81\xa6\xf0\x3b\x45\xda\xa6\x9a\x2b\x24", 32, + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c", 15, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4", 15, + "\x7a\x23\xa7\x35\x8d\x34\x5b\xf6\x0d\xa7\x6d\x3b\x58\x8c\x4c\x65" + "\xd9\x85\x4e\x17\xb7\x52\x48\xf7\x91\xb4\xdd\xd6\x8b\xec\x02", 31 + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b", 16, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7", 16, + "\xa3\x10\xae\x5f\x26\xd9\x90\xfa\xab\x30\x29\x80\x7f\x93\x62\x23" + "\x83\x8f\xc9\x57\x90\xbb\x05\x87\x02\x11\x57\xd6\x13\x9b\x82\x4d", 32 + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b", 12, + "\x4c\x9d\x4f\xca\xed\x8a\xe2\xba\xad\x3f\x3e\xa6\xe9\x3c\x8c\x8b" + "\xa0", 17, + "\xba\x99\x79\x31\x23\x7e\x3c\x53\x58\x7e\xd4\x93\x02\xab\xe4\xa7" + "\x08", 17, + "\x4c\x48\x67\x48\xce\x8b\x14\x7b\x70\xac\x71\xe8\x7b\x4e\x4a\x6a" + "\xb4\x3d\xb5\x8e\x58\x81\xfc\x3e\x97\xcd\xdf\xef\x67\x1e\xf4\x4f" + "\x0d", 33 + }, + { AEAD_AES_128_GCM_SIV, + "\x01\x23\x45\x67\x89\xab\xcd\xef\xf0\x12\x34\x56\x78\x9a\xbc\xde", 16, + "\xb0\x5a\x1b\xc7\x56\xe7\xb6\x2c\xb4\x85\xe5\x56", 12, + "\xe5\x8b\xd2\x6a\x30\xc5\xc5\x61\xcc\xbd\x7c\x27\xbf\xfe\xf9\x06" + "\x00\x5b\xd7\xfc\x11\x0b\xcf\x16\x61\xef\xac\x05\xa7\xaf\xec\x27" + "\x41\xc8\x5e\x9e\x0d\xf9\x2f\xaf\x20\x79\x17\xe5\x17\x91\x2a\x27" + "\x34\x1c\xbc\xaf\xeb\xef\x7f\x52\xe7\x1e\x4c\x2a\xca\xbd\x2b\xbe" + "\x34\xd6\xfb\x69\xd3\x3e\x49\x59\x60\xb4\x26\xc9\xb8\xce\xba", 79, + "\x6c\xe7\xcf\x7e\xab\x7b\xa0\xe1\xa7\x22\xcb\x88\xde\x5e\x42\xd2" + "\xec\x79\xe0\xa2\xcf\x5f\x0f\x6f\x6b\x89\x57\xcd\xae\x17\xd4\xc2" + "\xf3\x1b\xa2\xa8\x13\x78\x23\x2f\x83\xa8\xd4\x0c\xc0\xd2\xf3\x99" + "\xae\x81\xa1\xca\x5b\x5f\x45\xa6\x6f\x0c\x8a\xf3\xd4\x67\x40\x81" + "\x26\xe2\x01\x86\xe8\x5a\xd5\xf8\x58\x80\x9f\x56\xaa\x76\x96\xbf" + "\x31", 81, + "\xf6\xa0\x1a\xf3\x4f\xe9\x36\xde\x5c\xbd\xb6\x0a\x26\x9d\x60\x1d" + "\xe6\xc9\x6d\xb8\xf2\x5f\xcd\xce\x26\xf4\x0d\x86\xec\xdd\x84\x25" + "\xaf\xec\x72\x10\x2d\x74\x2d\xde\x95\x84\xac\xce\xbf\x8a\x52\x9f" + "\x10\x6f\xc2\xa8\x1f\xed\x47\xff\xeb\x28\x57\x54\xb3\x45\x45\x56" + "\xbb\xcf\x7d\x9b\x99\x68\xbd\x36\x75\xe3\xf7\x8c\x09\x25\x01\xbe" + "\xe1\xe2\x3d\x19\x4f\x15\x64\x12\x6e\xea\x67\x6c\x42\x2f\xc1\x91" + "\xff", 97 + }, { 0, "", 0 } }; unsigned char plaintext[sizeof (((struct siv_test *)NULL)->plaintext)]; unsigned char ciphertext[sizeof (((struct siv_test *)NULL)->ciphertext)]; SIV_Instance siv; - int i, j, r; + int i, j, r, fixed_nonce_length; - TEST_CHECK(SIV_CreateInstance(0) == NULL); + for (i = 0; i < AEAD_AES_256_GCM_SIV + 10; i++) { + switch (i) { + case AEAD_AES_SIV_CMAC_256: + case AEAD_AES_128_GCM_SIV: + continue; + } + TEST_CHECK(SIV_GetKeyLength(i) == 0); + TEST_CHECK(SIV_CreateInstance(i) == NULL); + } for (i = 0; tests[i].algorithm != 0; i++) { DEBUG_LOG("testing %d (%d)", (int)tests[i].algorithm, i); @@ -145,7 +224,24 @@ test_unit(void) assert(tests[i].ciphertext_length <= sizeof (tests[i].ciphertext)); siv = SIV_CreateInstance(tests[i].algorithm); - TEST_CHECK(siv != NULL); + + switch (tests[i].algorithm) { + case AEAD_AES_SIV_CMAC_256: + TEST_CHECK(siv != NULL); + fixed_nonce_length = 0; + break; + case AEAD_AES_128_GCM_SIV: + fixed_nonce_length = 1; + break; + default: + assert(0); + } + + if (!siv) { + DEBUG_LOG("missing %d support", (int)tests[i].algorithm); + TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == 0); + continue; + } TEST_CHECK(SIV_GetKeyLength(tests[i].algorithm) == tests[i].key_length); @@ -188,7 +284,7 @@ test_unit(void) tests[i].assoc, tests[i].assoc_length, tests[i].plaintext, tests[i].plaintext_length, ciphertext, tests[i].ciphertext_length); - if (j > 0) { + if (j > 0 && (j == tests[i].nonce_length || !fixed_nonce_length)) { TEST_CHECK(r); TEST_CHECK(memcmp(ciphertext, tests[i].ciphertext, tests[i].ciphertext_length) != 0); } else {