]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
cipher: limit plaintext length supplied to AES-GCM
authorDaiki Ueno <ueno@gnu.org>
Mon, 27 Jun 2022 02:14:50 +0000 (11:14 +0900)
committerDaiki Ueno <ueno@gnu.org>
Wed, 13 Jul 2022 11:46:47 +0000 (20:46 +0900)
According to SP800-38D 5.2.1.1, input data length of AES-GCM
encryption function must be less than or equal to 2^39-256 bits.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
NEWS
lib/accelerated/aarch64/aes-aarch64.h
lib/accelerated/aarch64/aes-gcm-aarch64.c
lib/accelerated/x86/aes-gcm-padlock.c
lib/accelerated/x86/aes-gcm-x86-aesni.c
lib/accelerated/x86/aes-gcm-x86-pclmul-avx.c
lib/accelerated/x86/aes-gcm-x86-pclmul.c
lib/accelerated/x86/aes-gcm-x86-ssse3.c
lib/accelerated/x86/aes-x86.h
lib/nettle/cipher.c
tests/slow/cipher-api-test.c

diff --git a/NEWS b/NEWS
index 38356f4c70dfb7ba3f1dbee792c027b79c8ed146..79a6342b16f9e4901981b5c201cae3be9e6552b3 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -13,6 +13,9 @@ See the end for copying conditions.
 ** libgnutls: Length limit for TLS PSK usernames has been increased
    from 128 to 65535 characters (#1323).
 
+** libgnutls: AES-GCM encryption function now limits plaintext
+   length to 2^39-256 bits, according to SP800-38D 5.2.1.1.
+
 ** API and ABI modifications:
 gnutls_fips140_run_self_tests: New function
 
index 692d8620d7762feeb6a877cd74836b693708dccd..0e64f4ed8da670804520affe9490cbd4852898be 100644 (file)
@@ -20,6 +20,21 @@ typedef struct {
        if (s != 16 && s != 24 && s != 32) \
                return GNUTLS_E_INVALID_REQUEST
 
+#include <intprops.h>
+#define AES_GCM_ENCRYPT_MAX_BYTES ((1ULL << 36) - 32)
+static inline int
+record_aes_gcm_encrypt_size(size_t *counter, size_t size) {
+       size_t sum;
+
+       if (!INT_ADD_OK(*counter, size, &sum) ||
+           sum > AES_GCM_ENCRYPT_MAX_BYTES) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+       *counter = sum;
+
+       return 0;
+}
+
 int aes_v8_set_encrypt_key(const unsigned char *userKey, int bits, AES_KEY *key);  
 int aes_v8_set_decrypt_key(const unsigned char *userKey, int bits, AES_KEY *key);
 void aes_v8_cbc_encrypt(const unsigned char *in, unsigned char *out,
index 901bd9f60f1e29c41d5761b261ca319a78fa15c0..be1e69c784eaa4f2c601944b63b8665c7fd3ef90 100644 (file)
@@ -62,6 +62,7 @@ struct aes_gcm_ctx {
        struct gcm128_context gcm;
        unsigned finished;
        unsigned auth_finished;
+       size_t rekey_counter;
 };
 
 void gcm_init_v8(u128 Htable[16], const uint64_t Xi[2]);
@@ -116,6 +117,7 @@ aes_gcm_cipher_setkey(void *_ctx, const void *userkey, size_t keysize)
        ctx->gcm.H.u[1] = bswap_64(ctx->gcm.H.u[1]);
 
        gcm_init_v8(ctx->gcm.Htable, ctx->gcm.H.u);
+       ctx->rekey_counter = 0;
 
        return 0;
 }
@@ -141,6 +143,7 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
        ctx->finished = 0;
        ctx->auth_finished = 0;
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -229,6 +232,7 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        int exp_blocks = blocks * GCM_BLOCK_SIZE;
        int rest = src_size - (exp_blocks);
        uint32_t counter;
+       int ret;
 
        if (unlikely(ctx->finished))
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
@@ -236,6 +240,11 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
        if (blocks > 0) {
                ctr32_encrypt_blocks(src, dst,
                                     blocks,
index a9c7441d650b18a75569748b1fd1825ee1b1d618..739883ab1b2cce4dd79cf978ee0ae11e1c2433dd 100644 (file)
  * Actually padlock doesn't include GCM mode. We just use
  * the ECB part of padlock and nettle for everything else.
  */
-struct gcm_padlock_aes_ctx GCM_CTX(struct padlock_ctx);
+struct gcm_padlock_aes_ctx {
+       struct GCM_CTX(struct padlock_ctx) inner;
+       size_t rekey_counter;
+};
 
 static void padlock_aes_encrypt(const void *_ctx,
                                size_t length, uint8_t * dst,
@@ -78,7 +81,7 @@ static void padlock_aes256_set_encrypt_key(struct padlock_ctx *_ctx,
 
 static void aes_gcm_deinit(void *_ctx)
 {
-       struct padlock_ctx *ctx = _ctx;
+       struct gcm_padlock_aes_ctx *ctx = _ctx;
 
        zeroize_temp_key(ctx, sizeof(*ctx));
        gnutls_free(ctx);
@@ -108,14 +111,15 @@ aes_gcm_cipher_setkey(void *_ctx, const void *key, size_t keysize)
        struct gcm_padlock_aes_ctx *ctx = _ctx;
 
        if (keysize == 16) {
-               GCM_SET_KEY(ctx, padlock_aes128_set_encrypt_key, padlock_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, padlock_aes128_set_encrypt_key, padlock_aes_encrypt,
                            key);
        } else if (keysize == 32) {
-               GCM_SET_KEY(ctx, padlock_aes256_set_encrypt_key, padlock_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, padlock_aes256_set_encrypt_key, padlock_aes_encrypt,
                            key);
        } else
                return GNUTLS_E_INVALID_REQUEST;
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -126,8 +130,9 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        if (iv_size != GCM_BLOCK_SIZE - 4)
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
-       GCM_SET_IV(ctx, iv_size, iv);
+       GCM_SET_IV(&ctx->inner, iv_size, iv);
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -136,11 +141,17 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
                void *dst, size_t length)
 {
        struct gcm_padlock_aes_ctx *ctx = _ctx;
+       int ret;
 
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_ENCRYPT(ctx, padlock_aes_encrypt, src_size, dst, src);
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       GCM_ENCRYPT(&ctx->inner, padlock_aes_encrypt, src_size, dst, src);
 
        return 0;
 }
@@ -154,7 +165,7 @@ aes_gcm_decrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(dst_size < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_DECRYPT(ctx, padlock_aes_encrypt, src_size, dst, src);
+       GCM_DECRYPT(&ctx->inner, padlock_aes_encrypt, src_size, dst, src);
        return 0;
 }
 
@@ -162,7 +173,7 @@ static int aes_gcm_auth(void *_ctx, const void *src, size_t src_size)
 {
        struct gcm_padlock_aes_ctx *ctx = _ctx;
 
-       GCM_UPDATE(ctx, src_size, src);
+       GCM_UPDATE(&ctx->inner, src_size, src);
 
        return 0;
 }
@@ -171,7 +182,7 @@ static void aes_gcm_tag(void *_ctx, void *tag, size_t tagsize)
 {
        struct gcm_padlock_aes_ctx *ctx = _ctx;
 
-       GCM_DIGEST(ctx, padlock_aes_encrypt, tagsize, tag);
+       GCM_DIGEST(&ctx->inner, padlock_aes_encrypt, tagsize, tag);
 }
 
 #include "aes-gcm-aead.h"
index b0edaebfba8c2886eafb83deb2c994e5fb673147..3be63ddd979fc0a72f523713f266164d0330f2c7 100644 (file)
 #include <x86-common.h>
 #include <byteswap.h>
 #include <nettle/gcm.h>
-#include <aes-x86.h>
 
 /* GCM mode 
  * It is used when the CPU doesn't include the PCLMUL instructions.
  */
-struct gcm_x86_aes_ctx GCM_CTX(AES_KEY);
+struct gcm_x86_aes_ctx {
+       struct GCM_CTX(AES_KEY) inner;
+       size_t rekey_counter;
+};
 
 static void x86_aes_encrypt(const void *_ctx,
                                size_t length, uint8_t * dst,
@@ -101,17 +103,18 @@ aes_gcm_cipher_setkey(void *_ctx, const void *key, size_t length)
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
        if (length == 16) {
-               GCM_SET_KEY(ctx, x86_aes128_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes128_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else if (length == 24) {
-               GCM_SET_KEY(ctx, x86_aes192_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes192_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else if (length == 32) {
-               GCM_SET_KEY(ctx, x86_aes256_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes256_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else
                return GNUTLS_E_INVALID_REQUEST;
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -122,8 +125,9 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        if (iv_size != GCM_BLOCK_SIZE - 4)
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
-       GCM_SET_IV(ctx, iv_size, iv);
+       GCM_SET_IV(&ctx->inner, iv_size, iv);
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -132,11 +136,17 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
                void *dst, size_t length)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
+       int ret;
 
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_ENCRYPT(ctx, x86_aes_encrypt, src_size, dst, src);
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       GCM_ENCRYPT(&ctx->inner, x86_aes_encrypt, src_size, dst, src);
 
        return 0;
 }
@@ -150,7 +160,7 @@ aes_gcm_decrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(dst_size < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_DECRYPT(ctx, x86_aes_encrypt, src_size, dst, src);
+       GCM_DECRYPT(&ctx->inner, x86_aes_encrypt, src_size, dst, src);
        return 0;
 }
 
@@ -158,7 +168,7 @@ static int aes_gcm_auth(void *_ctx, const void *src, size_t src_size)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
-       GCM_UPDATE(ctx, src_size, src);
+       GCM_UPDATE(&ctx->inner, src_size, src);
 
        return 0;
 }
@@ -167,7 +177,7 @@ static void aes_gcm_tag(void *_ctx, void *tag, size_t tagsize)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
-       GCM_DIGEST(ctx, x86_aes_encrypt, tagsize, tag);
+       GCM_DIGEST(&ctx->inner, x86_aes_encrypt, tagsize, tag);
 }
 
 static void aes_gcm_deinit(void *_ctx)
index 21aef94440e02f06c4d93382a79c12dd2c2dc895..fbefe432f494d15a4e166988f70ab17f9145d2b0 100644 (file)
@@ -61,6 +61,7 @@ struct aes_gcm_ctx {
        struct gcm128_context gcm;
        unsigned finished;
        unsigned auth_finished;
+       size_t rekey_counter;
 };
 
 void gcm_init_avx(u128 Htable[16], const uint64_t Xi[2]);
@@ -116,6 +117,7 @@ aes_gcm_cipher_setkey(void *_ctx, const void *userkey, size_t keysize)
 
        gcm_init_avx(ctx->gcm.Htable, ctx->gcm.H.u);
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -140,6 +142,7 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
        ctx->finished = 0;
        ctx->auth_finished = 0;
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -184,6 +187,7 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        int exp_blocks = blocks * GCM_BLOCK_SIZE;
        int rest = src_size - (exp_blocks);
        uint32_t counter;
+       int ret;
 
        if (unlikely(ctx->finished))
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
@@ -191,6 +195,11 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
        if (blocks > 0) {
                aesni_ctr32_encrypt_blocks(src, dst,
                                           blocks,
index e6b4990cbfdb750017f21ae419874d25a0a307ab..5385acbb6be2594017cc1c60d5401272f28aa383 100644 (file)
@@ -60,6 +60,7 @@ struct aes_gcm_ctx {
        struct gcm128_context gcm;
        unsigned finished;
        unsigned auth_finished;
+       size_t rekey_counter;
 };
 
 void gcm_init_clmul(u128 Htable[16], const uint64_t Xi[2]);
@@ -116,6 +117,7 @@ aes_gcm_cipher_setkey(void *_ctx, const void *userkey, size_t keysize)
 
        gcm_init_clmul(ctx->gcm.Htable, ctx->gcm.H.u);
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -140,6 +142,7 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
        ctx->finished = 0;
        ctx->auth_finished = 0;
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -184,6 +187,7 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        int exp_blocks = blocks * GCM_BLOCK_SIZE;
        int rest = src_size - (exp_blocks);
        uint32_t counter;
+       int ret;
 
        if (unlikely(ctx->finished))
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
@@ -191,6 +195,11 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
        if (blocks > 0) {
                aesni_ctr32_encrypt_blocks(src, dst,
                                           blocks,
index 7a2ac50869fe470e57b44e0d5c113cac5db66e7c..f074cb10966a8d8cbc288692ea76129bd8cb31e5 100644 (file)
 #include <x86-common.h>
 #include <byteswap.h>
 #include <nettle/gcm.h>
-#include <aes-x86.h>
 #include <assert.h>
 
 /* GCM mode 
  * It is used when the CPU doesn't include the PCLMUL instructions.
  */
-struct gcm_x86_aes_ctx GCM_CTX(AES_KEY);
+struct gcm_x86_aes_ctx {
+       struct GCM_CTX(AES_KEY) inner;
+       size_t rekey_counter;
+};
 
 static void x86_aes_encrypt(const void *_ctx,
                                size_t length, uint8_t * dst,
@@ -110,17 +112,18 @@ aes_gcm_cipher_setkey(void *_ctx, const void *key, size_t keysize)
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
        if (keysize == 16) {
-               GCM_SET_KEY(ctx, x86_aes_128_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes_128_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else if (keysize == 24) {
-               GCM_SET_KEY(ctx, x86_aes_192_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes_192_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else if (keysize == 32) {
-               GCM_SET_KEY(ctx, x86_aes_256_set_encrypt_key, x86_aes_encrypt,
+               GCM_SET_KEY(&ctx->inner, x86_aes_256_set_encrypt_key, x86_aes_encrypt,
                            key);
        } else
                return GNUTLS_E_INVALID_REQUEST;
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -131,8 +134,9 @@ static int aes_gcm_setiv(void *_ctx, const void *iv, size_t iv_size)
        if (iv_size != GCM_BLOCK_SIZE - 4)
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
-       GCM_SET_IV(ctx, iv_size, iv);
+       GCM_SET_IV(&ctx->inner, iv_size, iv);
 
+       ctx->rekey_counter = 0;
        return 0;
 }
 
@@ -141,11 +145,17 @@ aes_gcm_encrypt(void *_ctx, const void *src, size_t src_size,
                void *dst, size_t length)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
+       int ret;
 
        if (unlikely(length < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_ENCRYPT(ctx, x86_aes_encrypt, src_size, dst, src);
+       ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, src_size);
+       if (ret < 0) {
+               return gnutls_assert_val(ret);
+       }
+
+       GCM_ENCRYPT(&ctx->inner, x86_aes_encrypt, src_size, dst, src);
 
        return 0;
 }
@@ -159,7 +169,7 @@ aes_gcm_decrypt(void *_ctx, const void *src, size_t src_size,
        if (unlikely(dst_size < src_size))
                return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER);
 
-       GCM_DECRYPT(ctx, x86_aes_encrypt, src_size, dst, src);
+       GCM_DECRYPT(&ctx->inner, x86_aes_encrypt, src_size, dst, src);
        return 0;
 }
 
@@ -167,7 +177,7 @@ static int aes_gcm_auth(void *_ctx, const void *src, size_t src_size)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
-       GCM_UPDATE(ctx, src_size, src);
+       GCM_UPDATE(&ctx->inner, src_size, src);
 
        return 0;
 }
@@ -176,7 +186,7 @@ static void aes_gcm_tag(void *_ctx, void *tag, size_t tagsize)
 {
        struct gcm_x86_aes_ctx *ctx = _ctx;
 
-       GCM_DIGEST(ctx, x86_aes_encrypt, tagsize, tag);
+       GCM_DIGEST(&ctx->inner, x86_aes_encrypt, tagsize, tag);
 }
 
 static void aes_gcm_deinit(void *_ctx)
index 023b5f7be60672db019e5facf91d255aa05e6add..349d3d5d9c14f0e3d5b3139e5cc15987bb3c2808 100644 (file)
@@ -22,6 +22,21 @@ typedef struct {
        if (s != 16 && s != 24 && s != 32) \
                return GNUTLS_E_INVALID_REQUEST
 
+#include <intprops.h>
+#define AES_GCM_ENCRYPT_MAX_BYTES ((1ULL << 36) - 32)
+static inline int
+record_aes_gcm_encrypt_size(size_t *counter, size_t size) {
+       size_t sum;
+
+       if (!INT_ADD_OK(*counter, size, &sum) ||
+           sum > AES_GCM_ENCRYPT_MAX_BYTES) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+       *counter = sum;
+
+       return 0;
+}
+
 void aesni_ecb_encrypt(const unsigned char *in, unsigned char *out,
                       size_t len, const AES_KEY * key, int enc);
 
index ab4c46d2d0595847c76f7eb3aa4e6e8fe977f9e6..b41862d1eaf54e146e263100bd042b9a1ff61976 100644 (file)
@@ -63,6 +63,7 @@
 #include <nettle/xts.h>
 #include <nettle/siv-cmac.h>
 #include <fips.h>
+#include <intprops.h>
 
 struct nettle_cipher_ctx;
 
@@ -120,8 +121,23 @@ struct nettle_cipher_ctx {
        unsigned iv_size;
 
        bool enc;
+       size_t rekey_counter;
 };
 
+#define AES_GCM_ENCRYPT_MAX_BYTES ((1ULL << 36) - 32)
+static inline int
+record_aes_gcm_encrypt_size(size_t *counter, size_t size) {
+       size_t sum;
+
+       if (!INT_ADD_OK(*counter, size, &sum) ||
+           sum > AES_GCM_ENCRYPT_MAX_BYTES) {
+               return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+       }
+       *counter = sum;
+
+       return 0;
+}
+
 static void
 _stream_encrypt(struct nettle_cipher_ctx *ctx, size_t length, uint8_t * dst,
                const uint8_t * src)
@@ -1133,6 +1149,16 @@ wrap_nettle_cipher_setkey(void *_ctx, const void *key, size_t keysize)
        else
                ctx->cipher->set_decrypt_key(ctx->ctx_ptr, key);
 
+       switch (ctx->cipher->algo) {
+       case GNUTLS_CIPHER_AES_128_GCM:
+       case GNUTLS_CIPHER_AES_192_GCM:
+       case GNUTLS_CIPHER_AES_256_GCM:
+               ctx->rekey_counter = 0;
+               break;
+       default:
+               break;
+       }
+
        return 0;
 }
 
@@ -1147,6 +1173,7 @@ wrap_nettle_cipher_setiv(void *_ctx, const void *iv, size_t iv_size)
        case GNUTLS_CIPHER_AES_192_GCM:
        case GNUTLS_CIPHER_AES_256_GCM:
                FIPS_RULE(iv_size < GCM_IV_SIZE, GNUTLS_E_INVALID_REQUEST, "access to short GCM nonce size\n");
+               ctx->rekey_counter = 0;
                break;
        case GNUTLS_CIPHER_SALSA20_256:
        case GNUTLS_CIPHER_ESTREAM_SALSA20_256:
@@ -1207,10 +1234,24 @@ wrap_nettle_cipher_encrypt(void *_ctx, const void *plain, size_t plain_size,
                           void *encr, size_t encr_size)
 {
        struct nettle_cipher_ctx *ctx = _ctx;
+       int ret;
 
        if (unlikely(ctx->cipher->encrypt == NULL))
                return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
 
+       switch (ctx->cipher->algo) {
+       case GNUTLS_CIPHER_AES_128_GCM:
+       case GNUTLS_CIPHER_AES_192_GCM:
+       case GNUTLS_CIPHER_AES_256_GCM:
+               ret = record_aes_gcm_encrypt_size(&ctx->rekey_counter, plain_size);
+               if (ret < 0) {
+                       return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
+               }
+               break;
+       default:
+               break;
+       }
+
        ctx->cipher->encrypt(ctx, plain_size, encr, plain);
 
        return 0;
index fc880bcc9fca776cadb2c306379bb717ecd209e4..1d267ce312e77a20fe4249cff777886e9c3e4381 100644 (file)
@@ -21,6 +21,7 @@
  */
 
 #include <config.h>
+#include <limits.h>
 #include <stdint.h>
 #include <stdio.h>
 #include <string.h>
@@ -48,6 +49,11 @@ int main(int argc, char **argv)
 #include <assert.h>
 #include <utils.h>
 
+#define AES_GCM_ENCRYPT_PLAINTEXT_MAX ((1ULL << 36) - 32)
+#if SIZE_MAX >= AES_GCM_ENCRYPT_PLAINTEXT_MAX
+#define TEST_AES_GCM_ENCRYPT_PLAINTEXT_SIZE 1
+#endif
+
 static void tls_log_func(int level, const char *str)
 {
        fprintf(stderr, "<%d>| %s", level, str);
@@ -401,6 +407,74 @@ static void test_aead_invalid_short_decrypt(int algo)
        return;
 }
 
+#ifdef TEST_AES_GCM_ENCRYPT_PLAINTEXT_SIZE
+/* Test whether an invalid call to gnutls_cipher_encrypt() with too
+ * long message is caught */
+static void test_aead_invalid_too_long_encrypt(int algo)
+{
+       int ret;
+       gnutls_cipher_hd_t ch;
+       uint8_t key16[64];
+       uint8_t iv16[32];
+       uint8_t data[128];
+       gnutls_datum_t key, iv;
+
+       if (algo != GNUTLS_CIPHER_AES_128_GCM &&
+           algo != GNUTLS_CIPHER_AES_192_GCM &&
+           algo != GNUTLS_CIPHER_AES_256_GCM) {
+               return;
+       }
+
+       key.data = key16;
+       key.size = gnutls_cipher_get_key_size(algo);
+       assert(key.size <= sizeof(key16));
+
+       iv.data = iv16;
+       iv.size = gnutls_cipher_get_iv_size(algo);
+       assert(iv.size <= sizeof(iv16));
+
+       memset(iv.data, 0xff, iv.size);
+       memset(key.data, 0xfe, key.size);
+       memset(data, 0xfa, sizeof(data));
+
+       gnutls_global_set_log_function(tls_log_func);
+       if (debug)
+               gnutls_global_set_log_level(4711);
+
+       ret = global_init();
+       if (ret < 0) {
+               fail("Cannot initialize library\n"); /*errcode 1 */
+       }
+
+       ret = gnutls_cipher_init(&ch, algo, &key, &iv);
+       if (ret < 0)
+               fail("gnutls_cipher_init failed\n"); /*errcode 1 */
+
+       /* Test exceeding AES-GCM plaintext limit */
+       ret = gnutls_cipher_encrypt(ch, data, sizeof(data));
+       if (ret < 0)
+               fail("could not encrypt data\n");
+
+       /* A few blocks larger than AES_GCM_ENCRYPT_PLAINTEXT_MAX combined with
+        * the previous call.  Use NULL for PLAINTEXT so the access to the first
+        * block always results in page fault (in case the limit is not
+        * enforced).
+        */
+       ret = gnutls_cipher_encrypt(ch, NULL, AES_GCM_ENCRYPT_PLAINTEXT_MAX);
+       if (ret >= 0)
+               fail("succeeded in encrypting too long data\n");
+       if (ret != GNUTLS_E_INVALID_REQUEST)
+               fail("wrong kind of error on encrypting too long data,"
+                    "%s instead of GNUTLS_E_INVALID_REQUEST\n",
+                    gnutls_strerror_name(ret));
+
+       gnutls_cipher_deinit(ch);
+
+       gnutls_global_deinit();
+       return;
+}
+#endif
+
 static void check_status(int status)
 {
        if (WEXITSTATUS(status) != 0 ||
@@ -464,6 +538,11 @@ void start(const char *name, int algo, unsigned aead)
 
                success("trying %s: test_aead_invalid_short_decrypt\n", name);
                fork_subtest(test_aead_invalid_short_decrypt, algo);
+
+#if TEST_AES_GCM_ENCRYPT_PLAINTEXT_SIZE
+               success("trying %s: test_aead_invalid_too_long_encrypt\n", name);
+               fork_subtest(test_aead_invalid_too_long_encrypt, algo);
+#endif
        }
 }