Revert "Remove old ChaCha20-Poly1305 AEAD."

This reverts commit def9b46801181c8854f35663a4c26c70843d430a.

(I should have uploaded a new version before sending to the commit queue.)

Change-Id: Iaead89c8d7fc1f56e6294d869db9238b467f520a
Reviewed-on: https://boringssl-review.googlesource.com/13202
Reviewed-by: Adam Langley <agl@google.com>
Commit-Queue: Adam Langley <agl@google.com>
CQ-Verified: CQ bot account: commit-bot@chromium.org <commit-bot@chromium.org>
diff --git a/crypto/cipher/aead_test.cc b/crypto/cipher/aead_test.cc
index 5ec8af3..0c95fb4 100644
--- a/crypto/cipher/aead_test.cc
+++ b/crypto/cipher/aead_test.cc
@@ -314,6 +314,7 @@
   { "aes-128-gcm-siv", EVP_aead_aes_128_gcm_siv, false },
   { "aes-256-gcm-siv", EVP_aead_aes_256_gcm_siv, false },
   { "chacha20-poly1305", EVP_aead_chacha20_poly1305, false },
+  { "chacha20-poly1305-old", EVP_aead_chacha20_poly1305_old, false },
   { "aes-128-cbc-sha1-tls", EVP_aead_aes_128_cbc_sha1_tls, true },
   { "aes-128-cbc-sha1-tls-implicit-iv", EVP_aead_aes_128_cbc_sha1_tls_implicit_iv, true },
   { "aes-128-cbc-sha256-tls", EVP_aead_aes_128_cbc_sha256_tls, true },
diff --git a/crypto/cipher/e_chacha20poly1305.c b/crypto/cipher/e_chacha20poly1305.c
index 88b78bb..ed0d74c 100644
--- a/crypto/cipher/e_chacha20poly1305.c
+++ b/crypto/cipher/e_chacha20poly1305.c
@@ -242,3 +242,59 @@
 const EVP_AEAD *EVP_aead_chacha20_poly1305(void) {
   return &aead_chacha20_poly1305;
 }
+
+static void poly1305_update_old(poly1305_state *ctx, const uint8_t *ad,
+                                size_t ad_len, const uint8_t *ciphertext,
+                                size_t ciphertext_len) {
+  CRYPTO_poly1305_update(ctx, ad, ad_len);
+  poly1305_update_length(ctx, ad_len);
+  CRYPTO_poly1305_update(ctx, ciphertext, ciphertext_len);
+  poly1305_update_length(ctx, ciphertext_len);
+}
+
+static int aead_chacha20_poly1305_old_seal(
+    const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
+    const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
+    const uint8_t *ad, size_t ad_len) {
+  if (nonce_len != 8) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
+    return 0;
+  }
+  uint8_t nonce_96[12];
+  OPENSSL_memset(nonce_96, 0, 4);
+  OPENSSL_memcpy(nonce_96 + 4, nonce, 8);
+  return seal_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
+                   nonce_96, in, in_len, ad, ad_len);
+}
+
+static int aead_chacha20_poly1305_old_open(
+    const EVP_AEAD_CTX *ctx, uint8_t *out, size_t *out_len, size_t max_out_len,
+    const uint8_t *nonce, size_t nonce_len, const uint8_t *in, size_t in_len,
+    const uint8_t *ad, size_t ad_len) {
+  if (nonce_len != 8) {
+    OPENSSL_PUT_ERROR(CIPHER, CIPHER_R_UNSUPPORTED_NONCE_SIZE);
+    return 0;
+  }
+  uint8_t nonce_96[12];
+  OPENSSL_memset(nonce_96, 0, 4);
+  OPENSSL_memcpy(nonce_96 + 4, nonce, 8);
+  return open_impl(poly1305_update_old, ctx, out, out_len, max_out_len,
+                   nonce_96, in, in_len, ad, ad_len);
+}
+
+static const EVP_AEAD aead_chacha20_poly1305_old = {
+    32,                 /* key len */
+    8,                  /* nonce len */
+    POLY1305_TAG_LEN,   /* overhead */
+    POLY1305_TAG_LEN,   /* max tag length */
+    aead_chacha20_poly1305_init,
+    NULL, /* init_with_direction */
+    aead_chacha20_poly1305_cleanup,
+    aead_chacha20_poly1305_old_seal,
+    aead_chacha20_poly1305_old_open,
+    NULL,               /* get_iv */
+};
+
+const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void) {
+  return &aead_chacha20_poly1305_old;
+}
diff --git a/crypto/cipher/test/chacha20_poly1305_tests.txt b/crypto/cipher/test/chacha20_poly1305_tests.txt
index 018eb56..103c196 100644
--- a/crypto/cipher/test/chacha20_poly1305_tests.txt
+++ b/crypto/cipher/test/chacha20_poly1305_tests.txt
@@ -47,6 +47,9 @@
 CT: e275aeb341e1fc9a70c4fd4496fc7cdb
 TAG: 41acd0560ea6843d3e5d4e5babf6e946
 
+# Test vectors from chacha20_poly1305_old_tests.txt, modified for the RFC 7539
+# AEAD construction.
+
 KEY: 9a97f65b9b4c721b960a672145fca8d4e32e67f9111ea979ce9c4826806aeee6
 NONCE: 000000003de9c0da2bd7f91e
 IN: ""
diff --git a/include/openssl/aead.h b/include/openssl/aead.h
index bd655d6..35e0f13 100644
--- a/include/openssl/aead.h
+++ b/include/openssl/aead.h
@@ -100,6 +100,11 @@
  * Poly1305 as described in RFC 7539. */
 OPENSSL_EXPORT const EVP_AEAD *EVP_aead_chacha20_poly1305(void);
 
+/* EVP_aead_chacha20_poly1305_old is an AEAD built from ChaCha20 and
+ * Poly1305 that is used in the experimental ChaCha20-Poly1305 TLS cipher
+ * suites. */
+OPENSSL_EXPORT const EVP_AEAD *EVP_aead_chacha20_poly1305_old(void);
+
 /* EVP_aead_aes_128_ctr_hmac_sha256 is AES-128 in CTR mode with HMAC-SHA256 for
  * authentication. The nonce is 12 bytes; the bottom 32-bits are used as the
  * block counter, thus the maximum plaintext size is 64GB. */
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index 24edf55..4b3e1aa 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -411,6 +411,9 @@
 #define TLS1_CK_ECDH_RSA_WITH_AES_128_GCM_SHA256 0x0300C031
 #define TLS1_CK_ECDH_RSA_WITH_AES_256_GCM_SHA384 0x0300C032
 
+#define TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD 0x0300CC13
+#define TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD 0x0300CC14
+
 /* ChaCha20-Poly1305 cipher suites from RFC 7905. */
 #define TLS1_CK_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 0x0300CCA8
 #define TLS1_CK_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 0x0300CCA9
@@ -586,6 +589,14 @@
 #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"
+
 #define TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 \
   "ECDHE-RSA-CHACHA20-POLY1305"
 #define TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 \
diff --git a/ssl/internal.h b/ssl/internal.h
index 3ad6c96..eb47785 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -188,8 +188,9 @@
 #define SSL_AES256               0x00000004L
 #define SSL_AES128GCM            0x00000008L
 #define SSL_AES256GCM            0x00000010L
-#define SSL_eNULL                0x00000020L
-#define SSL_CHACHA20POLY1305     0x00000040L
+#define SSL_CHACHA20POLY1305_OLD 0x00000020L
+#define SSL_eNULL                0x00000040L
+#define SSL_CHACHA20POLY1305     0x00000080L
 
 #define SSL_AES (SSL_AES128 | SSL_AES256 | SSL_AES128GCM | SSL_AES256GCM)
 
diff --git a/ssl/ssl_cipher.c b/ssl/ssl_cipher.c
index 480304f..7ca79ab 100644
--- a/ssl/ssl_cipher.c
+++ b/ssl/ssl_cipher.c
@@ -542,6 +542,28 @@
 
     /* ChaCha20-Poly1305 cipher suites. */
 
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    {
+     TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_OLD,
+     TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
+     SSL_kECDHE,
+     SSL_aRSA,
+     SSL_CHACHA20POLY1305_OLD,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+
+    {
+     TLS1_TXT_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_OLD,
+     TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD,
+     SSL_kECDHE,
+     SSL_aECDSA,
+     SSL_CHACHA20POLY1305_OLD,
+     SSL_AEAD,
+     SSL_HANDSHAKE_MAC_SHA256,
+    },
+#endif
+
     /* Cipher CCA8 */
     {
      TLS1_TXT_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
@@ -652,7 +674,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, ~0u, 0},
+    {"CHACHA20", ~0u, ~0u, SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD, ~0u,
+     0},
 
     /* MAC aliases */
     {"SHA1", ~0u, ~0u, ~SSL_eNULL, SSL_SHA1, 0},
@@ -709,6 +732,11 @@
     } else if (cipher->algorithm_enc == SSL_AES256GCM) {
       *out_aead = EVP_aead_aes_256_gcm();
       *out_fixed_iv_len = 4;
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305_OLD) {
+      *out_aead = EVP_aead_chacha20_poly1305_old();
+      *out_fixed_iv_len = 0;
+#endif
     } else if (cipher->algorithm_enc == SSL_CHACHA20POLY1305) {
       *out_aead = EVP_aead_chacha20_poly1305();
       *out_fixed_iv_len = 12;
@@ -1216,6 +1244,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 */
@@ -1298,9 +1349,13 @@
                           &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_AES128GCM, ~0u, 0, CIPHER_ADD, -1, 0,
                           &head, &tail);
     ssl_cipher_apply_rule(0, ~0u, ~0u, SSL_AES256GCM, ~0u, 0, CIPHER_ADD, -1, 0,
@@ -1449,7 +1504,8 @@
 }
 
 int SSL_CIPHER_is_CHACHA20POLY1305(const SSL_CIPHER *cipher) {
-  return (cipher->algorithm_enc & SSL_CHACHA20POLY1305) != 0;
+  return (cipher->algorithm_enc &
+          (SSL_CHACHA20POLY1305 | SSL_CHACHA20POLY1305_OLD)) != 0;
 }
 
 int SSL_CIPHER_is_NULL(const SSL_CIPHER *cipher) {
@@ -1566,6 +1622,7 @@
     case SSL_AES256GCM:
       return "AES_256_GCM";
     case SSL_CHACHA20POLY1305:
+    case SSL_CHACHA20POLY1305_OLD:
       return "CHACHA20_POLY1305";
       break;
     default:
@@ -1643,6 +1700,9 @@
 
     case SSL_AES256:
     case SSL_AES256GCM:
+#if !defined(BORINGSSL_ANDROID_SYSTEM)
+    case SSL_CHACHA20POLY1305_OLD:
+#endif
     case SSL_CHACHA20POLY1305:
       alg_bits = 256;
       strength_bits = 256;
@@ -1748,6 +1808,10 @@
       enc = "AESGCM(256)";
       break;
 
+    case SSL_CHACHA20POLY1305_OLD:
+      enc = "ChaCha20-Poly1305-Old";
+      break;
+
     case SSL_CHACHA20POLY1305:
       enc = "ChaCha20-Poly1305";
       break;
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index 5bc1675..952ac11 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -75,7 +75,9 @@
         "ECDHE-RSA-AES128-GCM-SHA256",
         {
             {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},
         },
@@ -89,8 +91,10 @@
         "+aRSA",
         {
             {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},
         },
     },
@@ -103,6 +107,7 @@
         "ECDHE-RSA-AES128-GCM-SHA256",
         {
             {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},
         },
     },
@@ -134,7 +139,9 @@
         "BOGUS1:-BOGUS2:+BOGUS3:!BOGUS4",
         {
             {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},
         },
@@ -146,8 +153,10 @@
         "ECDHE-RSA-AES128-GCM-SHA256",
         {
             {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, 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},
         },
     },
@@ -168,6 +177,7 @@
         {
             {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_AES_128_CBC_SHA, 0},
             {TLS1_CK_RSA_WITH_AES_128_SHA, 0},
             {TLS1_CK_RSA_WITH_AES_256_SHA, 0},
@@ -217,7 +227,9 @@
         "!ECDSA+ECDHE-ECDSA-CHACHA20-POLY1305",
         {
             {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},
         },
     },
 };
