Serialize SSL curve list in handoff and check it on application.

A split SSL handshake may involve 2 binaries, potentially built at
different versions: call them the "handoff/handback" binary and the
"handshake" binary.  We would like to guarantee that the
handoff/handback binary does not make any promises that the handshake
binary cannot keep.

d2ed382 serialized |kCiphers|; this commit extends the same approach
to |kNamedGroups|.

Change-Id: Idb13e54e9b189236309f6054a36872c5a4d96985
Reviewed-on: https://boringssl-review.googlesource.com/c/32825
Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/ssl/handoff.cc b/ssl/handoff.cc
index a49d289..4cca981 100644
--- a/ssl/handoff.cc
+++ b/ssl/handoff.cc
@@ -37,6 +37,15 @@
       return false;
     }
   }
+  CBB curves;
+  if (!CBB_add_asn1(out, &curves, CBS_ASN1_OCTETSTRING)) {
+    return false;
+  }
+  for (const NamedGroup& g : NamedGroups()) {
+    if (!CBB_add_u16(&curves, g.group_id)) {
+      return false;
+    }
+  }
   return CBB_flush(out);
 }
 
@@ -120,7 +129,52 @@
   for (const SSL_CIPHER *unsupported_cipher : unsupported.get()) {
     ssl->config->cipher_list->Remove(unsupported_cipher);
   }
-  return sk_SSL_CIPHER_num(SSL_get_ciphers(ssl)) > 0;
+  if (sk_SSL_CIPHER_num(SSL_get_ciphers(ssl)) == 0) {
+    return false;
+  }
+
+  CBS curves;
+  if (!CBS_get_asn1(in, &curves, CBS_ASN1_OCTETSTRING)) {
+    return false;
+  }
+  Array<uint16_t> supported_curves;
+  if (!supported_curves.Init(CBS_len(&curves) / 2)) {
+    return false;
+  }
+  size_t idx = 0;
+  while (CBS_len(&curves)) {
+    uint16_t curve;
+    if (!CBS_get_u16(&curves, &curve)) {
+      return false;
+    }
+    supported_curves[idx++] = curve;
+  }
+  Span<const uint16_t> configured_curves =
+      tls1_get_grouplist(ssl->s3->hs.get());
+  Array<uint16_t> new_configured_curves;
+  if (!new_configured_curves.Init(configured_curves.size())) {
+    return false;
+  }
+  idx = 0;
+  for (uint16_t configured_curve : configured_curves) {
+    bool ok = false;
+    for (uint16_t supported_curve : supported_curves) {
+      if (supported_curve == configured_curve) {
+        ok = true;
+        break;
+      }
+    }
+    if (ok) {
+      new_configured_curves[idx++] = configured_curve;
+    }
+  }
+  if (idx == 0) {
+    return false;
+  }
+  new_configured_curves.Shrink(idx);
+  ssl->config->supported_group_list = std::move(new_configured_curves);
+
+  return true;
 }
 
 bool SSL_apply_handoff(SSL *ssl, Span<const uint8_t> handoff) {
diff --git a/ssl/internal.h b/ssl/internal.h
index 2e05ee0..9d78b1f 100644
--- a/ssl/internal.h
+++ b/ssl/internal.h
@@ -1000,6 +1000,15 @@
   virtual bool Deserialize(CBS *in) { return false; }
 };
 
+struct NamedGroup {
+  int nid;
+  uint16_t group_id;
+  const char name[8], alias[11];
+};
+
+// NamedGroups returns all supported groups.
+Span<const NamedGroup> NamedGroups();
+
 // ssl_nid_to_group_id looks up the group corresponding to |nid|. On success, it
 // sets |*out_group_id| to the group ID and returns true. Otherwise, it returns
 // false.
diff --git a/ssl/ssl_key_share.cc b/ssl/ssl_key_share.cc
index 8466eab..80b7d0a 100644
--- a/ssl/ssl_key_share.cc
+++ b/ssl/ssl_key_share.cc
@@ -211,11 +211,7 @@
   uint8_t private_key_[32];
 };
 
-CONSTEXPR_ARRAY struct {
-  int nid;
-  uint16_t group_id;
-  const char name[8], alias[11];
-} kNamedGroups[] = {
+CONSTEXPR_ARRAY NamedGroup kNamedGroups[] = {
     {NID_secp224r1, SSL_CURVE_SECP224R1, "P-224", "secp224r1"},
     {NID_X9_62_prime256v1, SSL_CURVE_SECP256R1, "P-256", "prime256v1"},
     {NID_secp384r1, SSL_CURVE_SECP384R1, "P-384", "secp384r1"},
@@ -225,6 +221,10 @@
 
 }  // namespace
 
+Span<const NamedGroup> NamedGroups() {
+  return MakeConstSpan(kNamedGroups, OPENSSL_ARRAY_SIZE(kNamedGroups));
+}
+
 UniquePtr<SSLKeyShare> SSLKeyShare::Create(uint16_t group_id) {
   switch (group_id) {
     case SSL_CURVE_SECP224R1:
diff --git a/ssl/ssl_test.cc b/ssl/ssl_test.cc
index f7b299a..14b85d4 100644
--- a/ssl/ssl_test.cc
+++ b/ssl/ssl_test.cc
@@ -4285,20 +4285,29 @@
   // handoff is a handoff message that has been artificially modified to pretend
   // that only cipher 0x0A is supported.  When it is applied to |server|, all
   // ciphers but that one should be removed.
+  //
+  // To make a new one of these, try sticking this in the |Handoff| test above:
+  //
+  // hexdump(stderr, "", handoff.data(), handoff.size());
+  // sed -e 's/\(..\)/0x\1, /g'
+  //
+  // and modify serialize_features() to emit only cipher 0x0A.
+
   uint8_t handoff[] = {
-      0x30, 0x81, 0x8e, 0x02, 0x01, 0x00, 0x04, 0x00, 0x04, 0x81, 0x82, 0x01,
-      0x00, 0x00, 0x7e, 0x03, 0x03, 0x77, 0x62, 0x00, 0x9a, 0x13, 0x48, 0x23,
-      0x46, 0x11, 0x6c, 0x0b, 0x1c, 0x91, 0x4e, 0xbc, 0x1c, 0xff, 0x54, 0xb9,
-      0xe6, 0x3f, 0xa8, 0x8d, 0x49, 0x37, 0x7a, 0x9e, 0xbf, 0x36, 0xd5, 0x08,
-      0x24, 0x00, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+      0x30, 0x81, 0x9a, 0x02, 0x01, 0x00, 0x04, 0x00, 0x04, 0x81, 0x82, 0x01,
+      0x00, 0x00, 0x7e, 0x03, 0x03, 0x30, 0x8e, 0x8f, 0x79, 0xd2, 0x87, 0x39,
+      0xc2, 0x23, 0x23, 0x13, 0xca, 0x3c, 0x80, 0x44, 0xfd, 0x80, 0x83, 0x62,
+      0x3c, 0xcc, 0xf8, 0x76, 0xd3, 0x62, 0xbb, 0x54, 0xe3, 0xc4, 0x39, 0x24,
+      0xa5, 0x00, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
       0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14,
       0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
-      0x00, 0x37, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00,
-      0x23, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08,
-      0x04, 0x04, 0x01, 0x05, 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06,
-      0x01, 0x02, 0x01, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x0a, 0x00,
-      0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x04, 0x02, 0x00,
-      0x0a,
+      0x00, 0x37, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00,
+      0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+      0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
+      0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08,
+      0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x04, 0x02, 0x00,
+      0x0a, 0x04, 0x0a, 0x00, 0x15, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, 0x00,
+      0x1d,
   };
 
   EXPECT_EQ(20u, sk_SSL_CIPHER_num(SSL_get_ciphers(server.get())));
@@ -4307,6 +4316,43 @@
   EXPECT_EQ(1u, sk_SSL_CIPHER_num(SSL_get_ciphers(server.get())));
 }
 
+TEST(SSLTest, ApplyHandoffRemovesUnsupportedCurves) {
+  bssl::UniquePtr<SSL_CTX> server_ctx(SSL_CTX_new(TLS_method()));
+  bssl::UniquePtr<SSL> server(SSL_new(server_ctx.get()));
+
+  // handoff is a handoff message that has been artificially modified to pretend
+  // that only one curve is supported.  When it is applied to |server|, all
+  // curves but that one should be removed.
+  //
+  // See |ApplyHandoffRemovesUnsupportedCiphers| for how to make a new one of
+  // these.
+  uint8_t handoff[] = {
+      0x30, 0x81, 0xc0, 0x02, 0x01, 0x00, 0x04, 0x00, 0x04, 0x81, 0x82, 0x01,
+      0x00, 0x00, 0x7e, 0x03, 0x03, 0x98, 0x30, 0xce, 0xd9, 0xb0, 0xdf, 0x5f,
+      0x82, 0x05, 0x4a, 0x43, 0x67, 0x7e, 0xdb, 0x6a, 0x4f, 0x21, 0x18, 0x4e,
+      0x0d, 0x94, 0x63, 0x18, 0x8b, 0x54, 0x89, 0xdb, 0x8b, 0x1d, 0x84, 0xbc,
+      0x09, 0x00, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30,
+      0xcc, 0xa9, 0xcc, 0xa8, 0xc0, 0x09, 0xc0, 0x13, 0xc0, 0x0a, 0xc0, 0x14,
+      0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00,
+      0x00, 0x37, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00,
+      0x0a, 0x00, 0x08, 0x00, 0x06, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00,
+      0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, 0x0d, 0x00,
+      0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, 0x03, 0x08,
+      0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, 0x04, 0x30, 0x00,
+      0x02, 0x00, 0x0a, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x8c, 0x00, 0x8d, 0x00,
+      0x9c, 0x00, 0x9d, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, 0xc0, 0x09, 0xc0,
+      0x0a, 0xc0, 0x13, 0xc0, 0x14, 0xc0, 0x2b, 0xc0, 0x2c, 0xc0, 0x2f, 0xc0,
+      0x30, 0xc0, 0x35, 0xc0, 0x36, 0xcc, 0xa8, 0xcc, 0xa9, 0xcc, 0xac, 0x04,
+      0x02, 0x00, 0x17,
+  };
+
+  // The zero length means that the default list of groups is used.
+  EXPECT_EQ(0u, server->config->supported_group_list.size());
+  ASSERT_TRUE(
+      SSL_apply_handoff(server.get(), {handoff, OPENSSL_ARRAY_SIZE(handoff)}));
+  EXPECT_EQ(1u, server->config->supported_group_list.size());
+}
+
 TEST_P(SSLVersionTest, VerifyBeforeCertRequest) {
   // Configure the server to request client certificates.
   SSL_CTX_set_custom_verify(