Implement draft-ietf-tls-chacha20-poly1305-04.

Only ECDHE-based ciphers are implemented. To ease the transition, the
pre-standard cipher shares a name with the standard one. The cipher rule parser
is hacked up to match the name to both ciphers. From the perspective of the
cipher suite configuration language, there is only one cipher.

This does mean it is impossible to disable the old variant without a code
change, but this situation will be very short-lived, so this is fine.

Also take this opportunity to make the CK and TXT names align with convention.

Change-Id: Ie819819c55bce8ff58e533f1dbc8bef5af955c21
Reviewed-on: https://boringssl-review.googlesource.com/6686
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 58a32bb..bce981c 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -1044,7 +1044,9 @@
 OPENSSL_EXPORT int SSL_CIPHER_is_AES256CBC(const SSL_CIPHER *cipher);
 
 /* SSL_CIPHER_is_CHACHA20POLY1305 returns one if |cipher| uses
- * CHACHA20_POLY1305. */
+ * CHACHA20_POLY1305. Note this includes both the
+ * draft-ietf-tls-chacha20-poly1305-04 and draft-agl-tls-chacha20poly1305-04
+ * versions. */
 OPENSSL_EXPORT int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher);
 
 /* SSL_CIPHER_is_NULL returns one if |cipher| does not encrypt. */
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 92210f6..92d2752 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -430,12 +430,14 @@
 #define TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD 0x0300CC13
 #define TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD 0x0300CC14
 
-/* TODO(davidben): Remove these once WebRTC is no longer using them, so they
- * may point to the future RFC 7539 variant. */
+#define TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0x0300CCA8
+#define TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0x0300CCA9
+#define TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 0x0300CCAC
+
+/* TODO(davidben): Remove this. Historically, the CK names for CHACHA20_POLY1305
+ * were missing 'WITH' and 'SHA256'. */
 #define TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305 \
-    TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD
-#define TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305 \
-    TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD
+  TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 
 /* XXX
  * Inconsistency alert:
@@ -597,14 +599,25 @@
 #define TLS1_TXT_ECDH_RSA_WITH_AES_128_GCM_SHA256 "ECDH-RSA-AES128-GCM-SHA256"
 #define TLS1_TXT_ECDH_RSA_WITH_AES_256_GCM_SHA384 "ECDH-RSA-AES256-GCM-SHA384"
 
+/* For convenience, the old and new CHACHA20_POLY1305 ciphers have the same
+ * name. In cipher strings, both will be selected. This is temporary and will be
+ * removed when the pre-standard construction is removed. */
 #define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD \
   "ECDHE-RSA-CHACHA20-POLY1305"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_OLD \
   "ECDHE-ECDSA-CHACHA20-POLY1305"
 
-/* TODO(davidben): Remove this once QUIC has switched to the '_OLD' name. */
+#define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 \
+  "ECDHE-RSA-CHACHA20-POLY1305"
+#define TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 \
+  "ECDHE-ECDSA-CHACHA20-POLY1305"
+#define TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 \
+  "ECDHE-PSK-CHACHA20-POLY1305"
+
+/* TODO(davidben): Remove this. Historically, the TXT names for CHACHA20_POLY1305
+ * were missing 'SHA256'. */
 #define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305 \
-    TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD
+  TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256
 
 #define TLS_CT_RSA_SIGN 1
 #define TLS_CT_DSS_SIGN 2
diff --git a/ssl/internal.h b/ssl/internal.h
index 7910461..bbbd939 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -183,6 +183,7 @@
 #define SSL_AES256GCM 0x00000020L
 #define SSL_CHACHA20POLY1305_OLD 0x00000040L
 #define SSL_eNULL 0x00000080L
+#define SSL_CHACHA20POLY1305 0x00000100L
 
 #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
 
@@ -266,7 +267,7 @@
   EVP_AEAD_CTX ctx;
   /* fixed_nonce contains any bytes of the nonce that are fixed for all
    * records. */
-  uint8_t fixed_nonce[8];
+  uint8_t fixed_nonce[12];
   uint8_t fixed_nonce_len, variable_nonce_len;
   /* variable_nonce_included_in_record is non-zero if the variable nonce
    * for a record is included as a prefix before the ciphertext. */
