Convert EC curves extension to the new system

Change-Id: Ia8be431bf279e0e895076609ee147bc935f46cee
Reviewed-on: https://boringssl-review.googlesource.com/5465
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index f5a28d9..70743df 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -2010,6 +2010,98 @@
   return ext_ec_point_add_extension(ssl, out);
 }
 
+
+/* EC supported curves.
+ *
+ * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
+
+static void ext_ec_curves_init(SSL *ssl) {
+  OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
+  ssl->s3->tmp.peer_ellipticcurvelist = NULL;
+  ssl->s3->tmp.peer_ellipticcurvelist_length = 0;
+}
+
+static int ext_ec_curves_add_clienthello(SSL *ssl, CBB *out) {
+  if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
+    return 1;
+  }
+
+  CBB contents, curves_bytes;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_elliptic_curves) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16_length_prefixed(&contents, &curves_bytes)) {
+    return 0;
+  }
+
+  const uint16_t *curves;
+  size_t curves_len;
+  tls1_get_curvelist(ssl, 0, &curves, &curves_len);
+
+  size_t i;
+  for (i = 0; i < curves_len; i++) {
+    if (!CBB_add_u16(&curves_bytes, curves[i])) {
+      return 0;
+    }
+  }
+
+  return CBB_flush(out);
+}
+
+static int ext_ec_curves_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                           CBS *contents) {
+  /* This extension is not expected to be echoed by servers and is ignored. */
+  return 1;
+}
+
+static int ext_ec_curves_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                           CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  CBS elliptic_curve_list;
+  if (!CBS_get_u16_length_prefixed(contents, &elliptic_curve_list) ||
+      CBS_len(&elliptic_curve_list) == 0 ||
+      (CBS_len(&elliptic_curve_list) & 1) != 0 ||
+      CBS_len(contents) != 0) {
+    return 0;
+  }
+
+  ssl->s3->tmp.peer_ellipticcurvelist =
+      (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list));
+
+  if (ssl->s3->tmp.peer_ellipticcurvelist == NULL) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  const size_t num_curves = CBS_len(&elliptic_curve_list) / 2;
+  size_t i;
+  for (i = 0; i < num_curves; i++) {
+    if (!CBS_get_u16(&elliptic_curve_list,
+                     &ssl->s3->tmp.peer_ellipticcurvelist[i])) {
+      goto err;
+    }
+  }
+
+  assert(CBS_len(&elliptic_curve_list) == 0);
+  ssl->s3->tmp.peer_ellipticcurvelist_length = num_curves;
+
+  return 1;
+
+err:
+  OPENSSL_free(ssl->s3->tmp.peer_ellipticcurvelist);
+  ssl->s3->tmp.peer_ellipticcurvelist = NULL;
+  *out_alert = SSL_AD_INTERNAL_ERROR;
+  return 0;
+}
+
+static int ext_ec_curves_add_serverhello(SSL *ssl, CBB *out) {
+  /* Servers don't echo this extension. */
+  return 1;
+}
+
+
 /* kExtensions contains all the supported extensions. */
 static const struct tls_extension kExtensions[] = {
   {
@@ -2111,6 +2203,14 @@
     ext_ec_point_parse_clienthello,
     ext_ec_point_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_elliptic_curves,
+    ext_ec_curves_init,
+    ext_ec_curves_add_clienthello,
+    ext_ec_curves_parse_serverhello,
+    ext_ec_curves_parse_clienthello,
+    ext_ec_curves_add_serverhello,
+  },
 };
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -2144,25 +2244,6 @@
   int extdatalen = 0;
   uint8_t *ret = buf;
   uint8_t *orig = buf;
-  /* See if we support any ECC ciphersuites */
-  int using_ecc = 0;
-
-  if (s->version >= TLS1_VERSION || SSL_IS_DTLS(s)) {
-    size_t i;
-    uint32_t alg_k, alg_a;
-    STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(s);
-
-    for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
-      const SSL_CIPHER *c = sk_SSL_CIPHER_value(cipher_stack, i);
-
-      alg_k = c->algorithm_mkey;
-      alg_a = c->algorithm_auth;
-      if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) {
-        using_ecc = 1;
-        break;
-      }
-    }
-  }
 
   /* don't add extensions for SSLv3 unless doing secure renegotiation */
   if (s->client_version == SSL3_VERSION && !s->s3->send_connection_binding) {
@@ -2207,35 +2288,6 @@
   ret += CBB_len(&cbb);
   CBB_cleanup(&cbb);
 
-  if (using_ecc) {
-    long lenmax;
-    const uint16_t *curves;
-    size_t curves_len;
-
-    /* Add TLS extension EllipticCurves to the ClientHello message */
-    tls1_get_curvelist(s, 0, &curves, &curves_len);
-
-    lenmax = limit - ret - 6;
-    if (lenmax < 0) {
-      return NULL;
-    }
-    if (curves_len * 2 > (size_t)lenmax) {
-      return NULL;
-    }
-    if (curves_len * 2 > 65532) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return NULL;
-    }
-
-    s2n(TLSEXT_TYPE_elliptic_curves, ret);
-    s2n((curves_len * 2) + 2, ret);
-
-    s2n(curves_len * 2, ret);
-    for (i = 0; i < curves_len; i++) {
-      s2n(curves[i], ret);
-    }
-  }
-
   if (header_len > 0) {
     size_t clienthello_minsize = 0;
     header_len += ret - orig;
@@ -2338,11 +2390,6 @@
 static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
   CBS extensions;
 
-  /* Clear ECC extensions */
-  OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-  s->s3->tmp.peer_ellipticcurvelist = NULL;
-  s->s3->tmp.peer_ellipticcurvelist_length = 0;
-
   size_t i;
   for (i = 0; i < kNumExtensions; i++) {
     if (kExtensions[i].init != NULL) {
@@ -2393,46 +2440,6 @@
 
       continue;
     }
-
-    if (type == TLSEXT_TYPE_elliptic_curves) {
-      CBS elliptic_curve_list;
-      size_t num_curves;
-
-      if (!CBS_get_u16_length_prefixed(&extension, &elliptic_curve_list) ||
-          CBS_len(&elliptic_curve_list) == 0 ||
-          (CBS_len(&elliptic_curve_list) & 1) != 0 ||
-          CBS_len(&extension) != 0) {
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-
-      OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
-      s->s3->tmp.peer_ellipticcurvelist_length = 0;
-
-      s->s3->tmp.peer_ellipticcurvelist =
-          (uint16_t *)OPENSSL_malloc(CBS_len(&elliptic_curve_list));
-
-      if (s->s3->tmp.peer_ellipticcurvelist == NULL) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-
-      num_curves = CBS_len(&elliptic_curve_list) / 2;
-      for (i = 0; i < num_curves; i++) {
-        if (!CBS_get_u16(&elliptic_curve_list,
-                         &s->s3->tmp.peer_ellipticcurvelist[i])) {
-          *out_alert = SSL_AD_INTERNAL_ERROR;
-          return 0;
-        }
-      }
-
-      if (CBS_len(&elliptic_curve_list) != 0) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-
-      s->s3->tmp.peer_ellipticcurvelist_length = num_curves;
-    }
   }
 
 no_extensions: