Convert the NPN extension to the new system

Change-Id: I92d76aef5a55d5fdef1b9fa24dd7db8047e56df0
Reviewed-on: https://boringssl-review.googlesource.com/5364
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index e8ac683..18d9d4a 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1400,6 +1400,125 @@
 }
 
 
+/* Next protocol negotiation.
+ *
+ * https://htmlpreview.github.io/?https://github.com/agl/technotes/blob/master/nextprotoneg.html */
+
+static void ext_npn_init(SSL *ssl) {
+  ssl->s3->next_proto_neg_seen = 0;
+}
+
+static int ext_npn_add_clienthello(SSL *ssl, CBB *out) {
+  if (ssl->s3->initial_handshake_complete ||
+      ssl->ctx->next_proto_select_cb == NULL ||
+      SSL_IS_DTLS(ssl)) {
+    return 1;
+  }
+
+  if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) ||
+      !CBB_add_u16(out, 0 /* length */)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ext_npn_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                     CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  /* If any of these are false then we should never have sent the NPN
+   * extension in the ClientHello and thus this function should never have been
+   * called. */
+  assert(!ssl->s3->initial_handshake_complete);
+  assert(!SSL_IS_DTLS(ssl));
+  assert(ssl->ctx->next_proto_select_cb != NULL);
+
+  const uint8_t *const orig_contents = CBS_data(contents);
+  const size_t orig_len = CBS_len(contents);
+
+  while (CBS_len(contents) != 0) {
+    CBS proto;
+    if (!CBS_get_u8_length_prefixed(contents, &proto) ||
+        CBS_len(&proto) == 0) {
+      return 0;
+    }
+  }
+
+  uint8_t *selected;
+  uint8_t selected_len;
+  if (ssl->ctx->next_proto_select_cb(
+          ssl, &selected, &selected_len, orig_contents, orig_len,
+          ssl->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  OPENSSL_free(ssl->next_proto_negotiated);
+  ssl->next_proto_negotiated = BUF_memdup(selected, selected_len);
+  if (ssl->next_proto_negotiated == NULL) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  ssl->next_proto_negotiated_len = selected_len;
+  ssl->s3->next_proto_neg_seen = 1;
+
+  return 1;
+}
+
+static int ext_npn_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                     CBS *contents) {
+  if (contents != NULL && CBS_len(contents) != 0) {
+    return 0;
+  }
+
+  if (contents == NULL ||
+      ssl->s3->initial_handshake_complete ||
+      /* If the ALPN extension is seen before NPN, ignore it. (If ALPN is seen
+       * afterwards, parsing the ALPN extension will clear
+       * |next_proto_neg_seen|. */
+      ssl->s3->alpn_selected != NULL ||
+      ssl->ctx->next_protos_advertised_cb == NULL ||
+      SSL_IS_DTLS(ssl)) {
+    return 1;
+  }
+
+  ssl->s3->next_proto_neg_seen = 1;
+  return 1;
+}
+
+static int ext_npn_add_serverhello(SSL *ssl, CBB *out) {
+  /* |next_proto_neg_seen| might have been cleared when an ALPN extension was
+   * parsed. */
+  if (!ssl->s3->next_proto_neg_seen) {
+    return 1;
+  }
+
+  const uint8_t *npa;
+  unsigned npa_len;
+
+  if (ssl->ctx->next_protos_advertised_cb(
+          ssl, &npa, &npa_len, ssl->ctx->next_protos_advertised_cb_arg) !=
+      SSL_TLSEXT_ERR_OK) {
+    ssl->s3->next_proto_neg_seen = 0;
+    return 1;
+  }
+
+  CBB contents;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_next_proto_neg) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_bytes(&contents, npa, npa_len) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+
 /* kExtensions contains all the supported extensions. */
 static const struct tls_extension kExtensions[] = {
   {
@@ -1453,6 +1572,14 @@
     ext_ocsp_parse_clienthello,
     ext_ocsp_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_next_proto_neg,
+    ext_npn_init,
+    ext_npn_add_clienthello,
+    ext_npn_parse_serverhello,
+    ext_npn_parse_clienthello,
+    ext_npn_add_serverhello,
+  },
 };
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -1549,17 +1676,6 @@
   ret += CBB_len(&cbb);
   CBB_cleanup(&cbb);
 
-  if (s->ctx->next_proto_select_cb && !s->s3->initial_handshake_complete &&
-      !SSL_IS_DTLS(s)) {
-    /* The client advertises an emtpy extension to indicate its support for
-     * Next Protocol Negotiation */
-    if (limit - ret - 4 < 0) {
-      return NULL;
-    }
-    s2n(TLSEXT_TYPE_next_proto_neg, ret);
-    s2n(0, ret);
-  }
-
   if (s->signed_cert_timestamps_enabled) {
     /* The client advertises an empty extension to indicate its support for
      * certificate timestamps. */
@@ -1721,7 +1837,6 @@
   int extdatalen = 0;
   uint8_t *orig = buf;
   uint8_t *ret = buf;
-  int next_proto_neg_seen;
   uint32_t alg_k = s->s3->tmp.new_cipher->algorithm_mkey;
   uint32_t alg_a = s->s3->tmp.new_cipher->algorithm_auth;
   int using_ecc = (alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA);
@@ -1807,27 +1922,6 @@
     ret += el;
   }
 
