Convert EC point formats extension to the new system

Change-Id: Iccee3c5b77d45a30b69290ebaea85752259fb269
Reviewed-on: https://boringssl-review.googlesource.com/5464
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 9b64c7b..f5a28d9 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1914,6 +1914,102 @@
   return 1;
 }
 
+
+/* EC point formats.
+ *
+ * https://tools.ietf.org/html/rfc4492#section-5.1.2 */
+
+static int ssl_any_ec_cipher_suites_enabled(const SSL *ssl) {
+  if (ssl->version < TLS1_VERSION && !SSL_IS_DTLS(ssl)) {
+    return 0;
+  }
+
+  const STACK_OF(SSL_CIPHER) *cipher_stack = SSL_get_ciphers(ssl);
+
+  size_t i;
+  for (i = 0; i < sk_SSL_CIPHER_num(cipher_stack); i++) {
+    const SSL_CIPHER *cipher = sk_SSL_CIPHER_value(cipher_stack, i);
+
+    const uint32_t alg_k = cipher->algorithm_mkey;
+    const uint32_t alg_a = cipher->algorithm_auth;
+    if ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA)) {
+      return 1;
+    }
+  }
+
+  return 0;
+}
+
+static void ext_ec_point_init(SSL *ssl) {
+  OPENSSL_free(ssl->s3->tmp.peer_ecpointformatlist);
+  ssl->s3->tmp.peer_ecpointformatlist = NULL;
+  ssl->s3->tmp.peer_ecpointformatlist_length = 0;
+}
+
+static int ext_ec_point_add_extension(SSL *ssl, CBB *out) {
+  const uint8_t *formats;
+  size_t formats_len;
+  tls1_get_formatlist(ssl, &formats, &formats_len);
+
+  CBB contents, format_bytes;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_ec_point_formats) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u8_length_prefixed(&contents, &format_bytes) ||
+      !CBB_add_bytes(&format_bytes, formats, formats_len) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ext_ec_point_add_clienthello(SSL *ssl, CBB *out) {
+  if (!ssl_any_ec_cipher_suites_enabled(ssl)) {
+    return 1;
+  }
+
+  return ext_ec_point_add_extension(ssl, out);
+}
+
+static int ext_ec_point_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                          CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  CBS ec_point_format_list;
+  if (!CBS_get_u8_length_prefixed(contents, &ec_point_format_list) ||
+      CBS_len(contents) != 0) {
+    return 0;
+  }
+
+  if (!CBS_stow(&ec_point_format_list, &ssl->s3->tmp.peer_ecpointformatlist,
+                &ssl->s3->tmp.peer_ecpointformatlist_length)) {
+    *out_alert = SSL_AD_INTERNAL_ERROR;
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ext_ec_point_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                          CBS *contents) {
+  return ext_ec_point_parse_serverhello(ssl, out_alert, contents);
+}
+
+static int ext_ec_point_add_serverhello(SSL *ssl, CBB *out) {
+  const uint32_t alg_k = ssl->s3->tmp.new_cipher->algorithm_mkey;
+  const uint32_t alg_a = ssl->s3->tmp.new_cipher->algorithm_auth;
+  const int using_ecc = ssl->s3->tmp.peer_ecpointformatlist_length != 0 &&
+                        ((alg_k & SSL_kECDHE) || (alg_a & SSL_aECDSA));
+
+  if (!using_ecc) {
+    return 1;
+  }
+
+  return ext_ec_point_add_extension(ssl, out);
+}
+
 /* kExtensions contains all the supported extensions. */
 static const struct tls_extension kExtensions[] = {
   {
@@ -2007,6 +2103,14 @@
     ext_srtp_parse_clienthello,
     ext_srtp_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_ec_point_formats,
+    ext_ec_point_init,
+    ext_ec_point_add_clienthello,
+    ext_ec_point_parse_serverhello,
+    ext_ec_point_parse_clienthello,
+    ext_ec_point_add_serverhello,
+  },
 };
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -2104,31 +2208,9 @@
   CBB_cleanup(&cbb);
 
   if (using_ecc) {
-    /* Add TLS extension ECPointFormats to the ClientHello message */
     long lenmax;
-    const uint8_t *formats;
     const uint16_t *curves;
-    size_t formats_len, curves_len;
-
-    tls1_get_formatlist(s, &formats, &formats_len);
-
-    lenmax = limit - ret - 5;
-    if (lenmax < 0) {
-      return NULL;
-    }
-    if (formats_len > (size_t)lenmax) {
-      return NULL;
-    }
-    if (formats_len > 255) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return NULL;
-    }
-
-    s2n(TLSEXT_TYPE_ec_point_formats, ret);
-    s2n(formats_len + 1, ret);
-    *(ret++) = (uint8_t)formats_len;
-    memcpy(ret, formats, formats_len);
-    ret += formats_len;
+    size_t curves_len;
 
     /* Add TLS extension EllipticCurves to the ClientHello message */
     tls1_get_curvelist(s, 0, &curves, &curves_len);
@@ -2210,10 +2292,6 @@
   int extdatalen = 0;
   uint8_t *orig = buf;
   uint8_t *ret = buf;
-  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);
-  using_ecc = using_ecc && (s->s3->tmp.peer_ecpointformatlist != NULL);
 
   /* don't add extensions for SSLv3, unless doing secure renegotiation */
   if (s->version == SSL3_VERSION && !s->s3->send_connection_binding) {
@@ -2248,34 +2326,6 @@
   ret += CBB_len(&cbb);
   CBB_cleanup(&cbb);
 
-  if (using_ecc) {
-    const uint8_t *plist;
-    size_t plistlen;
-    /* Add TLS extension ECPointFormats to the ServerHello message */
-    long lenmax;
-
-    tls1_get_formatlist(s, &plist, &plistlen);
-
-    lenmax = limit - ret - 5;
-    if (lenmax < 0) {
-      return NULL;
-    }
-    if (plistlen > (size_t)lenmax) {
-      return NULL;
-    }
-    if (plistlen > 255) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return NULL;
-    }
-
-    s2n(TLSEXT_TYPE_ec_point_formats, ret);
-    s2n(plistlen + 1, ret);
-    *(ret++) = (uint8_t)plistlen;
-    memcpy(ret, plist, plistlen);
-    ret += plistlen;
-  }
-  /* Currently the server should not respond with a SupportedCurves extension */
-
   extdatalen = ret - orig - 2;
   if (extdatalen == 0) {
     return orig;
@@ -2289,10 +2339,6 @@
   CBS extensions;
 
   /* Clear ECC extensions */
-  OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
-  s->s3->tmp.peer_ecpointformatlist = NULL;
-  s->s3->tmp.peer_ecpointformatlist_length = 0;
-
   OPENSSL_free(s->s3->tmp.peer_ellipticcurvelist);
   s->s3->tmp.peer_ellipticcurvelist = NULL;
   s->s3->tmp.peer_ellipticcurvelist_length = 0;
@@ -2348,21 +2394,7 @@
       continue;
     }
 
-    if (type == TLSEXT_TYPE_ec_point_formats) {
-      CBS ec_point_format_list;
-
-      if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) ||
-          CBS_len(&extension) != 0) {
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-
-      if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist,
-                    &s->s3->tmp.peer_ecpointformatlist_length)) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-    } else if (type == TLSEXT_TYPE_elliptic_curves) {
+    if (type == TLSEXT_TYPE_elliptic_curves) {
       CBS elliptic_curve_list;
       size_t num_curves;
 
@@ -2437,11 +2469,6 @@
 static int ssl_scan_serverhello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
   CBS extensions;
 
-  /* Clear ECC extensions */
-  OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
-  s->s3->tmp.peer_ecpointformatlist = NULL;
-  s->s3->tmp.peer_ecpointformatlist_length = 0;
-
   uint32_t received = 0;
   size_t i;
   assert(kNumExtensions <= sizeof(received) * 8);
@@ -2495,22 +2522,6 @@
 
       continue;
     }
-
-    if (type == TLSEXT_TYPE_ec_point_formats) {
-      CBS ec_point_format_list;
-
-      if (!CBS_get_u8_length_prefixed(&extension, &ec_point_format_list) ||
-          CBS_len(&extension) != 0) {
-        *out_alert = SSL_AD_DECODE_ERROR;
-        return 0;
-      }
-
-      if (!CBS_stow(&ec_point_format_list, &s->s3->tmp.peer_ecpointformatlist,
-                    &s->s3->tmp.peer_ecpointformatlist_length)) {
-        *out_alert = SSL_AD_INTERNAL_ERROR;
-        return 0;
-      }
-    }
   }
 
 no_extensions: