Convert the SRTP extension to the new system

Change-Id: I12f1d06562c34d357d82bbde7e5d0c15096046e6
Reviewed-on: https://boringssl-review.googlesource.com/5463
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/tls1.h b/include/openssl/tls1.h
index b3be3e1..b68db3d 100644
--- a/include/openssl/tls1.h
+++ b/include/openssl/tls1.h
@@ -209,7 +209,7 @@
 #define TLSEXT_TYPE_signature_algorithms 13
 
 /* ExtensionType value from RFC5764 */
-#define TLSEXT_TYPE_use_srtp 14
+#define TLSEXT_TYPE_srtp 14
 
 /* ExtensionType value from RFC5620 */
 #define TLSEXT_TYPE_heartbeat 15
diff --git a/ssl/d1_srtp.c b/ssl/d1_srtp.c
index a698b10..48ecfb2 100644
--- a/ssl/d1_srtp.c
+++ b/ssl/d1_srtp.c
@@ -125,12 +125,12 @@
 #include <openssl/srtp.h>
 
 
-static const SRTP_PROTECTION_PROFILE srtp_known_profiles[] = {
+const SRTP_PROTECTION_PROFILE kSRTPProfiles[] = {
     {
-     "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80,
+        "SRTP_AES128_CM_SHA1_80", SRTP_AES128_CM_SHA1_80,
     },
     {
-     "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32,
+        "SRTP_AES128_CM_SHA1_32", SRTP_AES128_CM_SHA1_32,
     },
     {0},
 };
@@ -140,7 +140,7 @@
                                 size_t len) {
   const SRTP_PROTECTION_PROFILE *p;
 
-  p = srtp_known_profiles;
+  p = kSRTPProfiles;
   while (p->name) {
     if (len == strlen(p->name) && !strncmp(p->name, profile_name, len)) {
       *pptr = p;
@@ -153,22 +153,6 @@
   return 0;
 }
 
-static int find_profile_by_num(unsigned profile_num,
-                               const SRTP_PROTECTION_PROFILE **pptr) {
-  const SRTP_PROTECTION_PROFILE *p;
-
-  p = srtp_known_profiles;
-  while (p->name) {
-    if (p->id == profile_num) {
-      *pptr = p;
-      return 1;
-    }
-    p++;
-  }
-
-  return 0;
-}
-
 static int ssl_ctx_make_profiles(const char *profiles_string,
                                  STACK_OF(SRTP_PROTECTION_PROFILE) **out) {
   STACK_OF(SRTP_PROTECTION_PROFILE) *profiles;
@@ -240,181 +224,3 @@
   /* This API inverts its return value. */
   return !SSL_set_srtp_profiles(s, profiles);
 }
-
-/* Note: this function returns 0 length if there are no profiles specified */
-int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen) {
-  int ct = 0;
-  int i;
-  STACK_OF(SRTP_PROTECTION_PROFILE) *clnt = 0;
-  const SRTP_PROTECTION_PROFILE *prof;
-
-  clnt = SSL_get_srtp_profiles(s);
-  ct = sk_SRTP_PROTECTION_PROFILE_num(clnt); /* -1 if clnt == 0 */
-
-  if (p) {
-    if (ct == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_EMPTY_SRTP_PROTECTION_PROFILE_LIST);
-      return 0;
-    }
-
-    if (2 + ct * 2 + 1 > maxlen) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG);
-      return 0;
-    }
-
-    /* Add the length */
-    s2n(ct * 2, p);
-    for (i = 0; i < ct; i++) {
-      prof = sk_SRTP_PROTECTION_PROFILE_value(clnt, i);
-      s2n(prof->id, p);
-    }
-
-    /* Add an empty use_mki value */
-    *p++ = 0;
-  }
-
-  *len = 2 + ct * 2 + 1;
-
-  return 1;
-}
-
-int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) {
-  CBS profile_ids, srtp_mki;
-  const SRTP_PROTECTION_PROFILE *cprof, *sprof;
-  STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles = 0, *server_profiles;
-  size_t i, j;
-  int ret = 0;
-
-  if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) ||
-      CBS_len(&profile_ids) < 2 ||
-      !CBS_get_u8_length_prefixed(cbs, &srtp_mki) ||
-      CBS_len(cbs) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    goto done;
-  }
-
-  client_profiles = sk_SRTP_PROTECTION_PROFILE_new_null();
-  if (client_profiles == NULL) {
-    OPENSSL_PUT_ERROR(SSL, ERR_R_MALLOC_FAILURE);
-    *out_alert = SSL_AD_INTERNAL_ERROR;
-    goto done;
-  }
-
-  while (CBS_len(&profile_ids) > 0) {
-    uint16_t profile_id;
-
-    if (!CBS_get_u16(&profile_ids, &profile_id)) {
-      *out_alert = SSL_AD_DECODE_ERROR;
-      goto done;
-    }
-
-    if (find_profile_by_num(profile_id, &cprof)) {
-      sk_SRTP_PROTECTION_PROFILE_push(client_profiles, cprof);
-    }
-  }
-
-  /* Discard the MKI value for now. */
-
-  server_profiles = SSL_get_srtp_profiles(s);
-
-  /* Pick the server's most preferred profile. */
-  for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) {
-    sprof = sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i);
-
-    for (j = 0; j < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); j++) {
-      cprof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, j);
-
-      if (cprof->id == sprof->id) {
-        s->srtp_profile = sprof;
-        ret = 1;
-        goto done;
-      }
-    }
-  }
-
-  ret = 1;
-
-done:
-  if (client_profiles) {
-    sk_SRTP_PROTECTION_PROFILE_free(client_profiles);
-  }
-
-  return ret;
-}
-
-int ssl_add_serverhello_use_srtp_ext(SSL *s, unsigned char *p, int *len,
-                                     int maxlen) {
-  if (p) {
-    if (maxlen < 5) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_SRTP_PROTECTION_PROFILE_LIST_TOO_LONG);
-      return 0;
-    }
-
-    if (s->srtp_profile == 0) {
-      OPENSSL_PUT_ERROR(SSL, SSL_R_USE_SRTP_NOT_NEGOTIATED);
-      return 0;
-    }
-
-    s2n(2, p);
-    s2n(s->srtp_profile->id, p);
-    *p++ = 0;
-  }
-
-  *len = 5;
-
-  return 1;
-}
-
-int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert) {
-  CBS profile_ids, srtp_mki;
-  uint16_t profile_id;
-  size_t i;
-
-  STACK_OF(SRTP_PROTECTION_PROFILE) *client_profiles;
-  const SRTP_PROTECTION_PROFILE *prof;
-
-  /* The extension consists of a u16-prefixed profile ID list containing a
-   * single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field.
-   *
-   * See https://tools.ietf.org/html/rfc5764#section-4.1.1 */
-  if (!CBS_get_u16_length_prefixed(cbs, &profile_ids) ||
-      !CBS_get_u16(&profile_ids, &profile_id) || CBS_len(&profile_ids) != 0 ||
-      !CBS_get_u8_length_prefixed(cbs, &srtp_mki) || CBS_len(cbs) != 0) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
-  }
-
-  if (CBS_len(&srtp_mki) != 0) {
-    /* Must be no MKI, since we never offer one. */
-    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_MKI_VALUE);
-    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-    return 0;
-  }
-
-  client_profiles = SSL_get_srtp_profiles(s);
-
-  /* Throw an error if the server gave us an unsolicited extension */
-  if (client_profiles == NULL) {
-    OPENSSL_PUT_ERROR(SSL, SSL_R_NO_SRTP_PROFILES);
-    *out_alert = SSL_AD_DECODE_ERROR;
-    return 0;
-  }
-
-  /* Check to see if the server gave us something we support
-     (and presumably offered). */
-  for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(client_profiles); i++) {
-    prof = sk_SRTP_PROTECTION_PROFILE_value(client_profiles, i);
-
-    if (prof->id == profile_id) {
-      s->srtp_profile = prof;
-      *out_alert = 0;
-      return 1;
-    }
-  }
-
-  OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
-  *out_alert = SSL_AD_ILLEGAL_PARAMETER;
-  return 0;
-}
diff --git a/ssl/internal.h b/ssl/internal.h
index f107452..1a28245 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1168,9 +1168,4 @@
                             CBS *cbs, EVP_PKEY *pkey);
 void ssl_set_client_disabled(SSL *s);
 
-int ssl_add_clienthello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen);
-int ssl_parse_clienthello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert);
-int ssl_add_serverhello_use_srtp_ext(SSL *s, uint8_t *p, int *len, int maxlen);
-int ssl_parse_serverhello_use_srtp_ext(SSL *s, CBS *cbs, int *out_alert);
-
 #endif /* OPENSSL_HEADER_SSL_INTERNAL_H */
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 4551025..9b64c7b 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1760,6 +1760,160 @@
   return 1;
 }
 
+
+/* Secure Real-time Transport Protocol (SRTP) extension.
+ *
+ * https://tools.ietf.org/html/rfc5764 */
+
+extern const SRTP_PROTECTION_PROFILE kSRTPProfiles[];
+
+static void ext_srtp_init(SSL *ssl) {
+  ssl->srtp_profile = NULL;
+}
+
+static int ext_srtp_add_clienthello(SSL *ssl, CBB *out) {
+  STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
+  if (profiles == NULL) {
+    return 1;
+  }
+  const size_t num_profiles = sk_SRTP_PROTECTION_PROFILE_num(profiles);
+  if (num_profiles == 0) {
+    return 1;
+  }
+
+  CBB contents, profile_ids;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16_length_prefixed(&contents, &profile_ids)) {
+    return 0;
+  }
+
+  size_t i;
+  for (i = 0; i < num_profiles; i++) {
+    if (!CBB_add_u16(&profile_ids,
+                     sk_SRTP_PROTECTION_PROFILE_value(profiles, i)->id)) {
+      return 0;
+    }
+  }
+
+  if (!CBB_add_u8(&contents, 0 /* empty use_mki value */) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
+static int ext_srtp_parse_serverhello(SSL *ssl, uint8_t *out_alert,
+                                      CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  /* The extension consists of a u16-prefixed profile ID list containing a
+   * single uint16_t profile ID, then followed by a u8-prefixed srtp_mki field.
+   *
+   * See https://tools.ietf.org/html/rfc5764#section-4.1.1 */
+  CBS profile_ids, srtp_mki;
+  uint16_t profile_id;
+  if (!CBS_get_u16_length_prefixed(contents, &profile_ids) ||
+      !CBS_get_u16(&profile_ids, &profile_id) ||
+      CBS_len(&profile_ids) != 0 ||
+      !CBS_get_u8_length_prefixed(contents, &srtp_mki) ||
+      CBS_len(contents) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
+    return 0;
+  }
+
+  if (CBS_len(&srtp_mki) != 0) {
+    /* Must be no MKI, since we never offer one. */
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_MKI_VALUE);
+    *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+    return 0;
+  }
+
+  STACK_OF(SRTP_PROTECTION_PROFILE) *profiles = SSL_get_srtp_profiles(ssl);
+
+  /* Check to see if the server gave us something we support (and presumably
+   * offered). */
+  size_t i;
+  for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(profiles); i++) {
+    const SRTP_PROTECTION_PROFILE *profile =
+        sk_SRTP_PROTECTION_PROFILE_value(profiles, i);
+
+    if (profile->id == profile_id) {
+      ssl->srtp_profile = profile;
+      return 1;
+    }
+  }
+
+  OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
+  *out_alert = SSL_AD_ILLEGAL_PARAMETER;
+  return 0;
+}
+
+static int ext_srtp_parse_clienthello(SSL *ssl, uint8_t *out_alert,
+                                      CBS *contents) {
+  if (contents == NULL) {
+    return 1;
+  }
+
+  CBS profile_ids, srtp_mki;
+  if (!CBS_get_u16_length_prefixed(contents, &profile_ids) ||
+      CBS_len(&profile_ids) < 2 ||
+      !CBS_get_u8_length_prefixed(contents, &srtp_mki) ||
+      CBS_len(contents) != 0) {
+    OPENSSL_PUT_ERROR(SSL, SSL_R_BAD_SRTP_PROTECTION_PROFILE_LIST);
+    return 0;
+  }
+  /* Discard the MKI value for now. */
+
+  const STACK_OF(SRTP_PROTECTION_PROFILE) *server_profiles =
+      SSL_get_srtp_profiles(ssl);
+
+  /* Pick the server's most preferred profile. */
+  size_t i;
+  for (i = 0; i < sk_SRTP_PROTECTION_PROFILE_num(server_profiles); i++) {
+    const SRTP_PROTECTION_PROFILE *server_profile =
+        sk_SRTP_PROTECTION_PROFILE_value(server_profiles, i);
+
+    CBS profile_ids_tmp;
+    CBS_init(&profile_ids_tmp, CBS_data(&profile_ids), CBS_len(&profile_ids));
+
+    while (CBS_len(&profile_ids_tmp) > 0) {
+      uint16_t profile_id;
+      if (!CBS_get_u16(&profile_ids_tmp, &profile_id)) {
+        return 0;
+      }
+
+      if (server_profile->id == profile_id) {
+        ssl->srtp_profile = server_profile;
+        return 1;
+      }
+    }
+  }
+
+  return 1;
+}
+
+static int ext_srtp_add_serverhello(SSL *ssl, CBB *out) {
+  if (ssl->srtp_profile == NULL) {
+    return 1;
+  }
+
+  CBB contents, profile_ids;
+  if (!CBB_add_u16(out, TLSEXT_TYPE_srtp) ||
+      !CBB_add_u16_length_prefixed(out, &contents) ||
+      !CBB_add_u16_length_prefixed(&contents, &profile_ids) ||
+      !CBB_add_u16(&profile_ids, ssl->srtp_profile->id) ||
+      !CBB_add_u8(&contents, 0 /* empty MKI */) ||
+      !CBB_flush(out)) {
+    return 0;
+  }
+
+  return 1;
+}
+
 /* kExtensions contains all the supported extensions. */
 static const struct tls_extension kExtensions[] = {
   {
@@ -1845,6 +1999,14 @@
     ext_channel_id_parse_clienthello,
     ext_channel_id_add_serverhello,
   },
+  {
+    TLSEXT_TYPE_srtp,
+    ext_srtp_init,
+    ext_srtp_add_clienthello,
+    ext_srtp_parse_serverhello,
+    ext_srtp_parse_clienthello,
+    ext_srtp_add_serverhello,
+  },
 };
 
 #define kNumExtensions (sizeof(kExtensions) / sizeof(struct tls_extension))
