Add back support for TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256

This TLS 1.2 algorithm is substantially inferior to AES-GCM and should
never be used. It will not be available unless configured by name.
However, in can be used to provide backwards-compatibility with devices
that cannot be updated if so needed.

Change-Id: I1fd78efeb33aceca76ec2e7cb76b70f761ed1af8
Reviewed-on: https://boringssl-review.googlesource.com/c/boringssl/+/59585
Reviewed-by: David Benjamin <davidben@google.com>
Commit-Queue: Adam Langley <agl@google.com>
Auto-Submit: Adam Langley <agl@google.com>
diff --git a/crypto/cipher_extra/cipher_test.cc b/crypto/cipher_extra/cipher_test.cc
index 4c8c142..6101ef9 100644
--- a/crypto/cipher_extra/cipher_test.cc
+++ b/crypto/cipher_extra/cipher_test.cc
@@ -623,6 +623,53 @@
   }
 }
 
+TEST(CipherTest, SHA256WithSecretSuffix) {
+  uint8_t buf[SHA256_CBLOCK * 4];
+  RAND_bytes(buf, sizeof(buf));
+  // Hashing should run in time independent of the bytes.
+  CONSTTIME_SECRET(buf, sizeof(buf));
+
+  // Exhaustively testing interesting cases in this function is cubic in the
+  // block size, so we test in 3-byte increments.
+  constexpr size_t kSkip = 3;
+  // This value should be less than 8 to test the edge case when the 8-byte
+  // length wraps to the next block.
+  static_assert(kSkip < 8, "kSkip is too large");
+
+  // |EVP_sha256_final_with_secret_suffix| is sensitive to the public length of
+  // the partial block previously hashed. In TLS, this is the HMAC prefix, the
+  // header, and the public minimum padding length.
+  for (size_t prefix = 0; prefix < SHA256_CBLOCK; prefix += kSkip) {
+    SCOPED_TRACE(prefix);
+    // The first block is treated differently, so we run with up to three
+    // blocks of length variability.
+    for (size_t max_len = 0; max_len < 3 * SHA256_CBLOCK; max_len += kSkip) {
+      SCOPED_TRACE(max_len);
+      for (size_t len = 0; len <= max_len; len += kSkip) {
+        SCOPED_TRACE(len);
+
+        uint8_t expected[SHA256_DIGEST_LENGTH];
+        SHA256(buf, prefix + len, expected);
+        CONSTTIME_DECLASSIFY(expected, sizeof(expected));
+
+        // Make a copy of the secret length to avoid interfering with the loop.
+        size_t secret_len = len;
+        CONSTTIME_SECRET(&secret_len, sizeof(secret_len));
+
+        SHA256_CTX ctx;
+        SHA256_Init(&ctx);
+        SHA256_Update(&ctx, buf, prefix);
+        uint8_t computed[SHA256_DIGEST_LENGTH];
+        ASSERT_TRUE(EVP_sha256_final_with_secret_suffix(
+            &ctx, computed, buf + prefix, secret_len, max_len));
+
+        CONSTTIME_DECLASSIFY(computed, sizeof(computed));
+        EXPECT_EQ(Bytes(expected), Bytes(computed));
+      }
+    }
+  }
+}
+
 TEST(CipherTest, GetCipher) {
   const EVP_CIPHER *cipher = EVP_get_cipherbynid(NID_aes_128_gcm);
   ASSERT_TRUE(cipher);
diff --git a/crypto/cipher_extra/e_tls.c b/crypto/cipher_extra/e_tls.c
index 00b4824..c462a2b 100644
--- a/crypto/cipher_extra/e_tls.c
+++ b/crypto/cipher_extra/e_tls.c
@@ -400,6 +400,14 @@
                        EVP_sha1(), 1);
 }
 
+static int aead_aes_128_cbc_sha256_tls_init(EVP_AEAD_CTX *ctx,
+                                            const uint8_t *key, size_t key_len,
+                                            size_t tag_len,
+                                            enum evp_aead_direction_t dir) {
+  return aead_tls_init(ctx, key, key_len, tag_len, dir, EVP_aes_128_cbc(),
+                       EVP_sha256(), 0);
+}
+
 static int aead_aes_256_cbc_sha1_tls_init(EVP_AEAD_CTX *ctx, const uint8_t *key,
                                           size_t key_len, size_t tag_len,
                                           enum evp_aead_direction_t dir) {
@@ -476,6 +484,23 @@
     aead_tls_tag_len,
 };
 
