** 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
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,
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]);
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;
}
ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
ctx->finished = 0;
ctx->auth_finished = 0;
+ ctx->rekey_counter = 0;
return 0;
}
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);
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,
* 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,
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);
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;
}
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;
}
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;
}
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;
}
{
struct gcm_padlock_aes_ctx *ctx = _ctx;
- GCM_UPDATE(ctx, src_size, src);
+ GCM_UPDATE(&ctx->inner, src_size, src);
return 0;
}
{
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"
#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,
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;
}
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;
}
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;
}
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;
}
{
struct gcm_x86_aes_ctx *ctx = _ctx;
- GCM_UPDATE(ctx, src_size, src);
+ GCM_UPDATE(&ctx->inner, src_size, src);
return 0;
}
{
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)
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]);
gcm_init_avx(ctx->gcm.Htable, ctx->gcm.H.u);
+ ctx->rekey_counter = 0;
return 0;
}
ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
ctx->finished = 0;
ctx->auth_finished = 0;
+ ctx->rekey_counter = 0;
return 0;
}
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);
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,
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]);
gcm_init_clmul(ctx->gcm.Htable, ctx->gcm.H.u);
+ ctx->rekey_counter = 0;
return 0;
}
ctx->gcm.Yi.c[GCM_BLOCK_SIZE - 1] = 2;
ctx->finished = 0;
ctx->auth_finished = 0;
+ ctx->rekey_counter = 0;
return 0;
}
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);
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,
#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,
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;
}
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;
}
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;
}
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;
}
{
struct gcm_x86_aes_ctx *ctx = _ctx;
- GCM_UPDATE(ctx, src_size, src);
+ GCM_UPDATE(&ctx->inner, src_size, src);
return 0;
}
{
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)
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);
#include <nettle/xts.h>
#include <nettle/siv-cmac.h>
#include <fips.h>
+#include <intprops.h>
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)
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;
}
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:
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;
*/
#include <config.h>
+#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#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);
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 ||
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
}
}