From f936f4fb802f745259f765d69bea05a81fd8ef23 Mon Sep 17 00:00:00 2001 From: Daiki Ueno Date: Thu, 13 Aug 2020 18:17:08 +0200 Subject: gnutls_aead_cipher_decrypt: check output buffer size before writing While the documentation of gnutls_aead_cipher_decrypt indicates that the inout argument ptext_len initially holds the size that sufficiently fits the expected output size, there was no runtime check on that. This makes the interface robuster against misuses. Signed-off-by: Daiki Ueno --- lib/nettle/cipher.c | 8 +++++ tests/slow/cipher-api-test.c | 83 ++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 91 insertions(+) diff --git a/lib/nettle/cipher.c b/lib/nettle/cipher.c index 93afca243b..5e3a06a744 100644 --- a/lib/nettle/cipher.c +++ b/lib/nettle/cipher.c @@ -1174,6 +1174,10 @@ wrap_nettle_cipher_aead_decrypt(void *_ctx, ctx->cipher->auth(ctx->ctx_ptr, auth_size, auth); encr_size -= tag_size; + + if (unlikely(plain_size < encr_size)) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + ctx->cipher->decrypt(ctx, encr_size, plain, encr); ctx->cipher->tag(ctx->ctx_ptr, tag_size, tag); @@ -1183,6 +1187,10 @@ wrap_nettle_cipher_aead_decrypt(void *_ctx, } else { /* CCM-style cipher */ encr_size -= tag_size; + + if (unlikely(plain_size < encr_size)) + return gnutls_assert_val(GNUTLS_E_SHORT_MEMORY_BUFFER); + ret = ctx->cipher->aead_decrypt(ctx, nonce_size, nonce, auth_size, auth, diff --git a/tests/slow/cipher-api-test.c b/tests/slow/cipher-api-test.c index 17872b7a43..a8e4bbf90a 100644 --- a/tests/slow/cipher-api-test.c +++ b/tests/slow/cipher-api-test.c @@ -198,6 +198,70 @@ static void test_aead_cipher2(int algo) return; } +/* Test whether an invalid call to gnutls_aead_cipher_decrypt() is caught */ +static void test_aead_cipher3(int algo) +{ + int ret; + gnutls_aead_cipher_hd_t ch; + uint8_t key16[64]; + uint8_t iv16[32]; + uint8_t auth[32]; + uint8_t ctext[128+32]; + size_t ctext_len; + uint8_t ptext[128]; + size_t ptext_len; + gnutls_datum_t key, iv; + + 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(ptext, 0xfa, sizeof(ptext)); + memset(ctext, 0xfa, sizeof(ctext)); + memset(auth, 0xfb, sizeof(auth)); + + 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_aead_cipher_init(&ch, algo, &key); + if (ret < 0) + fail("gnutls_aead_cipher_init failed\n"); /*errcode 1 */ + + ctext_len = sizeof(ctext)-1; + ret = gnutls_aead_cipher_encrypt(ch, iv.data, iv.size, auth, sizeof(auth), + gnutls_cipher_get_tag_size(algo), + ptext, sizeof(ptext)-1, + ctext, &ctext_len); + if (ret < 0) + fail("could not encrypt data\n"); + + ptext_len = 0; + ret = gnutls_aead_cipher_decrypt(ch, iv.data, iv.size, auth, sizeof(auth), + gnutls_cipher_get_tag_size(algo), + ctext, sizeof(ctext)-1, + ptext, &ptext_len); + if (ret >= 0) + fail("succeeded in decrypting data onto a short buffer\n"); + + gnutls_aead_cipher_deinit(ch); + + gnutls_global_deinit(); + return; +} + static void check_status(int status) { if (WEXITSTATUS(status) != 0 || @@ -261,6 +325,25 @@ void start(const char *name, int algo, unsigned aead) test_aead_cipher2(algo); exit(0); } + + /* check test_aead_cipher3 */ + + child = fork(); + if (child < 0) { + perror("fork"); + fail("fork"); + return; + } + + if (child) { + int status; + /* parent */ + wait(&status); + check_status(status); + } else { + test_aead_cipher3(algo); + exit(0); + } } void doit(void) -- cgit v1.2.1