diff --git a/doc/crypt.tex b/doc/crypt.tex index e29cc2aba..d17b121d6 100644 --- a/doc/crypt.tex +++ b/doc/crypt.tex @@ -2689,6 +2689,113 @@ \subsection{One--Shot Packet} \end{verbatim} \end{small} +\mysection{GCM-SIV} +\label{GCM-SIV} + +AES--GCM--SIV is a nonce--misuse--resistant authenticated encryption mode defined by \url{https://tools.ietf.org/html/rfc8452}. +Unlike AES--GCM, reusing a nonce with the same key does not break confidentiality or authenticity. + +\textbf{There is no streaming API}. The algorithm needs two passes: during encryption, the tag is computed over the whole plaintext +and then used as the AES--CTR starting counter, so ciphertext cannot be produced early. During decryption, plaintext must not be +returned before the tag is verified. + +The following parameters are fixed by RFC 8452: +\begin{itemize} + \item \textbf{Cipher}: AES. Any 128--bit block cipher registered with libtomcrypt will work, but only AES is interoperable with the RFC. + \item \textbf{Key length}: 16 bytes (AEAD\_AES\_128\_GCM\_SIV) or 32 bytes (AEAD\_AES\_256\_GCM\_SIV). + \item \textbf{Nonce length}: exactly 12 bytes. + \item \textbf{Tag length}: exactly 16 bytes. + \item \textbf{Plaintext and AAD length}: at most $2^{36}$ bytes each. +\end{itemize} + +The implementation reuses the GHASH field multiplier (\textit{gcm\_gf\_mult()}) for POLYVAL via the byte--reversal trick from RFC 8452 Appendix A. +It therefore inherits the same hardware acceleration: Intel PCLMUL, ARMv8 PMULL, or the \texttt{LTC\_FAST} table fallback. + +\index{gcm\_siv\_memory()} +\begin{verbatim} +int gcm_siv_memory( int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *aad, unsigned long aadlen, + unsigned char *in, unsigned long inlen, + unsigned char *out, + unsigned char *tag, unsigned long *taglen, + int direction); +\end{verbatim} + +This runs a single AES--GCM--SIV operation in the given \textit{direction} (\texttt{LTC\_ENCRYPT} or \texttt{LTC\_DECRYPT}). + +\textit{cipher} is the index of a registered 128--bit block cipher; use \texttt{find\_cipher("aes")} for the standard mode. + +\textit{key} (of length \textit{keylen} = 16 or 32) is the key from which the message--authentication key and the message--encryption key +are derived, together with the 12--byte \textit{nonce}, as described in RFC 8452 Section 4. + +\textit{aad} of length \textit{aadlen} is the optional associated data. Pass \texttt{NULL} and \texttt{0} for an empty AAD. + +\textit{in} of length \textit{inlen} is the input buffer: +\begin{itemize} + \item on \texttt{LTC\_ENCRYPT} it holds the plaintext, and \textit{out} (of the same length) receives the ciphertext; + \item on \texttt{LTC\_DECRYPT} it holds the ciphertext, and \textit{out} receives the plaintext. +\end{itemize} + +\textit{in} and \textit{out} may point at the same buffer for in--place processing. + +\textit{tag} is the 16--byte authentication tag: +\begin{itemize} + \item on \texttt{LTC\_ENCRYPT} the function writes it and sets \textit{taglen} to \texttt{16}; + \item on \texttt{LTC\_DECRYPT} the caller must place the expected tag into \textit{tag} before calling. The function recomputes the tag, + compares the two in constant time, and on a mismatch returns \texttt{CRYPT\_ERROR} after zeroing \textit{out}. The decrypted + plaintext is never returned to the caller when authentication fails. +\end{itemize} + +\textit{taglen} must be at least \texttt{16} on input. + +A short worked example of encryption and decryption with AES--GCM--SIV follows. + +\begin{small} +\begin{verbatim} +#include + +int main(void) +{ + int err; + unsigned char key[32] = {0}; /* AEAD_AES_256_GCM_SIV */ + unsigned char nonce[12] = {0}; + unsigned char aad[] = "header"; + unsigned char plain[16] = {0}; + unsigned char ct[sizeof(plain)] = {0}; + unsigned char dec[sizeof(plain)] = {0}; + unsigned char tag[16]; + unsigned long taglen = sizeof(tag); + + register_cipher(&aes_desc); + + if ((err = gcm_siv_memory(find_cipher("aes"), + key, sizeof(key), + nonce, sizeof(nonce), + aad, sizeof(aad) - 1, + plain, sizeof(plain), ct, + tag, &taglen, + LTC_ENCRYPT)) != CRYPT_OK) { + whine_and_pout(err); + } + + if ((err = gcm_siv_memory(find_cipher("aes"), + key, sizeof(key), + nonce, sizeof(nonce), + aad, sizeof(aad) - 1, + ct, sizeof(ct), dec, + tag, &taglen, + LTC_DECRYPT)) != CRYPT_OK) { + /* CRYPT_ERROR here means the tag did not match -- dec has been zeroed */ + whine_and_pout(err); + } + + return EXIT_SUCCESS; +} +\end{verbatim} +\end{small} + \chapter{One-Way Cryptographic Hash Functions} \mysection{Core Functions} Like the ciphers, there are hash core functions and a universal data type to hold the hash state called \textit{hash\_state}. To initialize hash diff --git a/libtomcrypt_VS2008.vcproj b/libtomcrypt_VS2008.vcproj index 11cfac1d5..0b90903e1 100644 --- a/libtomcrypt_VS2008.vcproj +++ b/libtomcrypt_VS2008.vcproj @@ -735,6 +735,18 @@ > + + + + + + diff --git a/makefile.mingw b/makefile.mingw index df39d6fbc..1250ebf11 100644 --- a/makefile.mingw +++ b/makefile.mingw @@ -56,7 +56,8 @@ src/encauth/eax/eax_init.o src/encauth/eax/eax_test.o src/encauth/gcm/gcm_add_aa src/encauth/gcm/gcm_add_iv.o src/encauth/gcm/gcm_done.o src/encauth/gcm/gcm_gf_mult.o \ src/encauth/gcm/gcm_init.o src/encauth/gcm/gcm_memory.o src/encauth/gcm/gcm_mult_h.o \ src/encauth/gcm/gcm_process.o src/encauth/gcm/gcm_reset.o src/encauth/gcm/gcm_test.o \ -src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ +src/encauth/gcm_siv/gcm_siv.o src/encauth/gcm_siv/gcm_siv_test.o src/encauth/ocb3/ocb3_add_aad.o \ +src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \ src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \ src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \ diff --git a/makefile.msvc b/makefile.msvc index e9028e8ab..ad09c83e7 100644 --- a/makefile.msvc +++ b/makefile.msvc @@ -49,7 +49,8 @@ src/encauth/eax/eax_init.obj src/encauth/eax/eax_test.obj src/encauth/gcm/gcm_ad src/encauth/gcm/gcm_add_iv.obj src/encauth/gcm/gcm_done.obj src/encauth/gcm/gcm_gf_mult.obj \ src/encauth/gcm/gcm_init.obj src/encauth/gcm/gcm_memory.obj src/encauth/gcm/gcm_mult_h.obj \ src/encauth/gcm/gcm_process.obj src/encauth/gcm/gcm_reset.obj src/encauth/gcm/gcm_test.obj \ -src/encauth/ocb3/ocb3_add_aad.obj src/encauth/ocb3/ocb3_decrypt.obj src/encauth/ocb3/ocb3_decrypt_last.obj \ +src/encauth/gcm_siv/gcm_siv.obj src/encauth/gcm_siv/gcm_siv_test.obj src/encauth/ocb3/ocb3_add_aad.obj \ +src/encauth/ocb3/ocb3_decrypt.obj src/encauth/ocb3/ocb3_decrypt_last.obj \ src/encauth/ocb3/ocb3_decrypt_verify_memory.obj src/encauth/ocb3/ocb3_done.obj \ src/encauth/ocb3/ocb3_encrypt.obj src/encauth/ocb3/ocb3_encrypt_authenticate_memory.obj \ src/encauth/ocb3/ocb3_encrypt_last.obj src/encauth/ocb3/ocb3_init.obj src/encauth/ocb3/ocb3_int_ntz.obj \ diff --git a/makefile.unix b/makefile.unix index b07b43b8c..601b6ab6a 100644 --- a/makefile.unix +++ b/makefile.unix @@ -70,7 +70,8 @@ src/encauth/eax/eax_init.o src/encauth/eax/eax_test.o src/encauth/gcm/gcm_add_aa src/encauth/gcm/gcm_add_iv.o src/encauth/gcm/gcm_done.o src/encauth/gcm/gcm_gf_mult.o \ src/encauth/gcm/gcm_init.o src/encauth/gcm/gcm_memory.o src/encauth/gcm/gcm_mult_h.o \ src/encauth/gcm/gcm_process.o src/encauth/gcm/gcm_reset.o src/encauth/gcm/gcm_test.o \ -src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ +src/encauth/gcm_siv/gcm_siv.o src/encauth/gcm_siv/gcm_siv_test.o src/encauth/ocb3/ocb3_add_aad.o \ +src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \ src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \ src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \ diff --git a/makefile_include.mk b/makefile_include.mk index 72b14a8f0..66be64ce5 100644 --- a/makefile_include.mk +++ b/makefile_include.mk @@ -241,7 +241,8 @@ src/encauth/eax/eax_init.o src/encauth/eax/eax_test.o src/encauth/gcm/gcm_add_aa src/encauth/gcm/gcm_add_iv.o src/encauth/gcm/gcm_done.o src/encauth/gcm/gcm_gf_mult.o \ src/encauth/gcm/gcm_init.o src/encauth/gcm/gcm_memory.o src/encauth/gcm/gcm_mult_h.o \ src/encauth/gcm/gcm_process.o src/encauth/gcm/gcm_reset.o src/encauth/gcm/gcm_test.o \ -src/encauth/ocb3/ocb3_add_aad.o src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ +src/encauth/gcm_siv/gcm_siv.o src/encauth/gcm_siv/gcm_siv_test.o src/encauth/ocb3/ocb3_add_aad.o \ +src/encauth/ocb3/ocb3_decrypt.o src/encauth/ocb3/ocb3_decrypt_last.o \ src/encauth/ocb3/ocb3_decrypt_verify_memory.o src/encauth/ocb3/ocb3_done.o \ src/encauth/ocb3/ocb3_encrypt.o src/encauth/ocb3/ocb3_encrypt_authenticate_memory.o \ src/encauth/ocb3/ocb3_encrypt_last.o src/encauth/ocb3/ocb3_init.o src/encauth/ocb3/ocb3_int_ntz.o \ diff --git a/sources.cmake b/sources.cmake index 0d3ef35e0..7c0e06782 100644 --- a/sources.cmake +++ b/sources.cmake @@ -62,6 +62,8 @@ src/encauth/gcm/gcm_mult_h.c src/encauth/gcm/gcm_process.c src/encauth/gcm/gcm_reset.c src/encauth/gcm/gcm_test.c +src/encauth/gcm_siv/gcm_siv.c +src/encauth/gcm_siv/gcm_siv_test.c src/encauth/ocb3/ocb3_add_aad.c src/encauth/ocb3/ocb3_decrypt.c src/encauth/ocb3/ocb3_decrypt_last.c diff --git a/src/encauth/gcm/gcm_gf_mult.c b/src/encauth/gcm/gcm_gf_mult.c index 778ce2da4..17fb618fa 100644 --- a/src/encauth/gcm/gcm_gf_mult.c +++ b/src/encauth/gcm/gcm_gf_mult.c @@ -7,7 +7,7 @@ */ #include "tomcrypt_private.h" -#if defined(LTC_GCM_MODE) || defined(LTC_LRW_MODE) +#if defined(LTC_GCM_MODE) || defined(LTC_LRW_MODE) || defined(LTC_GCM_SIV_MODE) #if defined(LTC_GCM_PCLMUL) #define LTC_GCM_PCLMUL_TARGET LTC_ATTRIBUTE((__target__("pclmul,ssse3"))) @@ -260,7 +260,7 @@ static void s_gcm_gf_mult_pmull(const unsigned char *a, const unsigned char *b, #endif /* defined(LTC_GCM_PMULL) */ #endif /* defined(LTC_GCM_MODE) || defined(LTC_LRW_MODE) */ -#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || (defined(LTC_GCM_MODE) && defined(LTC_FAST)) +#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_SIV_MODE)) && defined(LTC_FAST)) /* this is x*2^128 mod p(x) ... the results are 16 bytes each stored in a packed format. Since only the * lower 16 bits are not zero'ed I removed the upper 14 bytes */ @@ -301,7 +301,7 @@ const unsigned char gcm_shift_table[256*2] = { #endif -#if defined(LTC_GCM_MODE) || defined(LTC_LRW_MODE) +#if defined(LTC_GCM_MODE) || defined(LTC_LRW_MODE) || defined(LTC_GCM_SIV_MODE) #ifndef LTC_FAST diff --git a/src/encauth/gcm_siv/gcm_siv.c b/src/encauth/gcm_siv/gcm_siv.c new file mode 100644 index 000000000..76e2d8203 --- /dev/null +++ b/src/encauth/gcm_siv/gcm_siv.c @@ -0,0 +1,245 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +/** + @file gcm_siv.c + RFC 8452 - AES-GCM-SIV: Nonce Misuse-Resistant AEAD +*/ + +#ifdef LTC_GCM_SIV_MODE + +/* Multiply by x in the GHASH field GF(2^128) defined by + x^128 + x^7 + x^2 + x + 1, using GHASH's bit-byte ordering + (bit 7 of byte 0 is x^0, bit 0 of byte 15 is x^127). This is used + once during key derivation per RFC 8452 Appendix A: the POLYVAL + authentication key is computed as mulX_GHASH(ByteReverse(H_polyval)) + so that GHASH multiplication can be reused for POLYVAL. +*/ +static void s_mulx_ghash(unsigned char *b) +{ + unsigned int carry = b[15] & 1; + int x; + + for (x = 15; x > 0; x--) { + b[x] = (unsigned char)((b[x] >> 1) | ((b[x-1] & 1) << 7)); + } + b[0] >>= 1; + if (carry) b[0] ^= 0xe1; +} + +typedef struct { + unsigned char H[16]; /* GHASH-form authentication key */ + unsigned char S[16]; /* GHASH-form accumulator */ +} polyval_state; + +static void s_polyval_init(polyval_state *st, const unsigned char *auth_key) +{ + int i; + + for (i = 0; i < 16; i++) st->H[i] = auth_key[15 - i]; + s_mulx_ghash(st->H); + zeromem(st->S, 16); +} + +static void s_polyval_block(polyval_state *st, const unsigned char *X) +{ + unsigned char T[16]; + int i; + + for (i = 0; i < 16; i++) st->S[i] ^= X[15 - i]; + gcm_gf_mult(st->S, st->H, T); + XMEMCPY(st->S, T, 16); +} + +static void s_polyval_data(polyval_state *st, const unsigned char *data, unsigned long len) +{ + unsigned long full = len / 16; + unsigned long rem = len % 16; + unsigned long i; + unsigned char block[16]; + + for (i = 0; i < full; i++) s_polyval_block(st, data + i * 16); + if (rem) { + zeromem(block, 16); + XMEMCPY(block, data + full * 16, rem); + s_polyval_block(st, block); + } +} + +static void s_polyval_done(polyval_state *st, unsigned char *out) +{ + int i; + for (i = 0; i < 16; i++) out[i] = st->S[15 - i]; +} + +/* Derive the POLYVAL authentication key and the AES message-encryption key from K and N as specified + in RFC 8452 Section 4. The cipher is scheduled with the message-encryption key in *enc_ecb on success +*/ +static int s_gcm_siv_derive_keys(int cipher, + const unsigned char *K, unsigned long keylen, + const unsigned char *N, + unsigned char *auth_key, + unsigned char *enc_key, + symmetric_ECB *enc_ecb) +{ + int err; + unsigned char input[16], block[16]; + unsigned long ctr, num_blocks; + symmetric_ECB ecb; + + if ((err = ecb_start(cipher, K, (int)keylen, 0, &ecb)) != CRYPT_OK) return err; + + XMEMCPY(input + 4, N, 12); + + /* counters 0..1 yield the 16-byte authentication key, counters 2.. yield the encryption key (16 or 32 bytes) */ + num_blocks = (keylen == 16) ? 4 : 6; + for (ctr = 0; ctr < num_blocks; ctr++) { + STORE32L((ulong32)ctr, input); + if ((err = ecb_encrypt_block(input, block, &ecb)) != CRYPT_OK) goto cleanup; + if (ctr < 2) { + XMEMCPY(auth_key + 8 * ctr, block, 8); + } + else { + XMEMCPY(enc_key + 8 * (ctr - 2), block, 8); + } + } + + err = ecb_start(cipher, enc_key, (int)keylen, 0, enc_ecb); + +cleanup: + ecb_done(&ecb); +#ifdef LTC_CLEAN_STACK + zeromem(input, sizeof(input)); + zeromem(block, sizeof(block)); +#endif + return err; +} + +/** + AES-GCM-SIV one-shot encryption/decryption (RFC 8452). + + @param cipher The index of the cipher (must be a 128-bit block cipher; AES per RFC) + @param key The 16- or 32-byte key + @param keylen The length of the key (16 or 32) + @param nonce The 12-byte nonce + @param noncelen The length of the nonce (must be 12) + @param aad The associated data + @param aadlen The length of the associated data + @param in The input (plaintext on encrypt, ciphertext on decrypt) + @param inlen The length of the input + @param out The output (ciphertext on encrypt, plaintext on decrypt) + @param tag [in/out] The 16-byte tag (output on encrypt, input on decrypt) + @param taglen [in/out] The length of the tag (must be >= 16; set to 16 on output) + @param direction LTC_ENCRYPT or LTC_DECRYPT + @return CRYPT_OK on success, CRYPT_ERROR on tag mismatch during decrypt +*/ +int gcm_siv_memory( int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *aad, unsigned long aadlen, + unsigned char *in, unsigned long inlen, + unsigned char *out, + unsigned char *tag, unsigned long *taglen, + int direction) +{ + int err, ecb_started = 0, ctr_started = 0; + unsigned char auth_key[16]; + unsigned char enc_key[32]; + unsigned char S[16], IC[16], expected_tag[16]; + unsigned char lenblk[16]; + const unsigned char *plaintext; + symmetric_ECB enc_ecb; + symmetric_CTR ctr; + polyval_state pv; + unsigned long i; + + LTC_ARGCHK(key != NULL); + LTC_ARGCHK(nonce != NULL); + LTC_ARGCHK(aad != NULL || aadlen == 0); + LTC_ARGCHK(in != NULL || inlen == 0); + LTC_ARGCHK(out != NULL || inlen == 0); + LTC_ARGCHK(tag != NULL); + LTC_ARGCHK(taglen != NULL); + + if ((err = cipher_is_valid(cipher)) != CRYPT_OK) return err; + if (cipher_descriptor[cipher].block_length != 16) return CRYPT_INVALID_CIPHER; + if (keylen != 16 && keylen != 32) return CRYPT_INVALID_KEYSIZE; + if (noncelen != 12) return CRYPT_INVALID_ARG; + if (*taglen < 16) { + *taglen = 16; + return CRYPT_BUFFER_OVERFLOW; + } + + if ((err = s_gcm_siv_derive_keys(cipher, key, keylen, nonce, auth_key, enc_key, &enc_ecb)) != CRYPT_OK) goto cleanup; + ecb_started = 1; + + if (direction == LTC_DECRYPT) { + /* CTR mode in RFC 8452: the supplied tag is the initial counter block with the MSB of the last + byte forced to 1; only the first 4 bytes (interpreted as little-endian uint32) advance + */ + XMEMCPY(IC, tag, 16); + IC[15] |= 0x80; + if ((err = ctr_start(cipher, IC, enc_key, (int)keylen, 0, CTR_COUNTER_LITTLE_ENDIAN | 4, &ctr)) != CRYPT_OK) goto cleanup; + ctr_started = 1; + if (inlen) { + if ((err = ctr_decrypt(in, out, inlen, &ctr)) != CRYPT_OK) goto cleanup; + } + ctr_done(&ctr); + ctr_started = 0; + plaintext = out; + } + else { + plaintext = in; + } + + s_polyval_init(&pv, auth_key); + s_polyval_data(&pv, aad, aadlen); + s_polyval_data(&pv, plaintext, inlen); + STORE64L((ulong64)aadlen * 8, lenblk); + STORE64L((ulong64)inlen * 8, lenblk + 8); + s_polyval_block(&pv, lenblk); + s_polyval_done(&pv, S); + + for (i = 0; i < 12; i++) S[i] ^= nonce[i]; + S[15] &= 0x7f; + + if ((err = ecb_encrypt_block(S, expected_tag, &enc_ecb)) != CRYPT_OK) goto cleanup; + + if (direction == LTC_DECRYPT) { + err = XMEM_NEQ(expected_tag, tag, 16); + if (err != CRYPT_OK) { + if (inlen) zeromem(out, inlen); /* tag mismatch: do not release plaintext */ + goto cleanup; + } + } + else { + XMEMCPY(IC, expected_tag, 16); + IC[15] |= 0x80; + if ((err = ctr_start(cipher, IC, enc_key, (int)keylen, 0, CTR_COUNTER_LITTLE_ENDIAN | 4, &ctr)) != CRYPT_OK) { + goto cleanup; + } + ctr_started = 1; + if (inlen) { + if ((err = ctr_encrypt(in, out, inlen, &ctr)) != CRYPT_OK) goto cleanup; + } + XMEMCPY(tag, expected_tag, 16); + *taglen = 16; + } + +cleanup: + if (ctr_started) ctr_done(&ctr); + if (ecb_started) ecb_done(&enc_ecb); +#ifdef LTC_CLEAN_STACK + zeromem(auth_key, sizeof(auth_key)); + zeromem(enc_key, sizeof(enc_key)); + zeromem(S, sizeof(S)); + zeromem(IC, sizeof(IC)); + zeromem(expected_tag, sizeof(expected_tag)); + zeromem(lenblk, sizeof(lenblk)); + zeromem(&pv, sizeof(pv)); +#endif + return err; +} + +#endif diff --git a/src/encauth/gcm_siv/gcm_siv_test.c b/src/encauth/gcm_siv/gcm_siv_test.c new file mode 100644 index 000000000..dd02be9a1 --- /dev/null +++ b/src/encauth/gcm_siv/gcm_siv_test.c @@ -0,0 +1,489 @@ +/* LibTomCrypt, modular cryptographic library -- Tom St Denis */ +/* SPDX-License-Identifier: Unlicense */ +#include "tomcrypt_private.h" + +#ifdef LTC_GCM_SIV_MODE + +#ifdef LTC_TEST + +typedef struct { + const char *key; /* 32 (AES128) or 64 (AES256) hex chars */ + const char *nonce; /* 24 hex chars */ + const char *aad; /* hex */ + const char *pt; /* hex */ + const char *result; /* ciphertext || tag, hex */ +} gcm_siv_kat_t; + +/* RFC 8452 Appendix C.1 - AEAD_AES_128_GCM_SIV */ +static const gcm_siv_kat_t s_kat_128[] = { + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "", + "dc20e2d83f25705bb49e439eca56de25" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "0100000000000000", + "b5d839330ac7b786578782fff6013b815b287c22493a364c" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "010000000000000000000000", + "7323ea61d05932260047d942a4978db357391a0bc4fdec8b0d106639" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "01000000000000000000000000000000", + "743f7c8077ab25f8624e2e948579cf77303aaf90f6fe21199c6068577437a0c4" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "0100000000000000000000000000000002000000000000000000000000000000", + "84e07e62ba83a6585417245d7ec413a9fe427d6315c09b57ce45f2e3936a94451a8e45dcd4578c667cd86847bf6155ff" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000", + "3fd24ce1f5a67b75bf2351f181a475c7b800a5b4d3dcf70106b1eea82fa1d64df42bf7226122fa92e17a40eeaac1201b5e6e311dbf395d35b0fe39c2714388f8" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "", + "01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000", + "2433668f1058190f6d43e360f4f35cd8e475127cfca7028ea8ab5c20f7ab2af02516a2bdcbc08d521be37ff28c152bba36697f25b4cd169c6590d1dd39566d3f8a263dd317aa88d56bdf3936dba75bb8" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "0200000000000000", + "1e6daba35669f4273b0a1a2560969cdf790d99759abd1508" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "020000000000000000000000", + "296c7889fd99f41917f4462008299c5102745aaa3a0c469fad9e075a" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "02000000000000000000000000000000", + "e2b0c5da79a901c1745f700525cb335b8f8936ec039e4e4bb97ebd8c4457441f" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "0200000000000000000000000000000003000000000000000000000000000000", + "620048ef3c1e73e57e02bb8562c416a319e73e4caac8e96a1ecb2933145a1d71e6af6a7f87287da059a71684ed3498e1" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000", + "50c8303ea93925d64090d07bd109dfd9515a5a33431019c17d93465999a8b0053201d723120a8562b838cdff25bf9d1e6a8cc3865f76897c2e4b245cf31c51f2" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "01", + "02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000", + "2f5c64059db55ee0fb847ed513003746aca4e61c711b5de2e7a77ffd02da42feec601910d3467bb8b36ebbaebce5fba30d36c95f48a3e7980f0e7ac299332a80cdc46ae475563de037001ef84ae21744" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "010000000000000000000000", + "02000000", + "a8fe3e8707eb1f84fb28f8cb73de8e99e2f48a14" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "010000000000000000000000000000000200", + "0300000000000000000000000000000004000000", + "6bb0fecf5ded9b77f902c7d5da236a4391dd029724afc9805e976f451e6d87f6fe106514" + }, + { + "01000000000000000000000000000000", + "030000000000000000000000", + "0100000000000000000000000000000002000000", + "030000000000000000000000000000000400", + "44d0aaf6fb2f1f34add5e8064e83e12a2adabff9b2ef00fb47920cc72a0c0f13b9fd" + }, + { + "e66021d5eb8e4f4066d4adb9c33560e4", + "f46e44bb3da0015c94f70887", + "", + "", + "a4194b79071b01a87d65f706e3949578" + }, + { + "36864200e0eaf5284d884a0e77d31646", + "bae8e37fc83441b16034566b", + "46bb91c3c5", + "7a806c", + "af60eb711bd85bc1e4d3e0a462e074eea428a8" + }, + { + "aedb64a6c590bc84d1a5e269e4b47801", + "afc0577e34699b9e671fdd4f", + "fc880c94a95198874296", + "bdc66f146545", + "bb93a3e34d3cd6a9c45545cfc11f03ad743dba20f966" + }, + { + "d5cc1fd161320b6920ce07787f86743b", + "275d1ab32f6d1f0434d8848c", + "046787f3ea22c127aaf195d1894728", + "1177441f195495860f", + "4f37281f7ad12949d01d02fd0cd174c84fc5dae2f60f52fd2b" + }, + { + "b3fed1473c528b8426a582995929a149", + "9e9ad8780c8d63d0ab4149c0", + "c9882e5386fd9f92ec489c8fde2be2cf97e74e93", + "9f572c614b4745914474e7c7", + "f54673c5ddf710c745641c8bc1dc2f871fb7561da1286e655e24b7b0" + }, + { + "2d4ed87da44102952ef94b02b805249b", + "ac80e6f61455bfac8308a2d4", + "2950a70d5a1db2316fd568378da107b52b0da55210cc1c1b0a", + "0d8c8451178082355c9e940fea2f58", + "c9ff545e07b88a015f05b274540aa183b3449b9f39552de99dc214a1190b0b" + }, + { + "bde3b2f204d1e9f8b06bc47f9745b3d1", + "ae06556fb6aa7890bebc18fe", + "1860f762ebfbd08284e421702de0de18baa9c9596291b08466f37de21c7f", + "6b3db4da3d57aa94842b9803a96e07fb6de7", + "6298b296e24e8cc35dce0bed484b7f30d5803e377094f04709f64d7b985310a4db84" + }, + { + "f901cfe8a69615a93fdf7a98cad48179", + "6245709fb18853f68d833640", + "7576f7028ec6eb5ea7e298342a94d4b202b370ef9768ec6561c4fe6b7e7296fa859c21", + "e42a3c02c25b64869e146d7b233987bddfc240871d", + "391cc328d484a4f46406181bcd62efd9b3ee197d052d15506c84a9edd65e13e9d24a2a6e70" + } +}; + +/* RFC 8452 Appendix C.2 - AEAD_AES_256_GCM_SIV */ +static const gcm_siv_kat_t s_kat_256[] = { + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "", + "07f5f4169bbf55a8400cd47ea6fd400f" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "0100000000000000", + "c2ef328e5c71c83b843122130f7364b761e0b97427e3df28" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "010000000000000000000000", + "9aab2aeb3faa0a34aea8e2b18ca50da9ae6559e48fd10f6e5c9ca17e" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "01000000000000000000000000000000", + "85a01b63025ba19b7fd3ddfc033b3e76c9eac6fa700942702e90862383c6c366" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "0100000000000000000000000000000002000000000000000000000000000000", + "4a6a9db4c8c6549201b9edb53006cba821ec9cf850948a7c86c68ac7539d027fe819e63abcd020b006a976397632eb5d" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "010000000000000000000000000000000200000000000000000000000000000003000000000000000000000000000000", + "c00d121893a9fa603f48ccc1ca3c57ce7499245ea0046db16c53c7c66fe717e39cf6c748837b61f6ee3adcee17534ed5790bc96880a99ba804bd12c0e6a22cc4" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "", + "01000000000000000000000000000000020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000", + "c2d5160a1f8683834910acdafc41fbb1632d4a353e8b905ec9a5499ac34f96c7e1049eb080883891a4db8caaa1f99dd004d80487540735234e3744512c6f90ce112864c269fc0d9d88c61fa47e39aa08" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "0200000000000000", + "1de22967237a813291213f267e3b452f02d01ae33e4ec854" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "020000000000000000000000", + "163d6f9cc1b346cd453a2e4cc1a4a19ae800941ccdc57cc8413c277f" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "02000000000000000000000000000000", + "c91545823cc24f17dbb0e9e807d5ec17b292d28ff61189e8e49f3875ef91aff7" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "0200000000000000000000000000000003000000000000000000000000000000", + "07dad364bfc2b9da89116d7bef6daaaf6f255510aa654f920ac81b94e8bad365aea1bad12702e1965604374aab96dbbc" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "020000000000000000000000000000000300000000000000000000000000000004000000000000000000000000000000", + "c67a1f0f567a5198aa1fcc8e3f21314336f7f51ca8b1af61feac35a86416fa47fbca3b5f749cdf564527f2314f42fe2503332742b228c647173616cfd44c54eb" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "01", + "02000000000000000000000000000000030000000000000000000000000000000400000000000000000000000000000005000000000000000000000000000000", + "67fd45e126bfb9a79930c43aad2d36967d3f0e4d217c1e551f59727870beefc98cb933a8fce9de887b1e40799988db1fc3f91880ed405b2dd298318858467c895bde0285037c5de81e5b570a049b62a0" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "010000000000000000000000", + "02000000", + "22b3f4cd1835e517741dfddccfa07fa4661b74cf" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "010000000000000000000000000000000200", + "0300000000000000000000000000000004000000", + "43dd0163cdb48f9fe3212bf61b201976067f342bb879ad976d8242acc188ab59cabfe307" + }, + { + "0100000000000000000000000000000000000000000000000000000000000000", + "030000000000000000000000", + "0100000000000000000000000000000002000000", + "030000000000000000000000000000000400", + "462401724b5ce6588d5a54aae5375513a075cfcdf5042112aa29685c912fc2056543" + }, + { + "e66021d5eb8e4f4066d4adb9c33560e4f46e44bb3da0015c94f7088736864200", + "e0eaf5284d884a0e77d31646", + "", + "", + "169fbb2fbf389a995f6390af22228a62" + }, + { + "bae8e37fc83441b16034566b7a806c46bb91c3c5aedb64a6c590bc84d1a5e269", + "e4b47801afc0577e34699b9e", + "4fbdc66f14", + "671fdd", + "0eaccb93da9bb81333aee0c785b240d319719d" + }, + { + "6545fc880c94a95198874296d5cc1fd161320b6920ce07787f86743b275d1ab3", + "2f6d1f0434d8848c1177441f", + "6787f3ea22c127aaf195", + "195495860f04", + "a254dad4f3f96b62b84dc40c84636a5ec12020ec8c2c" + }, + { + "d1894728b3fed1473c528b8426a582995929a1499e9ad8780c8d63d0ab4149c0", + "9f572c614b4745914474e7c7", + "489c8fde2be2cf97e74e932d4ed87d", + "c9882e5386fd9f92ec", + "0df9e308678244c44bc0fd3dc6628dfe55ebb0b9fb2295c8c2" + }, + { + "a44102952ef94b02b805249bac80e6f61455bfac8308a2d40d8c845117808235", + "5c9e940fea2f582950a70d5a", + "0da55210cc1c1b0abde3b2f204d1e9f8b06bc47f", + "1db2316fd568378da107b52b", + "8dbeb9f7255bf5769dd56692404099c2587f64979f21826706d497d5" + }, + { + "9745b3d1ae06556fb6aa7890bebc18fe6b3db4da3d57aa94842b9803a96e07fb", + "6de71860f762ebfbd08284e4", + "f37de21c7ff901cfe8a69615a93fdf7a98cad481796245709f", + "21702de0de18baa9c9596291b08466", + "793576dfa5c0f88729a7ed3c2f1bffb3080d28f6ebb5d3648ce97bd5ba67fd" + }, + { + "b18853f68d833640e42a3c02c25b64869e146d7b233987bddfc240871d7576f7", + "028ec6eb5ea7e298342a94d4", + "9c2159058b1f0fe91433a5bdc20e214eab7fecef4454a10ef0657df21ac7", + "b202b370ef9768ec6561c4fe6b7e7296fa85", + "857e16a64915a787637687db4a9519635cdd454fc2a154fea91f8363a39fec7d0a49" + }, + { + "3c535de192eaed3822a2fbbe2ca9dfc88255e14a661b8aa82cc54236093bbc23", + "688089e55540db1872504e1c", + "734320ccc9d9bbbb19cb81b2af4ecbc3e72834321f7aa0f70b7282b4f33df23f167541", + "ced532ce4159b035277d4dfbb7db62968b13cd4eec", + "626660c26ea6612fb17ad91e8e767639edd6c9faee9d6c7029675b89eaf4ba1ded1a286594" + } +}; + +/* RFC 8452 Appendix C.3 - Counter Wrap Tests (AES-256) */ +static const gcm_siv_kat_t s_kat_wrap[] = { + { + "0000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000", + "", + "000000000000000000000000000000004db923dc793ee6497c76dcc03a98e108", + "f3f80f2cf0cb2dd9c5984fcda908456cc537703b5ba70324a6793a7bf218d3eaffffffff000000000000000000000000" + }, + { + "0000000000000000000000000000000000000000000000000000000000000000", + "000000000000000000000000", + "", + "eb3640277c7ffd1303c7a542d02d3e4c0000000000000000", + "18ce4f0b8cb4d0cac65fea8f79257b20888e53e72299e56dffffffff000000000000000000000000" + } +}; + +static int s_run_kat(int cipher, unsigned long keylen, const gcm_siv_kat_t *vec, unsigned long n, const char *name) +{ + unsigned char key[32], nonce[12]; + unsigned char *aad = NULL, *pt = NULL, *exp = NULL; + unsigned char *ct = NULL, *out = NULL, tag[16]; + unsigned long aadlen, ptlen, explen, ctlen, taglen; + unsigned long klen, nlen, slen; + unsigned long i; + int err = CRYPT_OK; + + for (i = 0; i < n; i++) { + klen = sizeof(key); + if ((err = base16_decode(vec[i].key, XSTRLEN(vec[i].key), key, &klen)) != CRYPT_OK) return err; + if (klen != keylen) return CRYPT_FAIL_TESTVECTOR; + + nlen = sizeof(nonce); + if ((err = base16_decode(vec[i].nonce, XSTRLEN(vec[i].nonce), nonce, &nlen)) != CRYPT_OK) return err; + if (nlen != 12) return CRYPT_FAIL_TESTVECTOR; + + aadlen = XSTRLEN(vec[i].aad) / 2; + ptlen = XSTRLEN(vec[i].pt) / 2; + explen = XSTRLEN(vec[i].result) / 2; + if (explen != ptlen + 16) return CRYPT_FAIL_TESTVECTOR; + ctlen = ptlen; + + aad = aadlen ? XMALLOC(aadlen) : NULL; + pt = ptlen ? XMALLOC(ptlen) : NULL; + exp = XMALLOC(explen); + ct = ctlen ? XMALLOC(ctlen) : NULL; + out = ptlen ? XMALLOC(ptlen) : NULL; + if (explen == 0 || (aadlen && !aad) || (ptlen && (!pt || !out)) || !exp || (ctlen && !ct)) { + err = CRYPT_MEM; + goto cleanup; + } + + if (aadlen) { + slen = aadlen; + if ((err = base16_decode(vec[i].aad, XSTRLEN(vec[i].aad), aad, &slen)) != CRYPT_OK) goto cleanup; + } + if (ptlen) { + slen = ptlen; + if ((err = base16_decode(vec[i].pt, XSTRLEN(vec[i].pt), pt, &slen)) != CRYPT_OK) goto cleanup; + } + slen = explen; + if ((err = base16_decode(vec[i].result, XSTRLEN(vec[i].result), exp, &slen)) != CRYPT_OK) goto cleanup; + + /* encrypt */ + taglen = sizeof(tag); + if ((err = gcm_siv_memory(cipher, key, keylen, nonce, 12, aad, aadlen, pt, ptlen, ct, tag, &taglen, LTC_ENCRYPT)) != CRYPT_OK) { + goto cleanup; + } + if (ctlen && (ltc_compare_testvector(ct, ctlen, exp, ctlen, name, (int)i) != 0)) { + err = CRYPT_FAIL_TESTVECTOR; + goto cleanup; + } + if (ltc_compare_testvector(tag, 16, exp + ctlen, 16, name, (int)(i + 0x100)) != 0) { + err = CRYPT_FAIL_TESTVECTOR; + goto cleanup; + } + + /* decrypt */ + if ((err = gcm_siv_memory(cipher, key, keylen, nonce, 12, aad, aadlen, ct, ctlen, out, tag, &taglen, LTC_DECRYPT)) != CRYPT_OK) { + goto cleanup; + } + if (ptlen && (ltc_compare_testvector(out, ptlen, pt, ptlen, name, (int)(i + 0x200)) != 0)) { + err = CRYPT_FAIL_TESTVECTOR; + goto cleanup; + } + + /* tampered tag must fail */ + tag[0] ^= 0x80; + err = gcm_siv_memory(cipher, key, keylen, nonce, 12, aad, aadlen, ct, ctlen, out, tag, &taglen, LTC_DECRYPT); + if (err == CRYPT_OK) { + err = CRYPT_FAIL_TESTVECTOR; + goto cleanup; + } + err = CRYPT_OK; + + XFREE(aad); aad = NULL; + XFREE(pt); pt = NULL; + XFREE(exp); exp = NULL; + XFREE(ct); ct = NULL; + XFREE(out); out = NULL; + } + +cleanup: + if (aad) XFREE(aad); + if (pt) XFREE(pt); + if (exp) XFREE(exp); + if (ct) XFREE(ct); + if (out) XFREE(out); + return err; +} + +#endif /* LTC_TEST */ + +int gcm_siv_test(void) +{ +#ifndef LTC_TEST + return CRYPT_NOP; +#else + int cipher, err; + if ((cipher = find_cipher("aes")) == -1) return CRYPT_NOP; + if ((err = s_run_kat(cipher, 16, s_kat_128, LTC_ARRAY_SIZE(s_kat_128), "AES-128-GCM-SIV")) != CRYPT_OK) return err; + if ((err = s_run_kat(cipher, 32, s_kat_256, LTC_ARRAY_SIZE(s_kat_256), "AES-256-GCM-SIV")) != CRYPT_OK) return err; + if ((err = s_run_kat(cipher, 32, s_kat_wrap, LTC_ARRAY_SIZE(s_kat_wrap), "AES-256-GCM-SIV-wrap")) != CRYPT_OK) return err; + return CRYPT_OK; +#endif +} + +#endif diff --git a/src/headers/tomcrypt_custom.h b/src/headers/tomcrypt_custom.h index 75e738fc9..5c3286bc3 100644 --- a/src/headers/tomcrypt_custom.h +++ b/src/headers/tomcrypt_custom.h @@ -310,6 +310,7 @@ #define LTC_GCM_MODE #define LTC_CHACHA20POLY1305_MODE #define LTC_SIV_MODE +#define LTC_GCM_SIV_MODE /* Use 64KiB tables */ #ifndef LTC_NO_TABLES diff --git a/src/headers/tomcrypt_mac.h b/src/headers/tomcrypt_mac.h index 44f4a3ecd..224b3eabf 100644 --- a/src/headers/tomcrypt_mac.h +++ b/src/headers/tomcrypt_mac.h @@ -399,13 +399,13 @@ int ccm_test(void); #endif /* LTC_CCM_MODE */ -#if defined(LTC_LRW_MODE) || defined(LTC_GCM_MODE) +#if defined(LTC_LRW_MODE) || defined(LTC_GCM_MODE) || defined(LTC_GCM_SIV_MODE) void gcm_gf_mult(const unsigned char *a, const unsigned char *b, unsigned char *c); #endif /* table shared between GCM and LRW */ -#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_MODE)) && defined(LTC_FAST)) +#if defined(LTC_GCM_TABLES) || defined(LTC_LRW_TABLES) || ((defined(LTC_GCM_MODE) || defined(LTC_GCM_SIV_MODE)) && defined(LTC_FAST)) extern const unsigned char gcm_shift_table[]; #endif @@ -525,3 +525,15 @@ int siv_test(void); #endif +#ifdef LTC_GCM_SIV_MODE +/* RFC 8452 - AES-GCM-SIV */ +int gcm_siv_memory( int cipher, + const unsigned char *key, unsigned long keylen, + const unsigned char *nonce, unsigned long noncelen, + const unsigned char *aad, unsigned long aadlen, + unsigned char *in, unsigned long inlen, + unsigned char *out, + unsigned char *tag, unsigned long *taglen, + int direction); +int gcm_siv_test(void); +#endif diff --git a/src/misc/crypt/crypt.c b/src/misc/crypt/crypt.c index ca27e75f3..a19eeee96 100644 --- a/src/misc/crypt/crypt.c +++ b/src/misc/crypt/crypt.c @@ -318,6 +318,9 @@ const char *crypt_build_settings = #if defined(LTC_SIV_MODE) " SIV\n" #endif +#if defined(LTC_GCM_SIV_MODE) + " GCM-SIV\n" +#endif "\nPRNG:\n" #if defined(LTC_YARROW) diff --git a/tests/mac_test.c b/tests/mac_test.c index d1c0307c6..aefaa20ee 100644 --- a/tests/mac_test.c +++ b/tests/mac_test.c @@ -50,6 +50,9 @@ int mac_test(void) #ifdef LTC_SIV_MODE DO(siv_test()); DO(siv_wycheproof_test()); +#endif +#ifdef LTC_GCM_SIV_MODE + DO(gcm_siv_test()); #endif return 0; }