@@ -281,6 +282,9 @@
   /* omit_version_in_ad is non-zero if the version should be omitted
    * in the AEAD's ad parameter. */
   char omit_version_in_ad;
+  /* xor_fixed_nonce is non-zero if the fixed nonce should be XOR'd into the
+   * variable nonce rather than prepended. */
+  char xor_fixed_nonce;
 } /* SSL_AEAD_CTX */;
 
 /* SSL_AEAD_CTX_new creates a newly-allocated |SSL_AEAD_CTX| using the supplied
diff --git a/ssl/ssl_aead_ctx.c b/ssl/ssl_aead_ctx.c
index f9001c7..8829679 100644
--- a/ssl/ssl_aead_ctx.c
+++ b/ssl/ssl_aead_ctx.c
@@ -74,17 +74,20 @@
   assert(EVP_AEAD_nonce_length(aead) <= EVP_AEAD_MAX_NONCE_LENGTH);
   aead_ctx->variable_nonce_len = (uint8_t)EVP_AEAD_nonce_length(aead);
   if (mac_key_len == 0) {
-    /* For a real AEAD, the IV is the fixed part of the nonce. */
-    if (fixed_iv_len > sizeof(aead_ctx->fixed_nonce) ||
-        fixed_iv_len > aead_ctx->variable_nonce_len) {
-      SSL_AEAD_CTX_free(aead_ctx);
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return 0;
-    }
-    aead_ctx->variable_nonce_len -= fixed_iv_len;
-
+    assert(fixed_iv_len <= sizeof(aead_ctx->fixed_nonce));
     memcpy(aead_ctx->fixed_nonce, fixed_iv, fixed_iv_len);
     aead_ctx->fixed_nonce_len = fixed_iv_len;