@@ -778,6 +790,12 @@
     {TLS1_CK_AES_256_GCM_SHA384, "TLS_AES_256_GCM_SHA384"},
     {TLS1_CK_AES_128_GCM_SHA256, "TLS_AES_128_GCM_SHA256"},
     {TLS1_CK_CHACHA20_POLY1305_SHA256, "TLS_CHACHA20_POLY1305_SHA256"},
+
+    // These names are non-standard:
+    {TLS1_CK_ECDHE_RSA_CHACHA20_POLY1305_OLD,
+     "TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256"},
+    {TLS1_CK_ECDHE_ECDSA_CHACHA20_POLY1305_OLD,
+     "TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256"},
 };
 
 static bool TestCipherGetRFCName(void) {
@@ -1929,20 +1947,20 @@
   }
 
   static const uint8_t kTLS12ClientHello[] = {
-      0x16, 0x03, 0x01, 0x00, 0x9a, 0x01, 0x00, 0x00, 0x96, 0x03, 0x03, 0x00,
+      0x16, 0x03, 0x01, 0x00, 0x9e, 0x01, 0x00, 0x00, 0x9a, 0x03, 0x03, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
       0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x36, 0xcc, 0xa9,
-      0xcc, 0xa8, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x2c, 0xc0, 0x30,
-      0x00, 0x9f, 0xc0, 0x09, 0xc0, 0x23, 0xc0, 0x13, 0xc0, 0x27, 0x00, 0x33,
-      0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x24, 0xc0, 0x14, 0xc0, 0x28, 0x00, 0x39,
-      0x00, 0x6b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x3c, 0x00, 0x35,
-      0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37, 0xff, 0x01, 0x00, 0x01,
-      0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
-      0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08,
-      0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x00, 0x0b, 0x00,
-      0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00,
-      0x17, 0x00, 0x18,
+      0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3a, 0xcc, 0xa9,
+      0xcc, 0xa8, 0xcc, 0x14, 0xcc, 0x13, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, 0x9e,
+      0xc0, 0x2c, 0xc0, 0x30, 0x00, 0x9f, 0xc0, 0x09, 0xc0, 0x23, 0xc0, 0x13,
+      0xc0, 0x27, 0x00, 0x33, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x24, 0xc0, 0x14,
+      0xc0, 0x28, 0x00, 0x39, 0x00, 0x6b, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f,
+      0x00, 0x3c, 0x00, 0x35, 0x00, 0x3d, 0x00, 0x0a, 0x01, 0x00, 0x00, 0x37,
+      0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x23, 0x00,
+      0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04,
+      0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02,
+      0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00, 0x08, 0x00,
+      0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18,
   };
   if (!ClientHelloMatches(TLS1_2_VERSION, kTLS12ClientHello,
                           sizeof(kTLS12ClientHello))) {
diff --git a/ssl/test/runner/chacha20_poly1305.go b/ssl/test/runner/chacha20_poly1305.go
index 866f724..f58e834 100644
--- a/ssl/test/runner/chacha20_poly1305.go
+++ b/ssl/test/runner/chacha20_poly1305.go
@@ -119,6 +119,9 @@
 // RFC 7539 and draft-agl-tls-chacha20poly1305-04.
 type chaCha20Poly1305 struct {
 	key [32]byte
+	// oldMode, if true, indicates that the draft spec should be
+	// implemented rather than the final, RFC version.
+	oldMode bool
 }
 
 func newChaCha20Poly1305(key []byte) (cipher.AEAD, error) {
@@ -130,8 +133,23 @@
 	return aead, nil
 }
 
+func newChaCha20Poly1305Old(key []byte) (cipher.AEAD, error) {
+	if len(key) != 32 {
+		return nil, errors.New("bad key length")
+	}
+	aead := &chaCha20Poly1305{
+		oldMode: true,
+	}
+	copy(aead.key[:], key)
+	return aead, nil
+}
+
 func (c *chaCha20Poly1305) NonceSize() int {
-	return 12
+	if c.oldMode {
+		return 8
+	} else {
+		return 12
+	}
 }
 
 func (c *chaCha20Poly1305) Overhead() int { return 16 }
@@ -158,6 +176,21 @@
 	poly1305.Sum(tag, input, &poly1305Key)
 }
 