+static const EVP_AEAD aead_aes_128_cbc_sha256_tls = {
+    SHA256_DIGEST_LENGTH + 16,  // key len (SHA256 + AES128)
+    16,                         // nonce len (IV)
+    16 + SHA256_DIGEST_LENGTH,  // overhead (padding + SHA256)
+    SHA256_DIGEST_LENGTH,       // max tag length
+    0,                          // seal_scatter_supports_extra_in
+
+    NULL,  // init
+    aead_aes_128_cbc_sha256_tls_init,
+    aead_tls_cleanup,
+    aead_tls_open,
+    aead_tls_seal_scatter,
+    NULL,  // open_gather
+    NULL,  // get_iv
+    aead_tls_tag_len,
+};
+
 static const EVP_AEAD aead_aes_256_cbc_sha1_tls = {
     SHA_DIGEST_LENGTH + 32,  // key len (SHA1 + AES256)
     16,                      // nonce len (IV)
@@ -552,6 +577,10 @@
   return &aead_aes_128_cbc_sha1_tls_implicit_iv;
 }
 
+const EVP_AEAD *EVP_aead_aes_128_cbc_sha256_tls(void) {
+  return &aead_aes_128_cbc_sha256_tls;
+}
+
 const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls(void) {
   return &aead_aes_256_cbc_sha1_tls;
 }
diff --git a/crypto/cipher_extra/internal.h b/crypto/cipher_extra/internal.h
index 76a0314..39ab950 100644
--- a/crypto/cipher_extra/internal.h
+++ b/crypto/cipher_extra/internal.h
@@ -109,6 +109,14 @@
     SHA_CTX *ctx, uint8_t out[SHA_DIGEST_LENGTH], const uint8_t *in, size_t len,
     size_t max_len);
 
+// EVP_sha256_final_with_secret_suffix acts like
+// |EVP_sha1_final_with_secret_suffix|, but for SHA-256.
+//
+// This function is exported for unit tests.
+OPENSSL_EXPORT int EVP_sha256_final_with_secret_suffix(
+    SHA256_CTX *ctx, uint8_t out[SHA256_DIGEST_LENGTH], const uint8_t *in,
+    size_t len, size_t max_len);
+
 // EVP_tls_cbc_digest_record computes the MAC of a decrypted, padded TLS
 // record.
 //
diff --git a/crypto/cipher_extra/tls_cbc.c b/crypto/cipher_extra/tls_cbc.c
index e1e95d4..ac6ca43 100644
--- a/crypto/cipher_extra/tls_cbc.c
+++ b/crypto/cipher_extra/tls_cbc.c
@@ -267,24 +267,115 @@
   return 1;
 }
 
