]> git.ipfire.org Git - thirdparty/gnutls.git/commitdiff
gnutls_aead_cipher_decrypt: check output buffer size before writing
authorDaiki Ueno <ueno@gnu.org>
Thu, 13 Aug 2020 16:17:08 +0000 (18:17 +0200)
committerDaiki Ueno <ueno@gnu.org>
Mon, 17 Aug 2020 08:45:16 +0000 (10:45 +0200)
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 <ueno@gnu.org>
lib/nettle/cipher.c
tests/slow/cipher-api-test.c

index ec0c1ab04377385df3f1b16a3472f6acddb3204d..a82386b100821909659b3744d4fee0f253280131 100644 (file)
@@ -1288,6 +1288,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);
@@ -1297,6 +1301,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,
index 17872b7a431a4e79c615b84872f34424ae3837c9..a8e4bbf90a7d84bab3ec15d97887f6f31877c788 100644 (file)
@@ -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)