@@ -1941,25 +2103,6 @@
   ret += CBB_len(&cbb);
   CBB_cleanup(&cbb);
 
-  if (SSL_get_srtp_profiles(s)) {
-    int el;
-
-    ssl_add_clienthello_use_srtp_ext(s, 0, &el, 0);
-
-    if ((limit - ret - 4 - el) < 0) {
-      return NULL;
-    }
-
-    s2n(TLSEXT_TYPE_use_srtp, ret);
-    s2n(el, ret);
-
-    if (!ssl_add_clienthello_use_srtp_ext(s, ret, &el, el)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return NULL;
-    }
-    ret += el;
-  }
-
   if (using_ecc) {
     /* Add TLS extension ECPointFormats to the ClientHello message */
     long lenmax;
@@ -2133,25 +2276,6 @@
   }
   /* Currently the server should not respond with a SupportedCurves extension */
 
-  if (s->srtp_profile) {
-    int el;
-
-    ssl_add_serverhello_use_srtp_ext(s, 0, &el, 0);
-
-    if ((limit - ret - 4 - el) < 0) {
-      return NULL;
-    }
-
-    s2n(TLSEXT_TYPE_use_srtp, ret);
-    s2n(el, ret);
-
-    if (!ssl_add_serverhello_use_srtp_ext(s, ret, &el, el)) {
-      OPENSSL_PUT_ERROR(SSL, ERR_R_INTERNAL_ERROR);
-      return NULL;
-    }
-    ret += el;
-  }
-
   extdatalen = ret - orig - 2;
   if (extdatalen == 0) {
     return orig;
@@ -2164,8 +2288,6 @@
 static int ssl_scan_clienthello_tlsext(SSL *s, CBS *cbs, int *out_alert) {
   CBS extensions;
 
-  s->srtp_profile = NULL;
-
   /* Clear ECC extensions */
   OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
   s->s3->tmp.peer_ecpointformatlist = NULL;
@@ -2278,10 +2400,6 @@
       }
 
       s->s3->tmp.peer_ellipticcurvelist_length = num_curves;
-    } else if (type == TLSEXT_TYPE_use_srtp) {
-      if (!ssl_parse_clienthello_use_srtp_ext(s, &extension, out_alert)) {
-        return 0;
-      }
     }
   }
 
@@ -2319,11 +2437,6 @@
 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->srtp_profile = NULL;
-
   /* Clear ECC extensions */
   OPENSSL_free(s->s3->tmp.peer_ecpointformatlist);
   s->s3->tmp.peer_ecpointformatlist = NULL;
@@ -2397,10 +2510,6 @@
         *out_alert = SSL_AD_INTERNAL_ERROR;
         return 0;
       }
-    } else if (type == TLSEXT_TYPE_use_srtp) {
-      if (!ssl_parse_serverhello_use_srtp_ext(s, &extension, out_alert)) {
-        return 0;
-      }
     }
   }