-int EVP_tls_cbc_record_digest_supported(const EVP_MD *md) {
-  return EVP_MD_type(md) == NID_sha1;
-}
-
-int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
-                              size_t *md_out_size, const uint8_t header[13],
-                              const uint8_t *data, size_t data_size,
-                              size_t data_plus_mac_plus_padding_size,
-                              const uint8_t *mac_secret,
-                              unsigned mac_secret_length) {
-  if (EVP_MD_type(md) != NID_sha1) {
-      // EVP_tls_cbc_record_digest_supported should have been called first to
-      // check that the hash function is supported.
-      assert(0);
-      *md_out_size = 0;
-      return 0;
+int EVP_sha256_final_with_secret_suffix(SHA256_CTX *ctx,
+                                        uint8_t out[SHA256_DIGEST_LENGTH],
+                                        const uint8_t *in, size_t len,
+                                        size_t max_len) {
+  // Bound the input length so |total_bits| below fits in four bytes. This is
+  // redundant with TLS record size limits. This also ensures |input_idx| below
+  // does not overflow.
+  size_t max_len_bits = max_len << 3;
+  if (ctx->Nh != 0 ||
+      (max_len_bits >> 3) != max_len ||  // Overflow
+      ctx->Nl + max_len_bits < max_len_bits ||
+      ctx->Nl + max_len_bits > UINT32_MAX) {
+    return 0;
   }
 
+  // We need to hash the following into |ctx|:
+  //
+  // - ctx->data[:ctx->num]
+  // - in[:len]
+  // - A 0x80 byte
+  // - However many zero bytes are needed to pad up to a block.
+  // - Eight bytes of length.
+  size_t num_blocks = (ctx->num + len + 1 + 8 + SHA256_CBLOCK - 1) >> 6;
+  size_t last_block = num_blocks - 1;
+  size_t max_blocks = (ctx->num + max_len + 1 + 8 + SHA256_CBLOCK - 1) >> 6;
+
+  // The bounds above imply |total_bits| fits in four bytes.
+  size_t total_bits = ctx->Nl + (len << 3);
+  uint8_t length_bytes[4];
+  length_bytes[0] = (uint8_t)(total_bits >> 24);
+  length_bytes[1] = (uint8_t)(total_bits >> 16);
+  length_bytes[2] = (uint8_t)(total_bits >> 8);
+  length_bytes[3] = (uint8_t)total_bits;
+
+  // We now construct and process each expected block in constant-time.
+  uint8_t block[SHA256_CBLOCK] = {0};
+  uint32_t result[8] = {0};
+  // input_idx is the index into |in| corresponding to the current block.
+  // However, we allow this index to overflow beyond |max_len|, to simplify the
+  // 0x80 byte.
+  size_t input_idx = 0;
+  for (size_t i = 0; i < max_blocks; i++) {
+    // Fill |block| with data from the partial block in |ctx| and |in|. We copy
+    // as if we were hashing up to |max_len| and then zero the excess later.
+    size_t block_start = 0;
+    if (i == 0) {
+      OPENSSL_memcpy(block, ctx->data, ctx->num);
+      block_start = ctx->num;
+    }
+    if (input_idx < max_len) {
+      size_t to_copy = SHA256_CBLOCK - block_start;
+      if (to_copy > max_len - input_idx) {
+        to_copy = max_len - input_idx;
+      }
+      OPENSSL_memcpy(block + block_start, in + input_idx, to_copy);
+    }
+
+    // Zero any bytes beyond |len| and add the 0x80 byte.
+    for (size_t j = block_start; j < SHA256_CBLOCK; j++) {
+      // input[idx] corresponds to block[j].
+      size_t idx = input_idx + j - block_start;
+      // The barriers on |len| are not strictly necessary. However, without
+      // them, GCC compiles this code by incorporating |len| into the loop
+      // counter and subtracting it out later. This is still constant-time, but
+      // it frustrates attempts to validate this.
+      uint8_t is_in_bounds = constant_time_lt_8(idx, value_barrier_w(len));
+      uint8_t is_padding_byte = constant_time_eq_8(idx, value_barrier_w(len));
+      block[j] &= is_in_bounds;
+      block[j] |= 0x80 & is_padding_byte;
+    }
+
+    input_idx += SHA256_CBLOCK - block_start;
+
+    // Fill in the length if this is the last block.
+    crypto_word_t is_last_block = constant_time_eq_w(i, last_block);
+    for (size_t j = 0; j < 4; j++) {
+      block[SHA256_CBLOCK - 4 + j] |= is_last_block & length_bytes[j];
+    }
+
+    // Process the block and save the hash state if it is the final value.
+    SHA256_Transform(ctx, block);
+    for (size_t j = 0; j < 8; j++) {
+      result[j] |= is_last_block & ctx->h[j];
+    }
+  }
+
+  // Write the output.
+  for (size_t i = 0; i < 8; i++) {
+    CRYPTO_store_u32_be(out + 4 * i, result[i]);
+  }
+  return 1;
+}
+
+int EVP_tls_cbc_record_digest_supported(const EVP_MD *md) {
+  switch (EVP_MD_type(md)) {
+    case NID_sha1:
+    case NID_sha256:
+      return 1;
+    default:
+      return 0;
+  }
+}
+
+static int tls_cbc_digest_record_sha1(uint8_t *md_out, size_t *md_out_size,
+                                      const uint8_t header[13],
+                                      const uint8_t *data, size_t data_size,
+                                      size_t data_plus_mac_plus_padding_size,
+                                      const uint8_t *mac_secret,
+                                      unsigned mac_secret_length) {
   if (mac_secret_length > SHA_CBLOCK) {
     // HMAC pads small keys with zeros and hashes large keys down. This function
     // should never reach the large key case.
@@ -336,3 +427,88 @@
   *md_out_size = SHA_DIGEST_LENGTH;
   return 1;
 }
+
+static int tls_cbc_digest_record_sha256(uint8_t *md_out, size_t *md_out_size,
+                                        const uint8_t header[13],
+                                        const uint8_t *data, size_t data_size,
+                                        size_t data_plus_mac_plus_padding_size,
+                                        const uint8_t *mac_secret,
+                                        unsigned mac_secret_length) {
+  if (mac_secret_length > SHA256_CBLOCK) {
+    // HMAC pads small keys with zeros and hashes large keys down. This function
+    // should never reach the large key case.
+    assert(0);
+    return 0;
+  }
+
+  // Compute the initial HMAC block.
+  uint8_t hmac_pad[SHA256_CBLOCK];
+  OPENSSL_memset(hmac_pad, 0, sizeof(hmac_pad));
+  OPENSSL_memcpy(hmac_pad, mac_secret, mac_secret_length);
+  for (size_t i = 0; i < SHA256_CBLOCK; i++) {
+    hmac_pad[i] ^= 0x36;
+  }
+
+  SHA256_CTX ctx;
+  SHA256_Init(&ctx);
+  SHA256_Update(&ctx, hmac_pad, SHA256_CBLOCK);
+  SHA256_Update(&ctx, header, 13);
+
+  // There are at most 256 bytes of padding, so we can compute the public
+  // minimum length for |data_size|.
+  size_t min_data_size = 0;
+  if (data_plus_mac_plus_padding_size > SHA256_DIGEST_LENGTH + 256) {
+    min_data_size =
+        data_plus_mac_plus_padding_size - SHA256_DIGEST_LENGTH - 256;
+  }
+
+  // Hash the public minimum length directly. This reduces the number of blocks
+  // that must be computed in constant-time.
+  SHA256_Update(&ctx, data, min_data_size);
+
+  // Hash the remaining data without leaking |data_size|.
+  uint8_t mac_out[SHA256_DIGEST_LENGTH];
+  if (!EVP_sha256_final_with_secret_suffix(
+          &ctx, mac_out, data + min_data_size, data_size - min_data_size,
+          data_plus_mac_plus_padding_size - min_data_size)) {
+    return 0;
+  }
+
+  // Complete the HMAC in the standard manner.
+  SHA256_Init(&ctx);
+  for (size_t i = 0; i < SHA256_CBLOCK; i++) {
+    hmac_pad[i] ^= 0x6a;
+  }
+
+  SHA256_Update(&ctx, hmac_pad, SHA256_CBLOCK);
+  SHA256_Update(&ctx, mac_out, SHA256_DIGEST_LENGTH);
+  SHA256_Final(md_out, &ctx);
+  *md_out_size = SHA256_DIGEST_LENGTH;
+  return 1;
+}
+
+int EVP_tls_cbc_digest_record(const EVP_MD *md, uint8_t *md_out,
+                              size_t *md_out_size, const uint8_t header[13],
+                              const uint8_t *data, size_t data_size,
+                              size_t data_plus_mac_plus_padding_size,
+                              const uint8_t *mac_secret,
+                              unsigned mac_secret_length) {
+  switch (EVP_MD_type(md)) {
+    case NID_sha1:
+      return tls_cbc_digest_record_sha1(
+          md_out, md_out_size, header, data, data_size,
+          data_plus_mac_plus_padding_size, mac_secret, mac_secret_length);
+
+    case NID_sha256:
+      return tls_cbc_digest_record_sha256(
+          md_out, md_out_size, header, data, data_size,
+          data_plus_mac_plus_padding_size, mac_secret, mac_secret_length);
+
+    default:
+      // EVP_tls_cbc_record_digest_supported should have been called first to
+      // check that the hash function is supported.
+      assert(0);
+      *md_out_size = 0;
+      return 0;
+  }
+}
diff --git a/include/openssl/aead.h b/include/openssl/aead.h
index 7aa0b44..376bff1 100644
--- a/include/openssl/aead.h
+++ b/include/openssl/aead.h
@@ -400,6 +400,8 @@
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls(void);
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha1_tls_implicit_iv(void);
 
+OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_128_cbc_sha256_tls(void);
+
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls(void);
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_aes_256_cbc_sha1_tls_implicit_iv(void);
 
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 724d580..772fb87 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -408,6 +408,8 @@
 #define TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA 0x0300C013
 #define TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA 0x0300C014
 
+#define TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256 0x0300C027
+
 #define TLS1_CK_ECDH_anon_WITH_NULL_SHA 0x0300C015
 #define TLS1_CK_ECDH_anon_WITH_RC4_128_SHA 0x0300C016
 #define TLS1_CK_ECDH_anon_WITH_DES_192_CBC3_SHA 0x0300C017
@@ -518,6 +520,8 @@
 #define TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA "ECDHE-RSA-AES128-SHA"
 #define TLS1_TXT_ECDHE_RSA_WITH_AES_256_CBC_SHA "ECDHE-RSA-AES256-SHA"
 
+#define TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA256 "ECDHE-RSA-AES128-SHA256"
+
 #define TLS1_TXT_ECDH_anon_WITH_NULL_SHA "AECDH-NULL-SHA"
 #define TLS1_TXT_ECDH_anon_WITH_RC4_128_SHA "AECDH-RC4-SHA"
 #define TLS1_TXT_ECDH_anon_WITH_DES_192_CBC3_SHA "AECDH-DES-CBC3-SHA"
diff --git a/ssl/internal.h b/ssl/internal.h
index 9ba2b14..1e6da21 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -553,8 +553,9 @@
 
 // Bits for |algorithm_mac| (symmetric authentication).
 #define SSL_SHA1 0x00000001u
+#define SSL_SHA256 0x00000002u
 // SSL_AEAD is set for all AEADs.
-#define SSL_AEAD 0x00000002u
+#define SSL_AEAD 0x00000004u
 
 // Bits for |algorithm_prf| (handshake digest).
 #define SSL_HANDSHAKE_MAC_DEFAULT 0x1
@@ -672,6 +673,9 @@
 bool ssl_tls13_cipher_meets_policy(uint16_t cipher_id,
                                    enum ssl_compliance_policy_t policy);
 
+// ssl_cipher_is_deprecated returns true if |cipher| is deprecated.
+OPENSSL_EXPORT bool ssl_cipher_is_deprecated(const SSL_CIPHER *cipher);
+
 
 // Transcript layer.
 
diff --git a/ssl/ssl_cipher.cc b/ssl/ssl_cipher.cc
index 2f8fc2a..0d4206c 100644
--- a/ssl/ssl_cipher.cc
+++ b/ssl/ssl_cipher.cc
@@ -335,6 +335,18 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
+    // Cipher C027
+    {
+     TLS1_TXT_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+     "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+     TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_AES128,
+     SSL_SHA256,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
     // GCM based TLS v1.2 ciphersuites from RFC 5289
 
     // Cipher C02B
@@ -626,6 +638,14 @@
     }
 
     *out_mac_secret_len = SHA_DIGEST_LENGTH;
+  } else if (cipher->algorithm_mac == SSL_SHA256) {
+    if (cipher->algorithm_enc == SSL_AES128) {
+      *out_aead = EVP_aead_aes_128_cbc_sha256_tls();
+    } else {
+      return false;
+    }
+
+    *out_mac_secret_len = SHA256_DIGEST_LENGTH;
   } else {
     return false;
   }
@@ -748,9 +768,9 @@
   sk_SSL_CIPHER_delete(ciphers.get(), index);
 }
 
-static bool ssl_cipher_is_deprecated(const SSL_CIPHER *cipher) {
+bool ssl_cipher_is_deprecated(const SSL_CIPHER *cipher) {
   // TODO(crbug.com/boringssl/599): Deprecate 3DES.
-  return false;
+  return cipher->id == TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256;
 }
 
 // ssl_cipher_apply_rule applies the rule type |rule| to ciphers matching its
@@ -1138,6 +1158,7 @@
       TLS1_CK_ECDHE_ECDSA_WITH_AES_256_CBC_SHA & 0xffff,
       TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA & 0xffff,
       TLS1_CK_ECDHE_PSK_WITH_AES_256_CBC_SHA & 0xffff,
+      TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256 & 0xffff,
       TLS1_CK_RSA_WITH_AES_128_GCM_SHA256 & 0xffff,
       TLS1_CK_RSA_WITH_AES_256_GCM_SHA384 & 0xffff,
       TLS1_CK_RSA_WITH_AES_128_SHA & 0xffff,
@@ -1380,6 +1401,8 @@
       return NID_undef;
     case SSL_SHA1:
       return NID_sha1;
+    case SSL_SHA256:
+      return NID_sha256;
   }
   assert(0);
   return NID_undef;
@@ -1628,6 +1651,10 @@
       mac = "SHA1";
       break;
 
+    case SSL_SHA256:
+      mac = "SHA256";
+      break;
+
     case SSL_AEAD:
       mac = "AEAD";
       break;
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 1a06ba8..a665ced 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -382,7 +382,7 @@
   "[AES128-SHA | AES128-SHA256]",
 };
 