+
+    if (cipher->algorithm_enc & SSL_CHACHA20POLY1305) {
+      /* The fixed nonce into the actual nonce (the sequence number). */
+      aead_ctx->xor_fixed_nonce = 1;
+      aead_ctx->variable_nonce_len = 8;
+    } else {
+      /* The fixed IV is prepended to the nonce. */
+      assert(fixed_iv_len <= aead_ctx->variable_nonce_len);
+      aead_ctx->variable_nonce_len -= fixed_iv_len;
+    }
+
     /* AES-GCM uses an explicit nonce. */
     if (cipher->algorithm_enc & (SSL_AES128GCM | SSL_AES256GCM)) {
       aead_ctx->variable_nonce_included_in_record = 1;
@@ -176,8 +179,17 @@
   /* Assemble the nonce. */
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
   size_t nonce_len = 0;
-  memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
-  nonce_len += aead->fixed_nonce_len;
+
+  /* Prepend the fixed nonce, or left-pad with zeros if XORing. */
+  if (aead->xor_fixed_nonce) {
+    nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len;
+    memset(nonce, 0, nonce_len);
+  } else {
+    memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
+    nonce_len += aead->fixed_nonce_len;
+  }
+
+  /* Add the variable nonce. */
   if (aead->variable_nonce_included_in_record) {
     if (in_len < aead->variable_nonce_len) {
       /* Publicly invalid. */
@@ -193,6 +205,15 @@
   }
   nonce_len += aead->variable_nonce_len;
 
+  /* XOR the fixed nonce, if necessary. */
+  if (aead->xor_fixed_nonce) {
+    assert(nonce_len == aead->fixed_nonce_len);
+    size_t i;
+    for (i = 0; i < aead->fixed_nonce_len; i++) {
+      nonce[i] ^= aead->fixed_nonce[i];
+    }
+  }
+
   return EVP_AEAD_CTX_open(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
                            in, in_len, ad, ad_len);
 }
@@ -219,8 +240,17 @@
   /* Assemble the nonce. */
   uint8_t nonce[EVP_AEAD_MAX_NONCE_LENGTH];
   size_t nonce_len = 0;
-  memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
-  nonce_len += aead->fixed_nonce_len;
+
+  /* Prepend the fixed nonce, or left-pad with zeros if XORing. */
+  if (aead->xor_fixed_nonce) {
+    nonce_len = aead->fixed_nonce_len - aead->variable_nonce_len;
+    memset(nonce, 0, nonce_len);
+  } else {
+    memcpy(nonce, aead->fixed_nonce, aead->fixed_nonce_len);
+    nonce_len += aead->fixed_nonce_len;
+  }
+
+  /* Select the variable nonce. */
   if (aead->random_variable_nonce) {
     assert(aead->variable_nonce_included_in_record);
     if (!RAND_bytes(nonce + nonce_len, aead->variable_nonce_len)) {
@@ -230,13 +260,14 @@
     /* When sending we use the sequence number as the variable part of the
      * nonce. */
     assert(aead->variable_nonce_len == 8);
-    memcpy(nonce + nonce_len, ad, aead->variable_nonce_len);
+    memcpy(nonce + nonce_len, seqnum, aead->variable_nonce_len);
   }
   nonce_len += aead->variable_nonce_len;
 
   /* Emit the variable nonce if included in the record. */
   size_t extra_len = 0;
   if (aead->variable_nonce_included_in_record) {
+    assert(!aead->xor_fixed_nonce);
     if (max_out < aead->variable_nonce_len) {
       OPENSSL_PUT_ERROR(SSL, SSL_R_BUFFER_TOO_SMALL);
       return 0;
@@ -251,6 +282,15 @@
     max_out -= aead->variable_nonce_len;
   }
 
+  /* XOR the fixed nonce, if necessary. */
+  if (aead->xor_fixed_nonce) {
+    assert(nonce_len == aead->fixed_nonce_len);
+    size_t i;
+    for (i = 0; i < aead->fixed_nonce_len; i++) {
+      nonce[i] ^= aead->fixed_nonce[i];
+    }
+  }
+
   if (!EVP_AEAD_CTX_seal(&aead->ctx, out, out_len, max_out, nonce, nonce_len,
                          in, in_len, ad, ad_len)) {
     return 0;
diff --git a/ssl/ssl_cipher.c b/ssl/ssl_cipher.c
index a9dc0b4..6d8237d 100644
--- a/ssl/ssl_cipher.c
+++ b/ssl/ssl_cipher.c
@@ -560,9 +560,9 @@
      SSL_HANDSHAKE_MAC_DEFAULT,
     },
 
-#if !defined(BORINGSSL_ANDROID_SYSTEM)
     /* ChaCha20-Poly1305 cipher suites. */
 
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
     {
      TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD,
      TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
@@ -583,6 +583,39 @@
      SSL_HANDSHAKE_MAC_SHA256,
     },
 #endif
+
+    /* Cipher CCA8 */
+    {
+     TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher CCA9 */
+    {
+     TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    /* Cipher CCAB */
+    {
+     TLS1_TXT_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+     TLS1_CK_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256,
+     SSL_kECDHE,
+     SSL_aPSK,
+     SSL_CHACHA20POLY1305,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
 };
 
 static const size_t kCiphersLen = sizeof(kCiphers) / sizeof(kCiphers[0]);
@@ -661,7 +694,8 @@
     {"AES256", ~0u, ~0u, SSL_AES256 | SSL_AES256GCM, ~0u, 0},
     {"AES", ~0u, ~0u, SSL_AES, ~0u, 0},
     {"AESGCM", ~0u, ~0u, SSL_AES128GCM | SSL_AES256GCM, ~0u, 0},
-    {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0},
+    {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
+     0},
 
     /* MAC aliases */
     {"MD5", ~0u, ~0u, ~0u, SSL_MD5, 0},
@@ -736,6 +770,11 @@
       return 1;
 #endif
 
+    case SSL_CHACHA20POLY1305:
+      *out_aead = EVP_aead_chacha20_poly1305();
+      *out_fixed_iv_len = 12;
+      return 1;
+
     case SSL_RC4:
       switch (cipher->algorithm_mac) {
         case SSL_MD5:
@@ -1276,6 +1315,29 @@
       multi = 1;
     }
 
+    /* If one of the CHACHA20_POLY1305 variants is selected, include the other
+     * as well. They have the same name to avoid requiring changes in
+     * configuration. Apply this transformation late so that the cipher name
+     * still behaves as an exact name and not an alias in multipart rules.
+     *
+     * This is temporary and will be removed when the pre-standard construction
+     * is removed. */
+    if (cipher_id == TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD ||
+        cipher_id == TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256) {
+      cipher_id = 0;
+      alg_mkey = SSL_kECDHE;
+      alg_auth = SSL_aRSA;
+      alg_enc = SSL_CHACHA20POLY1305|SSL_CHACHA20POLY1305_OLD;
+      alg_mac = SSL_AEAD;
+    } else if (cipher_id == TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD ||
+               cipher_id == TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256) {
+      cipher_id = 0;
+      alg_mkey = SSL_kECDHE;
+      alg_auth = SSL_aECDSA;
+      alg_enc = SSL_CHACHA20POLY1305|SSL_CHACHA20POLY1305_OLD;
+      alg_mac = SSL_AEAD;
+    }
+
     /* Ok, we have the rule, now apply it. */
     if (rule == CIPHER_SPECIAL) {
       /* special command */
@@ -1352,15 +1414,20 @@
 
   /* Order the bulk ciphers. First the preferred AEAD ciphers. We prefer
    * CHACHA20 unless there is hardware support for fast and constant-time
-   * AES_GCM. */
+   * AES_GCM. Of the two CHACHA20 variants, the new one is preferred over the
+   * old one. */
   if (EVP_has_aes_hardware()) {
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
                           &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
                           &head, &tail);
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD,
+                          -1, 0, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
                           CIPHER_ADD, -1, 0, &head, &tail);
   } else {
+    ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305, ~0u, 0, CIPHER_ADD,
+                          -1, 0, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_CHACHA20POLY1305_OLD, ~0u, 0,
                           CIPHER_ADD, -1, 0, &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1527,7 +1594,8 @@
 }
 
 int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) {
-  return (cipher->algorithm_enc & SSL_CHACHA20POLY1305_OLD) != 0;
+  return (cipher->algorithm_enc &
+          (SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD)) != 0;
 }
 
 int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher) {
@@ -1621,6 +1689,7 @@
       return "AES_128_GCM";
     case SSL_AES256GCM:
       return "AES_256_GCM";
+    case SSL_CHACHA20POLY1305:
     case SSL_CHACHA20POLY1305_OLD:
       return "CHACHA20_POLY1305";
       break;
@@ -1700,6 +1769,7 @@
 #if !defined(BORINGSSL_ANDROID_SYSTEM)
     case SSL_CHACHA20POLY1305_OLD:
 #endif
+    case SSL_CHACHA20POLY1305:
       alg_bits = 256;
       strength_bits = 256;
       break;
@@ -1802,6 +1872,10 @@
       break;
 
     case SSL_CHACHA20POLY1305_OLD:
+      enc = "ChaCha20-Poly1305-Old";
+      break;
+
+    case SSL_CHACHA20POLY1305:
       enc = "ChaCha20-Poly1305";
       break;
 
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 9ae9bd9..beb57ad 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -2300,9 +2300,7 @@
       (s->s3->alpn_selected || s->s3->next_proto_neg_seen) &&
       cipher != NULL &&
       cipher->algorithm_mkey == SSL_kECDHE &&
-      (cipher->algorithm_enc == SSL_AES128GCM ||
-       cipher->algorithm_enc == SSL_AES256GCM ||
-       cipher->algorithm_enc == SSL_CHACHA20POLY1305_OLD);
+      cipher->algorithm_mac == SSL_AEAD;
 }
 
 const SSL3_ENC_METHOD *ssl3_get_enc_method(uint16_t version) {
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 065efc0..9558f1c 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -50,7 +50,9 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected1[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
@@ -67,8 +69,10 @@
     "+aRSA";
 
 static const ExpectedCipher kExpected2[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -83,6 +87,7 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected3[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -119,7 +124,9 @@
     "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4";
 
 static const ExpectedCipher kExpected6[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
@@ -133,8 +140,10 @@
     "ECDHE-RSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected7[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 1 },
   { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 1 },
   { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 1 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
@@ -157,6 +166,7 @@
 
 static const ExpectedCipher kExpected8[] = {
   { TLS1_CK_ECDHE_RSA_WITH_AES_256_CBC_SHA, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_RC4_128_SHA, 0 },
   { TLS1_CK_ECDHE_RSA_WITH_AES_128_CBC_SHA, 0 },
@@ -169,14 +179,14 @@
 // Exact ciphers may not be used in multi-part rules; they are treated
 // as unknown aliases.
 static const char kRule9[] =
-    "ECDHE-ECDSA-CHACHA20-POLY1305:"
-    "ECDHE-RSA-CHACHA20-POLY1305:"
-    "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
-    "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305";
+    "ECDHE-ECDSA-AES128-GCM-SHA256:"
+    "ECDHE-RSA-AES128-GCM-SHA256:"
+    "!ECDHE-RSA-AES128-GCM-SHA256+RSA:"
+    "!ECDSA+ECDHE-ECDSA-AES128-GCM-SHA256";
 
 static const ExpectedCipher kExpected9[] = {
-  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
-  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 0 },
   { 0, 0 },
 };
 
@@ -205,6 +215,23 @@
   { 0, 0 },
 };
 
+// The shared name of the CHACHA20_POLY1305 variants behaves like a cipher name
+// and not an alias. It may not be used in a multipart rule. (That the shared
+// name works is covered by the standard tests.)
+static const char kRule13[] =
+    "ECDHE-ECDSA-CHACHA20-POLY1305:"
+    "ECDHE-RSA-CHACHA20-POLY1305:"
+    "!ECDHE-RSA-CHACHA20-POLY1305+RSA:"
+    "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305";
+
+static const ExpectedCipher kExpected13[] = {
+  { TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
+  { TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD, 0 },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 0 },
+  { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD, 0 },
+  { 0, 0 },
+};
+
 static CipherTest kCipherTests[] = {
   { kRule1, kExpected1 },
   { kRule2, kExpected2 },
@@ -218,6 +245,7 @@
   { kRule10, kExpected10 },
   { kRule11, kExpected11 },
   { kRule12, kExpected12 },
+  { kRule13, kExpected13 },
   { NULL, NULL },
 };
 
@@ -696,6 +724,8 @@
   { TLS1_CK_PSK_WITH_RC4_128_SHA, "TLS_PSK_WITH_RC4_SHA" },
   { TLS1_CK_ECDHE_PSK_WITH_AES_128_CBC_SHA,
     "TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA" },
+  { TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
+    "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" },
   // These names are non-standard:
   { TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
     "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256" },
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 3a0ecb5..ed4a8b6 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -800,6 +800,7 @@
 	{"ECDHE-ECDSA-AES256-GCM", TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384},
 	{"ECDHE-ECDSA-AES256-SHA", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-ECDSA-AES256-SHA384", TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384},
+	{"ECDHE-ECDSA-CHACHA20-POLY1305", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE-ECDSA-CHACHA20-POLY1305-OLD", TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD},
 	{"ECDHE-ECDSA-RC4-SHA", TLS_ECDHE_ECDSA_WITH_RC4_128_SHA},
 	{"ECDHE-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
@@ -808,12 +809,14 @@
 	{"ECDHE-RSA-AES256-GCM", TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384},
 	{"ECDHE-RSA-AES256-SHA", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
 	{"ECDHE-RSA-AES256-SHA384", TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384},
+	{"ECDHE-RSA-CHACHA20-POLY1305", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256},
 	{"ECDHE-RSA-CHACHA20-POLY1305-OLD", TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD},
 	{"ECDHE-RSA-RC4-SHA", TLS_ECDHE_RSA_WITH_RC4_128_SHA},
 	{"PSK-AES128-CBC-SHA", TLS_PSK_WITH_AES_128_CBC_SHA},
 	{"PSK-AES256-CBC-SHA", TLS_PSK_WITH_AES_256_CBC_SHA},
 	{"ECDHE-PSK-AES128-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA},
 	{"ECDHE-PSK-AES256-CBC-SHA", TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA},
+	{"ECDHE-PSK-CHACHA20-POLY1305", TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256},
 	{"PSK-RC4-SHA", TLS_PSK_WITH_RC4_128_SHA},
 	{"RC4-MD5", TLS_RSA_WITH_RC4_128_MD5},
 	{"RC4-SHA", TLS_RSA_WITH_RC4_128_SHA},