Implement draft 16 HelloRetryRequest and cookie.

We'll never send cookies, but we'll echo them on request. Implement it
in runner as well and test.

BUG=98

Change-Id: Idd3799f1eaccd52ac42f5e2e5ae07c209318c270
Reviewed-on: https://boringssl-review.googlesource.com/11565
Commit-Queue: David Benjamin <davidben@google.com>
Reviewed-by: Steven Valdez <svaldez@google.com>
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/tls13_client.c b/ssl/tls13_client.c
index 69d2a35..87ccdc4 100644
--- a/ssl/tls13_client.c
+++ b/ssl/tls13_client.c
@@ -54,48 +54,110 @@
   }
 
   CBS cbs, extensions;
-  uint16_t server_wire_version, cipher_suite, group_id;
+  uint16_t server_wire_version;
   CBS_init(&cbs, ssl->init_msg, ssl->init_num);
   if (!CBS_get_u16(&cbs, &server_wire_version) ||
-      !CBS_get_u16(&cbs, &cipher_suite) ||
-      !CBS_get_u16(&cbs, &group_id) ||
-      /* We do not currently parse any HelloRetryRequest extensions. */
       !CBS_get_u16_length_prefixed(&cbs, &extensions) ||
+      /* HelloRetryRequest may not be empty. */
+      CBS_len(&extensions) == 0 ||
       CBS_len(&cbs) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
     ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
     return ssl_hs_error;
   }
 
-  /* TODO(svaldez): Don't do early_data on HelloRetryRequest. */
+  while (CBS_len(&extensions) != 0) {
+    uint16_t type;
+    CBS extension;
+    if (!CBS_get_u16(&extensions, &type) ||
+        !CBS_get_u16_length_prefixed(&extensions, &extension)) {
+      OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+      ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+      return ssl_hs_error;
+    }
 
-  const uint16_t *groups;
-  size_t groups_len;
-  tls1_get_grouplist(ssl, &groups, &groups_len);
-  int found = 0;
-  for (size_t i = 0; i < groups_len; i++) {
-    if (groups[i] == group_id) {
-      found = 1;
-      break;
+    switch (type) {
+      case TLSEXT_TYPE_cookie: {
+        if (hs->cookie != NULL) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+          return ssl_hs_error;
+        }
+
+        /* Cookies may be requested whether or not advertised, so no need to
+         * check. */
+
+        CBS cookie;
+        if (!CBS_get_u16_length_prefixed(&extension, &cookie) ||
+            CBS_len(&cookie) == 0 ||
+            CBS_len(&extension) != 0) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          return ssl_hs_error;
+        }
+
+        if (!CBS_stow(&cookie, &hs->cookie, &hs->cookie_len)) {
+          return ssl_hs_error;
+        }
+        break;
+      }
+
+      case TLSEXT_TYPE_key_share: {
+        if (hs->retry_group != 0) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+          return ssl_hs_error;
+        }
+
+        /* key_share is always advertised, so no need to check. */
+
+        uint16_t group_id;
+        if (!CBS_get_u16(&extension, &group_id) ||
+            CBS_len(&extension) != 0) {
+          OPENSSL_PUT_ERROR(SSL, SSL_R_DECODE_ERROR);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          return ssl_hs_error;
+        }
+
+        /* The group must be supported. */
+        const uint16_t *groups;
+        size_t groups_len;
+        tls1_get_grouplist(ssl, &groups, &groups_len);
+        int found = 0;
+        for (size_t i = 0; i < groups_len; i++) {
+          if (groups[i] == group_id) {
+            found = 1;
+            break;
+          }
+        }
+
+        if (!found) {
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+          OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+          return ssl_hs_error;
+        }
+
+        /* Check that the HelloRetryRequest does not request the key share that
+         * was provided in the initial ClientHello. */
+        if (SSL_ECDH_CTX_get_id(&hs->ecdh_ctx) == group_id) {
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
+          OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
+          return ssl_hs_error;
+        }
+
+        SSL_ECDH_CTX_cleanup(&hs->ecdh_ctx);
+        hs->retry_group = group_id;
+        break;
+      }
+
+      default:
+        OPENSSL_PUT_ERROR(SSL, SSL_R_UNEXPECTED_EXTENSION);
+        ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_UNSUPPORTED_EXTENSION);
+        return ssl_hs_error;
     }
   }
 
-  if (!found) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-    return ssl_hs_error;
-  }
-
-  /* Check that the HelloRetryRequest does not request the key share that was
-   * provided in the initial ClientHello. */
-  if (SSL_ECDH_CTX_get_id(&ssl->s3->hs->ecdh_ctx) == group_id) {
-    ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
-    OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_CURVE);
-    return ssl_hs_error;
-  }
-
-  SSL_ECDH_CTX_cleanup(&ssl->s3->hs->ecdh_ctx);
-  ssl->s3->hs->retry_group = group_id;
-
+  hs->received_hello_retry_request = 1;
   hs->state = state_send_second_client_hello;
   return ssl_hs_ok;
 }
@@ -183,7 +245,7 @@
       case TLSEXT_TYPE_key_share:
         if (have_key_share) {
           OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
           return ssl_hs_error;
         }
         key_share = extension;
@@ -192,7 +254,7 @@
       case TLSEXT_TYPE_pre_shared_key:
         if (have_pre_shared_key) {
           OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
           return ssl_hs_error;
         }
         pre_shared_key = extension;
@@ -201,7 +263,7 @@
       case TLSEXT_TYPE_signature_algorithms:
         if (have_sigalgs) {
           OPENSSL_PUT_ERROR(SSL, SSL_R_DUPLICATE_EXTENSION);
-          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_DECODE_ERROR);
+          ssl3_send_alert(ssl, SSL3_AL_FATAL, SSL_AD_ILLEGAL_PARAMETER);
           return ssl_hs_error;
         }
         sigalgs = extension;
@@ -318,7 +380,7 @@
 
   /* If there was no HelloRetryRequest, the version negotiation logic has
    * already hashed the message. */
-  if (ssl->s3->hs->retry_group != 0 &&
+  if (hs->received_hello_retry_request &&
       !ssl->method->hash_current_message(ssl)) {
     return ssl_hs_error;
   }