-static const char *kMustNotIncludeNull[] = {
+static const char *kMustNotIncludeDeprecated[] = {
   "ALL",
   "DEFAULT",
   "HIGH",
@@ -395,6 +395,11 @@
   "TLSv1.2",
 };
 
+static const char* kShouldIncludeCBCSHA256[] = {
+  "TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+  "ALL:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256",
+};
+
 static const CurveTest kCurveTests[] = {
   {
     "P-256",
@@ -578,7 +583,7 @@
     ERR_clear_error();
   }
 
-  for (const char *rule : kMustNotIncludeNull) {
+  for (const char *rule : kMustNotIncludeDeprecated) {
     SCOPED_TRACE(rule);
     bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
     ASSERT_TRUE(ctx);
@@ -586,6 +591,25 @@
     ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(ctx.get(), rule));
     for (const SSL_CIPHER *cipher : SSL_CTX_get_ciphers(ctx.get())) {
       EXPECT_NE(NID_undef, SSL_CIPHER_get_cipher_nid(cipher));
+      EXPECT_FALSE(ssl_cipher_is_deprecated(cipher));
+    }
+  }
+
+  {
+    for (const char *rule : kShouldIncludeCBCSHA256) {
+      bssl::UniquePtr<SSL_CTX> ctx(SSL_CTX_new(TLS_method()));
+      ASSERT_TRUE(ctx);
+      ASSERT_TRUE(SSL_CTX_set_strict_cipher_list(ctx.get(), rule));
+
+      bool found = false;
+      for (const SSL_CIPHER *cipher : SSL_CTX_get_ciphers(ctx.get())) {
+        if ((TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA256 & 0xffff) ==
+            SSL_CIPHER_get_protocol_id(cipher)) {
+          found = true;
+          break;
+        }
+      }
+      EXPECT_TRUE(found);
     }
   }
 }
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index a4887af..86daca2 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -1843,6 +1843,7 @@
 	{"ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE_RSA_WITH_AES_128_GCM_SHA256", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"ECDHE_RSA_WITH_AES_128_CBC_SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+	{"ECDHE_RSA_WITH_AES_128_CBC_SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
 	{"ECDHE_RSA_WITH_AES_256_GCM_SHA384", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
 	{"ECDHE_RSA_WITH_AES_256_CBC_SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index aa15320..b204491 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -1391,7 +1391,7 @@
 
   SSL_CTX_set0_buffer_pool(ssl_ctx.get(), g_pool);
 
-  std::string cipher_list = "ALL";
+  std::string cipher_list = "ALL:TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256";
   if (!cipher.empty()) {
     cipher_list = cipher;
     SSL_CTX_set_options(ssl_ctx.get(), SSL_OP_CIPHER_SERVER_PREFERENCE);