Update evp_test to make sure passing partial block to "Update" is ok
authorMatt Caswell <matt@openssl.org>
Mon, 23 Jan 2017 12:45:33 +0000 (12:45 +0000)
committerMatt Caswell <matt@openssl.org>
Wed, 25 Jan 2017 15:02:44 +0000 (15:02 +0000)
The previous commit fixed a bug where a partial block had been passed to
an "Update" function and it wasn't properly handled. We should catch this
type of error in evp_test.

Reviewed-by: Rich Salz <rsalz@openssl.org>
(Merged from https://github.com/openssl/openssl/pull/2275)

test/evp_test.c

index a4c3146888499790dd9a48259bf8ed4cbce078c5..cc0a5bd42041dc99aa975b7694b1507244c1058e 100644 (file)
@@ -880,12 +880,12 @@ static int cipher_test_parse(struct evp_test *t, const char *keyword,
 }
 
 static int cipher_test_enc(struct evp_test *t, int enc,
-                           size_t out_misalign, size_t inp_misalign)
+                           size_t out_misalign, size_t inp_misalign, int frag)
 {
     struct cipher_data *cdat = t->data;
     unsigned char *in, *out, *tmp = NULL;
-    size_t in_len, out_len;
-    int tmplen, tmpflen;
+    size_t in_len, out_len, donelen = 0;
+    int tmplen, chunklen, tmpflen;
     EVP_CIPHER_CTX *ctx = NULL;
     const char *err;
     err = "INTERNAL_ERROR";
@@ -983,15 +983,62 @@ static int cipher_test_enc(struct evp_test *t, int enc,
         }
     }
     if (cdat->aad) {
-        if (!EVP_CipherUpdate(ctx, NULL, &tmplen, cdat->aad, cdat->aad_len)) {
-            err = "AAD_SET_ERROR";
-            goto err;
+        err = "AAD_SET_ERROR";
+        if (!frag) {
+            if (!EVP_CipherUpdate(ctx, NULL, &chunklen, cdat->aad,
+                                  cdat->aad_len))
+                goto err;
+        } else {
+            /*
+             * Supply the AAD in chunks less than the block size where possible
+             */
+            if (cdat->aad_len > 0) {
+                if (!EVP_CipherUpdate(ctx, NULL, &chunklen, cdat->aad, 1))
+                    goto err;
+                donelen++;
+            }
+            if (cdat->aad_len > 2) {
+                if (!EVP_CipherUpdate(ctx, NULL, &chunklen, cdat->aad + donelen,
+                                      cdat->aad_len - 2))
+                    goto err;
+                donelen += cdat->aad_len - 2;
+            }
+            if (cdat->aad_len > 1
+                    && !EVP_CipherUpdate(ctx, NULL, &chunklen,
+                                         cdat->aad + donelen, 1))
+                goto err;
         }
     }
     EVP_CIPHER_CTX_set_padding(ctx, 0);
     err = "CIPHERUPDATE_ERROR";
-    if (!EVP_CipherUpdate(ctx, tmp + out_misalign, &tmplen, in, in_len))
-        goto err;
+    tmplen = 0;
+    donelen = 0;
+    if (!frag) {
+        /* We supply the data all in one go */
+        if (!EVP_CipherUpdate(ctx, tmp + out_misalign, &tmplen, in, in_len))
+            goto err;
+    } else {
+        /* Supply the data in chunks less than the block size where possible */
+        if (in_len > 0) {
+            if (!EVP_CipherUpdate(ctx, tmp + out_misalign, &chunklen, in, 1))
+                goto err;
+            tmplen += chunklen;
+            donelen = 1;
+        }
+        if (in_len > 2) {
+            if (!EVP_CipherUpdate(ctx, tmp + out_misalign + tmplen, &chunklen,
+                                  in + donelen, in_len - 2))
+                goto err;
+            tmplen += chunklen;
+            donelen += in_len - 2;
+        }
+        if (in_len > 1 ) {
+            if (!EVP_CipherUpdate(ctx, tmp + out_misalign + tmplen, &chunklen,
+                                  in + donelen, 1))
+                goto err;
+            tmplen += chunklen;
+        }
+    }
     if (cdat->aead == EVP_CIPH_CCM_MODE)
         tmpflen = 0;
     else {
@@ -1032,7 +1079,7 @@ static int cipher_test_enc(struct evp_test *t, int enc,
 static int cipher_test_run(struct evp_test *t)
 {
     struct cipher_data *cdat = t->data;
-    int rv;
+    int rv, frag = 0;
     size_t out_misalign, inp_misalign;
 
     if (!cdat->key) {
@@ -1050,21 +1097,28 @@ static int cipher_test_run(struct evp_test *t)
         t->err = "NO_TAG";
         return 0;
     }
-    for (out_misalign = 0; out_misalign <= 1; out_misalign++) {
+    for (out_misalign = 0; out_misalign <= 1;) {
         static char aux_err[64];
         t->aux_err = aux_err;
         for (inp_misalign = (size_t)-1; inp_misalign != 2; inp_misalign++) {
+            if (frag && inp_misalign == (size_t)-1)
+                 continue;
+
             if (inp_misalign == (size_t)-1) {
                 /* kludge: inp_misalign == -1 means "exercise in-place" */
-                BIO_snprintf(aux_err, sizeof(aux_err), "%s in-place",
-                             out_misalign ? "misaligned" : "aligned");
+                BIO_snprintf(aux_err, sizeof(aux_err),
+                             "%s in-place, %sfragmented",
+                             out_misalign ? "misaligned" : "aligned",
+                             frag ? "" : "not ");
             } else {
-                BIO_snprintf(aux_err, sizeof(aux_err), "%s output and %s input",
+                BIO_snprintf(aux_err, sizeof(aux_err),
+                             "%s output and %s input, %sfragmented",
                              out_misalign ? "misaligned" : "aligned",
-                             inp_misalign ? "misaligned" : "aligned");
+                             inp_misalign ? "misaligned" : "aligned",
+                             frag ? "" : "not ");
             }
             if (cdat->enc) {
-                rv = cipher_test_enc(t, 1, out_misalign, inp_misalign);
+                rv = cipher_test_enc(t, 1, out_misalign, inp_misalign, frag);
                 /* Not fatal errors: return */
                 if (rv != 1) {
                     if (rv < 0)
@@ -1073,7 +1127,7 @@ static int cipher_test_run(struct evp_test *t)
                 }
             }
             if (cdat->enc != 1) {
-                rv = cipher_test_enc(t, 0, out_misalign, inp_misalign);
+                rv = cipher_test_enc(t, 0, out_misalign, inp_misalign, frag);
                 /* Not fatal errors: return */
                 if (rv != 1) {
                     if (rv < 0)
@@ -1082,6 +1136,21 @@ static int cipher_test_run(struct evp_test *t)
                 }
             }
         }
+
+        if (out_misalign == 1 && frag == 0) {
+            /*
+             * XTS, CCM and Wrap modes have special requirements about input
+             * lengths so we don't fragment for those
+             */
+            if (cdat->aead == EVP_CIPH_CCM_MODE
+                    || EVP_CIPHER_mode(cdat->cipher) == EVP_CIPH_XTS_MODE
+                     || EVP_CIPHER_mode(cdat->cipher) == EVP_CIPH_WRAP_MODE)
+                break;
+            out_misalign = 0;
+            frag++;
+        } else {
+            out_misalign++;
+        }
     }
     t->aux_err = NULL;