diff --git a/ctaocrypt/benchmark/benchmark.c b/ctaocrypt/benchmark/benchmark.c index e7100ba95..b1e662034 100644 --- a/ctaocrypt/benchmark/benchmark.c +++ b/ctaocrypt/benchmark/benchmark.c @@ -186,8 +186,8 @@ void bench_aesgcm() double start, total, persec; int i; - AesGcmSetKey(&enc, key, 16); - AesSetIV(&enc, iv); + AesGcmSetKey(&enc, key, 16, iv); + AesGcmSetExpIV(&enc, iv+4); start = current_time(); for(i = 0; i < megs; i++) diff --git a/ctaocrypt/src/aes.c b/ctaocrypt/src/aes.c index 15e486b03..9d2102488 100644 --- a/ctaocrypt/src/aes.c +++ b/ctaocrypt/src/aes.c @@ -1407,6 +1407,25 @@ void AesCtrEncrypt(Aes* aes, byte* out, const byte* in, word32 sz) #ifdef HAVE_AESGCM +/* + * The IV for AES GCM, stored in struct Aes's member reg, is comprised of + * three parts in order: + * 1. The implicit IV. This is generated from the PRF using the shared + * secrets between endpoints. It is 4 bytes long. + * 2. The explicit IV. This is set by the user of the AES. It needs to be + * unique for each call to encrypt. The explicit IV is shared with the + * other end of the transaction in the clear. + * 3. The counter. Each block of data is encrypted with its own sequence + * number counter. + */ + +enum { + IMPLICIT_IV_SZ = 4, + EXPLICIT_IV_SZ = 8, + CTR_SZ = 4 +}; + + static INLINE void InitGcmCounter(byte* inOutCtr) { inOutCtr[AES_BLOCK_SIZE - 4] = 0; @@ -1421,13 +1440,43 @@ static INLINE void IncrementGcmCounter(byte* inOutCtr) int i; /* in network byte order so start at end and work back */ - for (i = AES_BLOCK_SIZE - 1; i >= AES_BLOCK_SIZE - 4; i--) { + for (i = AES_BLOCK_SIZE - 1; i >= AES_BLOCK_SIZE - CTR_SZ; i--) { if (++inOutCtr[i]) /* we're done unless we overflow */ return; } } +/* + * The explicit IV is set by the caller. A common practice is to treat it as + * a sequence number seeded with a random number. The caller manages + * incrementing the explicit IV when appropriate. + */ + +void AesGcmSetExpIV(Aes* aes, const byte* iv) +{ + XMEMCPY((byte*)aes->reg + IMPLICIT_IV_SZ, iv, EXPLICIT_IV_SZ); +} + + +void AesGcmGetExpIV(Aes* aes, byte* iv) +{ + XMEMCPY(iv, (byte*)aes->reg + IMPLICIT_IV_SZ, EXPLICIT_IV_SZ); +} + + +void AesGcmIncExpIV(Aes* aes) +{ + int i; + byte* iv = (byte*)aes->reg + IMPLICIT_IV_SZ; + + for (i = EXPLICIT_IV_SZ - 1; i >= 0; i--) { + if (++iv[i]) + return; + } +} + + #if defined(GCM_SMALL) || defined(GCM_TABLE) static INLINE void FlattenSzInBits(byte* buf, word32 sz) @@ -1493,13 +1542,17 @@ static void GenerateM0(Aes* aes) #endif /* GCM_TABLE */ -void AesGcmSetKey(Aes* aes, const byte* key, word32 len) +void AesGcmSetKey(Aes* aes, const byte* key, word32 len, + const byte* implicitIV) { - byte iv[AES_BLOCK_SIZE]; + byte fullIV[AES_BLOCK_SIZE]; - XMEMSET(iv, 0, AES_BLOCK_SIZE); - AesSetKey(aes, key, len, iv, AES_ENCRYPTION); - AesEncrypt(aes, iv, aes->H); + XMEMSET(fullIV, 0, AES_BLOCK_SIZE); + XMEMCPY(fullIV, implicitIV, IMPLICIT_IV_SZ); + AesSetKey(aes, key, len, fullIV, AES_ENCRYPTION); + + XMEMSET(fullIV, 0, AES_BLOCK_SIZE); + AesEncrypt(aes, fullIV, aes->H); #ifdef GCM_TABLE GenerateM0(aes); #endif /* GCM_TABLE */ diff --git a/ctaocrypt/test/test.c b/ctaocrypt/test/test.c index 8dcb5cd4b..4f1f28716 100644 --- a/ctaocrypt/test/test.c +++ b/ctaocrypt/test/test.c @@ -1226,8 +1226,8 @@ int aesgcm_test() memset(c2, 0, 60); memset(p2, 0, 60); - AesGcmSetKey(&enc, k, sizeof(k)); - AesSetIV(&enc, iv); + AesGcmSetKey(&enc, k, sizeof(k), iv); + AesGcmSetExpIV(&enc, iv + /*AES_GCM_IMP_IV_SZ*/ 4); /* AES-GCM encrypt and decrypt both use AES encrypt internally */ AesGcmEncrypt(&enc, c2, p, sizeof(c2), t2, sizeof(t2), a, sizeof(a)); if (memcmp(c, c2, sizeof(c2))) diff --git a/cyassl/ctaocrypt/aes.h b/cyassl/ctaocrypt/aes.h index 86875d4f1..9ab625dfc 100644 --- a/cyassl/ctaocrypt/aes.h +++ b/cyassl/ctaocrypt/aes.h @@ -89,7 +89,11 @@ CYASSL_API void AesEncryptDirect(Aes* aes, byte* out, const byte* in); CYASSL_API void AesDecryptDirect(Aes* aes, byte* out, const byte* in); #ifdef HAVE_AESGCM -CYASSL_API void AesGcmSetKey(Aes* aes, const byte* key, word32 len); +CYASSL_API void AesGcmSetKey(Aes* aes, const byte* key, word32 len, + const byte* implicitIV); +CYASSL_API void AesGcmSetExpIV(Aes* aes, const byte* iv); +CYASSL_API void AesGcmGetExpIV(Aes* aes, byte* iv); +CYASSL_API void AesGcmIncExpIV(Aes* aes); CYASSL_API void AesGcmEncrypt(Aes* aes, byte* out, const byte* in, word32 sz, byte* authTag, word32 authTagSz, const byte* authIn, word32 authInSz); diff --git a/src/internal.c b/src/internal.c index f9877ae34..19af1581d 100644 --- a/src/internal.c +++ b/src/internal.c @@ -2223,22 +2223,6 @@ static INLINE void Encrypt(CYASSL* ssl, byte* out, const byte* input, word32 sz) case aes_gcm: { byte additional[AES_BLOCK_SIZE]; - byte nonce[AES_BLOCK_SIZE]; - - /* use this side's IV */ - if (ssl->options.side == SERVER_END) { - XMEMCPY(nonce, ssl->keys.server_write_IV, - AES_GCM_IMP_IV_SZ); - } - else { - XMEMCPY(nonce, ssl->keys.client_write_IV, - AES_GCM_IMP_IV_SZ); - } - XMEMCPY(nonce + AES_GCM_IMP_IV_SZ, - input, AES_GCM_EXP_IV_SZ); - XMEMSET(nonce + AES_GCM_IMP_IV_SZ + AES_GCM_EXP_IV_SZ, - 0, AES_GCM_CTR_IV_SZ); - AesSetIV(&ssl->encrypt.aes, nonce); XMEMSET(additional, 0, AES_BLOCK_SIZE); @@ -2259,6 +2243,7 @@ static INLINE void Encrypt(CYASSL* ssl, byte* out, const byte* input, word32 sz) sz - AES_GCM_EXP_IV_SZ - AEAD_AUTH_TAG_SZ, out + sz - AEAD_AUTH_TAG_SZ, AEAD_AUTH_TAG_SZ, additional, AEAD_AUTH_DATA_SZ); + AesGcmIncExpIV(&ssl->encrypt.aes); } break; #endif @@ -2307,23 +2292,8 @@ static INLINE int Decrypt(CYASSL* ssl, byte* plain, const byte* input, case aes_gcm: { byte additional[AES_BLOCK_SIZE]; - byte nonce[AES_BLOCK_SIZE]; - - /* use the other side's IV */ - if (ssl->options.side == SERVER_END) { - XMEMCPY(nonce, ssl->keys.client_write_IV, - AES_GCM_IMP_IV_SZ); - } - else { - XMEMCPY(nonce, ssl->keys.server_write_IV, - AES_GCM_IMP_IV_SZ); - } - XMEMCPY(nonce + AES_GCM_IMP_IV_SZ, - input, AES_GCM_EXP_IV_SZ); - XMEMSET(nonce + AES_GCM_IMP_IV_SZ + AES_GCM_EXP_IV_SZ, - 0, AES_GCM_CTR_IV_SZ); - AesSetIV(&ssl->decrypt.aes, nonce); + AesGcmSetExpIV(&ssl->decrypt.aes, input); XMEMSET(additional, 0, AES_BLOCK_SIZE); /* sequence number field is 64-bits, we only use 32-bits */ @@ -3035,7 +3005,7 @@ static int BuildMessage(CYASSL* ssl, byte* output, const byte* input, int inSz, if (ssl->specs.cipher_type == aead) { ivSz = AES_GCM_EXP_IV_SZ; sz += (ivSz + 16 - digestSz); - RNG_GenerateBlock(&ssl->rng, iv, ivSz); + AesGcmGetExpIV(&ssl->encrypt.aes, iv); } size = (word16)(sz - headerSz); /* include mac and digest */ AddRecordHeader(output, size, (byte)type, ssl); diff --git a/src/keys.c b/src/keys.c index d6ef0c6e6..fe699f617 100644 --- a/src/keys.c +++ b/src/keys.c @@ -1009,12 +1009,16 @@ static int SetKeys(Ciphers* enc, Ciphers* dec, Keys* keys, CipherSpecs* specs, #ifdef BUILD_AESGCM if (specs->bulk_cipher_algorithm == aes_gcm) { if (side == CLIENT_END) { - AesGcmSetKey(&enc->aes, keys->client_write_key, specs->key_size); - AesGcmSetKey(&dec->aes, keys->server_write_key, specs->key_size); + AesGcmSetKey(&enc->aes, keys->client_write_key, specs->key_size, + keys->client_write_IV); + AesGcmSetKey(&dec->aes, keys->server_write_key, specs->key_size, + keys->server_write_IV); } else { - AesGcmSetKey(&enc->aes, keys->server_write_key, specs->key_size); - AesGcmSetKey(&dec->aes, keys->client_write_key, specs->key_size); + AesGcmSetKey(&enc->aes, keys->server_write_key, specs->key_size, + keys->server_write_IV); + AesGcmSetKey(&dec->aes, keys->client_write_key, specs->key_size, + keys->client_write_IV); } } #endif @@ -1039,6 +1043,13 @@ int StoreKeys(CYASSL* ssl, const byte* keyData) XMEMCPY(ssl->keys.server_write_MAC_secret,&keyData[i], sz); i += sz; } + else if (ssl->specs.bulk_cipher_algorithm == aes_gcm) { + byte iv[AES_GCM_EXP_IV_SZ]; + + /* Initialize the AES-GCM explicit IV to a random number. */ + RNG_GenerateBlock(&ssl->rng, iv, sizeof(iv)); + AesGcmSetExpIV(&ssl->encrypt.aes, iv); + } sz = ssl->specs.key_size; XMEMCPY(ssl->keys.client_write_key, &keyData[i], sz);