-  next_proto_neg_seen = s->s3->next_proto_neg_seen;
-  s->s3->next_proto_neg_seen = 0;
-  if (next_proto_neg_seen && s->ctx->next_protos_advertised_cb) {
-    const uint8_t *npa;
-    unsigned int npalen;
-    int r;
-
-    r = s->ctx->next_protos_advertised_cb(
-        s, &npa, &npalen, s->ctx->next_protos_advertised_cb_arg);
-    if (r == SSL_TLSEXT_ERR_OK) {
-      if ((long)(limit - ret - 4 - npalen) < 0) {
-        return NULL;
-      }
-      s2n(TLSEXT_TYPE_next_proto_neg, ret);
-      s2n(npalen, ret);
-      memcpy(ret, npa, npalen);
-      ret += npalen;
-      s->s3->next_proto_neg_seen = 1;
-    }
-  }
-
   if (s->s3->alpn_selected) {
     const uint8_t *selected = s->s3->alpn_selected;
     size_t len = s->s3->alpn_selected_len;
@@ -1924,7 +2018,6 @@
   CBS extensions;
 
   s->srtp_profile = NULL;
-  s->s3->next_proto_neg_seen = 0;
 
   OPENSSL_free(s->s3->alpn_selected);
   s->s3->alpn_selected = NULL;
@@ -2041,15 +2134,6 @@
       }
 
       s->s3->tmp.peer_ellipticcurvelist_length = num_curves;
-    } else if (type == TLSEXT_TYPE_next_proto_neg &&
-               !s->s3->initial_handshake_complete &&
-               s->s3->alpn_selected == NULL && !SSL_IS_DTLS(s)) {
-      /* The extension must be empty. */
-      if (CBS_len(&extension) != 0) {
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-      s->s3->next_proto_neg_seen = 1;
     } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
                s->ctx->alpn_select_cb && !s->s3->initial_handshake_complete) {
       if (!tls1_alpn_handle_client_hello(s, &extension, out_alert)) {
@@ -2115,29 +2199,12 @@
   return 1;
 }
 
-/* ssl_next_proto_validate validates a Next Protocol Negotiation block. No
- * elements of zero length are allowed and the set of elements must exactly
- * fill the length of the block. */
-static char ssl_next_proto_validate(const CBS *cbs) {
-  CBS copy = *cbs;
-
-  while (CBS_len(&copy) != 0) {
-    CBS proto;
-    if (!CBS_get_u8_length_prefixed(&copy, &proto) || CBS_len(&proto) == 0) {
-      return 0;
-    }
-  }
-
-  return 1;
-}
-
 static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
   CBS extensions;
 
   /* TODO(davidben): Move all of these to some per-handshake state that gets
    * systematically reset on a new handshake; perhaps allocate it fresh each
    * time so it's not even kept around post-handshake. */
-  s->s3->next_proto_neg_seen = 0;
   s->srtp_profile = NULL;
 
   OPENSSL_free(s->s3->alpn_selected);
@@ -2217,39 +2284,6 @@
         *out_alert = SSL_AD_INTERNAL_ERROR;
         return 0;
       }
-    } else if (type == TLSEXT_TYPE_next_proto_neg &&
-               !s->s3->initial_handshake_complete && !SSL_IS_DTLS(s)) {
-      uint8_t *selected;
-      uint8_t selected_len;
-
-      /* We must have requested it. */
-      if (s->ctx->next_proto_select_cb == NULL) {
-        *out_alert = SSL_AD_UNSUPPORTED_EXTENSION;
-        return 0;
-      }
-
-      /* The data must be valid. */
-      if (!ssl_next_proto_validate(&extension)) {
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-
-      if (s->ctx->next_proto_select_cb(
-              s, &selected, &selected_len, CBS_data(&extension),
-              CBS_len(&extension),
-              s->ctx->next_proto_select_cb_arg) != SSL_TLSEXT_ERR_OK) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-
-      s->next_proto_negotiated = BUF_memdup(selected, selected_len);
-      if (s->next_proto_negotiated == NULL) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-
-      s->next_proto_negotiated_len = selected_len;
-      s->s3->next_proto_neg_seen = 1;
     } else if (type == TLSEXT_TYPE_application_layer_protocol_negotiation &&
                !s->s3->initial_handshake_complete) {
       CBS protocol_name_list, protocol_name;