+func (c *chaCha20Poly1305) poly1305Old(tag *[16]byte, nonce, ciphertext, additionalData []byte) {
+	input := make([]byte, 0, len(additionalData)+8+len(ciphertext)+8)
+	input = append(input, additionalData...)
+	input, out := sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(additionalData)))
+	input = append(input, ciphertext...)
+	input, out = sliceForAppend(input, 8)
+	binary.LittleEndian.PutUint64(out, uint64(len(ciphertext)))
+
+	var poly1305Key [32]byte
+	chaCha20(poly1305Key[:], poly1305Key[:], c.key[:], nonce, 0)
+
+	poly1305.Sum(tag, input, &poly1305Key)
+}
+
 func (c *chaCha20Poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
 	if len(nonce) != c.NonceSize() {
 		panic("Bad nonce length")
@@ -167,7 +200,11 @@
 	chaCha20(out[:len(plaintext)], plaintext, c.key[:], nonce, 1)
 
 	var tag [16]byte
-	c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+	if c.oldMode {
+		c.poly1305Old(&tag, nonce, out[:len(plaintext)], additionalData)
+	} else {
+		c.poly1305(&tag, nonce, out[:len(plaintext)], additionalData)
+	}
 	copy(out[len(plaintext):], tag[:])
 
 	return ret
@@ -183,7 +220,11 @@
 	plaintextLen := len(ciphertext) - 16
 
 	var tag [16]byte
-	c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	if c.oldMode {
+		c.poly1305Old(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	} else {
+		c.poly1305(&tag, nonce, ciphertext[:plaintextLen], additionalData)
+	}
 	if subtle.ConstantTimeCompare(tag[:], ciphertext[plaintextLen:]) != 1 {
 		return nil, errors.New("chacha20: message authentication failed")
 	}
diff --git a/ssl/test/runner/chacha20_poly1305_test.go b/ssl/test/runner/chacha20_poly1305_test.go
index 38c4b70..e1597a4 100644
--- a/ssl/test/runner/chacha20_poly1305_test.go
+++ b/ssl/test/runner/chacha20_poly1305_test.go
@@ -88,6 +88,38 @@
 	return out
 }
 
+// See draft-agl-tls-chacha20poly1305-04, section 7.
+func TestChaCha20Poly1305Old(t *testing.T) {
+	key := decodeHexOrPanic("4290bcb154173531f314af57f3be3b5006da371ece272afa1b5dbdd1100a1007")
+	input := decodeHexOrPanic("86d09974840bded2a5ca")
+	nonce := decodeHexOrPanic("cd7cf67be39c794a")
+	ad := decodeHexOrPanic("87e229d4500845a079c0")
+	output := decodeHexOrPanic("e3e446f7ede9a19b62a4677dabf4e3d24b876bb284753896e1d6")
+
+	aead, err := newChaCha20Poly1305Old(key)
+	if err != nil {
+		t.Fatal(err)
+	}
+
+	out, err := aead.Open(nil, nonce, output, ad)
+	if err != nil {
+		t.Errorf("Open failed: %s", err)
+	} else if !bytes.Equal(out, input) {
+		t.Errorf("Open gave %x, wanted %x", out, input)
+	}
+
+	out = aead.Seal(nil, nonce, input, ad)
+	if !bytes.Equal(out, output) {
+		t.Errorf("Open gave %x, wanted %x", out, output)
+	}
+
+	out[0]++
+	_, err = aead.Open(nil, nonce, out, ad)
+	if err == nil {
+		t.Errorf("Open on malformed data unexpectedly succeeded")
+	}
+}
+
 var chaCha20Poly1305TestVectors = []struct {
 	key, input, nonce, ad, output string
 }{
diff --git a/ssl/test/runner/cipher_suites.go b/ssl/test/runner/cipher_suites.go
index dd1bab6..fe283eb 100644
--- a/ssl/test/runner/cipher_suites.go
+++ b/ssl/test/runner/cipher_suites.go
@@ -106,6 +106,8 @@
 	{TLS_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, nil, suiteTLS13 | suiteSHA384, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
 	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, 32, 0, ivLenChaCha20Poly1305, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305},
+	{TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD, 32, 0, noIV, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadCHACHA20POLY1305Old},
+	{TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD, 32, 0, noIV, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadCHACHA20POLY1305Old},
 	{TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, ecdheRSAKA, suiteECDHE | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, 16, 0, ivLenAESGCM, ecdheECDSAKA, suiteECDHE | suiteECDSA | suiteTLS12, nil, nil, aeadAESGCM},
 	{TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, 32, 0, ivLenAESGCM, ecdheRSAKA, suiteECDHE | suiteTLS12 | suiteSHA384, nil, nil, aeadAESGCM},
@@ -294,6 +296,14 @@
 	return &tlsAead{&fixedNonceAEAD{nonce1, nonce2, aead}, true}
 }
 
+func aeadCHACHA20POLY1305Old(version uint16, key, fixedNonce []byte) *tlsAead {
+	aead, err := newChaCha20Poly1305Old(key)
+	if err != nil {
+		panic(err)
+	}
+	return &tlsAead{aead, false}
+}
+
 func xorSlice(out, in []byte) {
 	for i := range out {
 		out[i] ^= in[i]
@@ -509,6 +519,8 @@
 
 // Additional cipher suite IDs, not IANA-assigned.
 const (
+	TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD   uint16 = 0xcc13
+	TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD uint16 = 0xcc14
 	TLS_AES_128_GCM_SHA256                            uint16 = 0x1301
 	TLS_AES_256_GCM_SHA384                            uint16 = 0x1302
 	TLS_CHACHA20_POLY1305_SHA256                      uint16 = 0x1303
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 54bfca5..3a884bc 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -1103,6 +1103,7 @@
 	{"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-RSA-AES128-GCM", TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
 	{"ECDHE-RSA-AES128-SHA", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
 	{"ECDHE-RSA-AES128-SHA256", TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
@@ -1110,6 +1111,7 @@
 	{"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},
 	{"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},
diff --git a/tool/speed.cc b/tool/speed.cc
index cf4d8ba..52708c0 100644
--- a/tool/speed.cc
+++ b/tool/speed.cc
@@ -618,6 +618,8 @@
       !SpeedAEAD(EVP_aead_aes_256_gcm(), "AES-256-GCM", kTLSADLen, selected) ||
       !SpeedAEAD(EVP_aead_chacha20_poly1305(), "ChaCha20-Poly1305", kTLSADLen,
                  selected) ||
+      !SpeedAEAD(EVP_aead_chacha20_poly1305_old(), "ChaCha20-Poly1305-Old",
+                 kTLSADLen, selected) ||
       !SpeedAEAD(EVP_aead_des_ede3_cbc_sha1_tls(), "DES-EDE3-CBC-SHA1",
                  kLegacyADLen, selected) ||
       !SpeedAEAD(EVP_aead_aes_128_cbc_sha1_tls(), "AES-128-CBC-SHA1",
diff --git a/util/all_tests.json b/util/all_tests.json
index 2382893..a2f64aa 100644
--- a/util/all_tests.json
+++ b/util/all_tests.json
@@ -11,6 +11,7 @@
 	["crypto/cipher/aead_test", "aes-128-gcm-siv", "crypto/cipher/test/aes_128_gcm_siv_tests.txt"],
 	["crypto/cipher/aead_test", "aes-256-gcm-siv", "crypto/cipher/test/aes_256_gcm_siv_tests.txt"],
 	["crypto/cipher/aead_test", "chacha20-poly1305", "crypto/cipher/test/chacha20_poly1305_tests.txt"],
+	["crypto/cipher/aead_test", "chacha20-poly1305-old", "crypto/cipher/test/chacha20_poly1305_old_tests.txt"],
 	["crypto/cipher/aead_test", "aes-128-cbc-sha1-tls", "crypto/cipher/test/aes_128_cbc_sha1_tls_tests.txt"],
 	["crypto/cipher/aead_test", "aes-128-cbc-sha1-tls-implicit-iv", "crypto/cipher/test/aes_128_cbc_sha1_tls_implicit_iv_tests.txt"],
 	["crypto/cipher/aead_test", "aes-128-cbc-sha256-tls", "crypto/cipher/test/aes_128_cbc_sha256_tls_tests.txt"],