Fix provider cipher reinit after init/update with a partial update block.
authorShane Lontis <shane.lontis@oracle.com>
Tue, 4 Aug 2020 22:45:29 +0000 (08:45 +1000)
committerShane Lontis <shane.lontis@oracle.com>
Tue, 4 Aug 2020 22:45:29 +0000 (08:45 +1000)
The test added previously used a 16 byte block during the update which does not cause internal buffering in the provider.
Some internal variables related to the buffering were not being cleared in the init, which meant that the second
update would use the buffered data from the first update.
Added test for this scenario with exclusions for ciphers that do not support partial block updates.

Found by guidovranken.

Reviewed-by: Matt Caswell <matt@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/12523)

providers/implementations/ciphers/cipher_aes_ocb.c
providers/implementations/ciphers/cipher_des.c
providers/implementations/ciphers/cipher_tdes_common.c
providers/implementations/ciphers/ciphercommon.c
test/evp_libctx_test.c

index 2f30b7ffdf384e443a6e8e871adb17c26ef52874..230b353c502f1292124fb4f2d3c6a8e1c23062b8 100644 (file)
@@ -103,6 +103,8 @@ static int aes_ocb_init(void *vctx, const unsigned char *key, size_t keylen,
 {
    PROV_AES_OCB_CTX *ctx = (PROV_AES_OCB_CTX *)vctx;
 
+   ctx->aad_buf_len = 0;
+   ctx->data_buf_len = 0;
    ctx->base.enc = enc;
 
    if (iv != NULL) {
index 9a7c13902f5a71281335b2281b593148239f4913..4974234efd49129911cf35e65e63a485a009d71a 100644 (file)
@@ -68,6 +68,7 @@ static int des_init(void *vctx, const unsigned char *key, size_t keylen,
     PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
 
     ctx->num = 0;
+    ctx->bufsz = 0;
     ctx->enc = enc;
 
     if (iv != NULL) {
index d2379f741b88101467687efc9bf407d6cf66542b..a226e2aac42ae231ef50f7469abfef0aa5108815 100644 (file)
@@ -58,6 +58,7 @@ static int tdes_init(void *vctx, const unsigned char *key, size_t keylen,
     PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx;
 
     ctx->num = 0;
+    ctx->bufsz = 0;
     ctx->enc = enc;
 
     if (iv != NULL) {
index a3ebd3f7e7eb8678963a07db2894cfb86a42f91e..2d119a7b397a729ea726c474c1e5d1b5cb7110e4 100644 (file)
@@ -150,6 +150,7 @@ static int cipher_generic_init_internal(PROV_CIPHER_CTX *ctx,
                                         int enc)
 {
     ctx->num = 0;
+    ctx->bufsz = 0;
     ctx->updated = 0;
     ctx->enc = enc ? 1 : 0;
 
index 395c5d99b5c6f55ec1a0f08d3e76ebfa5e275c63..7421e1e3ca5edc90c0a82c31cb448b9242e0f4bc 100644 (file)
@@ -268,6 +268,82 @@ err:
     return ret;
 }
 
+/*
+ * This test only uses a partial block (half the block size) of input for each
+ * EVP_EncryptUpdate() in order to test that the second init/update is not using
+ * a leftover buffer from the first init/update.
+ * Note: some ciphers don't need a full block to produce output.
+ */
+static int test_cipher_reinit_partialupdate(int test_id)
+{
+    int ret = 0, out1_len = 0, out2_len = 0, in_len;
+    EVP_CIPHER *cipher = NULL;
+    EVP_CIPHER_CTX *ctx = NULL;
+    unsigned char out1[256];
+    unsigned char out2[256];
+    static const unsigned char in[32] = {
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0xba, 0xbe, 0xba, 0xbe, 0x00, 0x00, 0xba, 0xbe,
+        0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    };
+    static const unsigned char key[64] = {
+        0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x01, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x02, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+        0x03, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,
+        0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,
+    };
+    static const unsigned char iv[16] = {
+        0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
+        0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00
+    };
+    const char *name = sk_OPENSSL_CSTRING_value(cipher_names, test_id);
+
+    if (!TEST_ptr(ctx = EVP_CIPHER_CTX_new()))
+        goto err;
+
+    TEST_note("Fetching %s\n", name);
+    if (!TEST_ptr(cipher = EVP_CIPHER_fetch(libctx, name, NULL)))
+        goto err;
+
+    in_len = EVP_CIPHER_block_size(cipher) / 2;
+
+    /* skip any ciphers that don't allow partial updates */
+    if (((EVP_CIPHER_flags(cipher)
+          & (EVP_CIPH_FLAG_CTS | EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)) != 0)
+        || EVP_CIPHER_mode(cipher) == EVP_CIPH_CCM_MODE
+        || EVP_CIPHER_mode(cipher) == EVP_CIPH_XTS_MODE
+        || EVP_CIPHER_mode(cipher) == EVP_CIPH_WRAP_MODE) {
+        ret = 1;
+        goto err;
+    }
+
+    if (!TEST_true(EVP_EncryptInit_ex(ctx, cipher, NULL, key, iv))
+        || !TEST_true(EVP_EncryptUpdate(ctx, out1, &out1_len, in, in_len))
+        || !TEST_true(EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv))
+        || !TEST_true(EVP_EncryptUpdate(ctx, out2, &out2_len, in, in_len)))
+        goto err;
+
+    /* DES3-WRAP uses random every update - so it will give a different value */
+    if (EVP_CIPHER_is_a(cipher, "DES3-WRAP")) {
+        if (!TEST_mem_ne(out1, out1_len, out2, out2_len))
+            goto err;
+    } else {
+        if (!TEST_mem_eq(out1, out1_len, out2, out2_len))
+            goto err;
+    }
+    ret = 1;
+err:
+    EVP_CIPHER_free(cipher);
+    EVP_CIPHER_CTX_free(ctx);
+    return ret;
+}
+
+
 static int name_cmp(const char * const *a, const char * const *b)
 {
     return strcasecmp(*a, *b);
@@ -332,6 +408,8 @@ int setup_tests(void)
     EVP_CIPHER_do_all_provided(libctx, collect_cipher_names, cipher_names);
 
     ADD_ALL_TESTS(test_cipher_reinit, sk_OPENSSL_CSTRING_num(cipher_names));
+    ADD_ALL_TESTS(test_cipher_reinit_partialupdate,
+                  sk_OPENSSL_CSTRING_num(cipher_names));
     return 1;
 }