Add a poisoned field to EVP_CIPHER_CTX.

Poison the EVP_CIPHER_CTX structure on failures, and indicate
that it is an error to re-use an EVP_CIPHER_CTX context in another
call after a failure.

Bug: 494
Change-Id: Ibcdf28b83a2e690f7aab789d908c076d844231c6
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/54185
Commit-Queue: Bob Beck <bbe@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/crypto/fipsmodule/cipher/cipher.c b/crypto/fipsmodule/cipher/cipher.c
index a126986..ee45578 100644
--- a/crypto/fipsmodule/cipher/cipher.c
+++ b/crypto/fipsmodule/cipher/cipher.c
@@ -104,6 +104,11 @@
     return 0;
   }
 
+  if (in->poisoned) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
   EVP_CIPHER_CTX_cleanup(out);
   OPENSSL_memcpy(out, in, sizeof(EVP_CIPHER_CTX));
 
@@ -226,6 +231,9 @@
 
   ctx->buf_len = 0;
   ctx->final_used = 0;
+  // Clear the poisoned flag to permit re-use of a CTX that previously had a
+  // failed operation.
+  ctx->poisoned = 0;
   return 1;
 }
 
@@ -250,6 +258,15 @@
 
 int EVP_EncryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
                       const uint8_t *in, int in_len) {
+  if (ctx->poisoned) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+  // If the first call to |cipher| succeeds and the second fails, |ctx| may be
+  // left in an indeterminate state. We set a poison flag on failure to ensure
+  // callers do not continue to use the object in that case.
+  ctx->poisoned = 1;
+
   // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output
   // does not overflow |*out_len|.
   int bl = ctx->cipher->block_size;
@@ -265,17 +282,23 @@
     } else {
       *out_len = ret;
     }
+    ctx->poisoned = 0;
     return 1;
   }
 
   if (in_len <= 0) {
     *out_len = 0;
-    return in_len == 0;
+    if (in_len == 0) {
+      ctx->poisoned = 0;
+      return 1;
+    }
+    return 0;
   }
 
   if (ctx->buf_len == 0 && block_remainder(ctx, in_len) == 0) {
     if (ctx->cipher->cipher(ctx, out, in, in_len)) {
       *out_len = in_len;
+      ctx->poisoned = 0;
       return 1;
     } else {
       *out_len = 0;
@@ -290,6 +313,7 @@
       OPENSSL_memcpy(&ctx->buf[i], in, in_len);
       ctx->buf_len += in_len;
       *out_len = 0;
+      ctx->poisoned = 0;
       return 1;
     } else {
       int j = bl - i;
@@ -319,6 +343,7 @@
     OPENSSL_memcpy(ctx->buf, &in[in_len], i);
   }
   ctx->buf_len = i;
+  ctx->poisoned = 0;
   return 1;
 }
 
@@ -326,6 +351,11 @@
   int n;
   unsigned int i, b, bl;
 
+  if (ctx->poisoned) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
   if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
     // When EVP_CIPH_FLAG_CUSTOM_CIPHER is set, the return value of |cipher| is
     // the number of bytes written, or -1 on error. Otherwise the return value
@@ -371,6 +401,11 @@
 
 int EVP_DecryptUpdate(EVP_CIPHER_CTX *ctx, uint8_t *out, int *out_len,
                       const uint8_t *in, int in_len) {
+  if (ctx->poisoned) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
   // Ciphers that use blocks may write up to |bl| extra bytes. Ensure the output
   // does not overflow |*out_len|.
   unsigned int b = ctx->cipher->block_size;
@@ -433,6 +468,11 @@
   unsigned int b;
   *out_len = 0;
 
+  if (ctx->poisoned) {
+    OPENSSL_PUT_ERROR(CIPHER, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
+    return 0;
+  }
+
   if (ctx->cipher->flags & EVP_CIPH_FLAG_CUSTOM_CIPHER) {
     i = ctx->cipher->cipher(ctx, out, NULL, 0);
     if (i < 0) {
diff --git a/include/openssl/cipher.h b/include/openssl/cipher.h
index 380d25d..ba4b698 100644
--- a/include/openssl/cipher.h
+++ b/include/openssl/cipher.h
@@ -587,6 +587,9 @@
   int final_used;
 
   uint8_t final[EVP_MAX_BLOCK_LENGTH];  // possible final block
+
+  // Has this structure been rendered unusable by a failure.
+  int poisoned;
 } /* EVP_CIPHER_CTX */;
 
 typedef struct evp_cipher_info_st {