Add server-side support for Signed Certificate Timestamps.

Change-Id: Ifa44fef160fc9d67771eed165f8fc277f28a0222
Reviewed-on: https://boringssl-review.googlesource.com/5840
Reviewed-by: David Benjamin <davidben@chromium.org>
Reviewed-by: Adam Langley <agl@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h
index 4e2a64d..7109214 100644
--- a/include/openssl/ssl.h
+++ b/include/openssl/ssl.h
@@ -624,6 +624,18 @@
 OPENSSL_EXPORT int SSL_get0_chain_certs(const SSL *ssl,
                                         STACK_OF(X509) **out_chain);
 
+/* SSL_CTX_set_signed_cert_timestamp_list sets the list of signed certificate
+ * timestamps that is sent to clients that request it. The |list| argument must
+ * contain one or more SCT structures serialised as a SignedCertificateTimestamp
+ * List (see https://tools.ietf.org/html/rfc6962#section-3.3) – i.e. each SCT
+ * is prefixed by a big-endian, uint16 length and the concatenation of one or
+ * more such prefixed SCTs are themselves also prefixed by a uint16 length. It
+ * returns one on success and zero on error. The caller retains ownership of
+ * |list|. */
+OPENSSL_EXPORT int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx,
+                                                          const uint8_t *list,
+                                                          size_t list_len);
+
 /* SSL_CTX_set_ocsp_response sets the OCSP reponse that is sent to clients
  * which request it. It returns one on success and zero on error. The caller
  * retains ownership of |response|. */
@@ -1472,6 +1484,10 @@
   /* If true, a client will request certificate timestamps. */
   char signed_cert_timestamps_enabled;
 
+  /* Signed certificate timestamp list to be sent to the client, if requested */
+  uint8_t *signed_cert_timestamp_list;
+  size_t signed_cert_timestamp_list_length;
+
   /* If true, a client will request a stapled OCSP response. */
   char ocsp_stapling_enabled;
 
diff --git a/ssl/ssl_lib.c b/ssl/ssl_lib.c
index 373dffb..574ae7b 100644
--- a/ssl/ssl_lib.c
+++ b/ssl/ssl_lib.c
@@ -1385,6 +1385,20 @@
   *out_len = session->ocsp_response_length;
 }
 
+int SSL_CTX_set_signed_cert_timestamp_list(SSL_CTX *ctx, const uint8_t *list,
+                                           size_t list_len) {
+  OPENSSL_free(ctx->signed_cert_timestamp_list);
+  ctx->signed_cert_timestamp_list_length = 0;
+
+  ctx->signed_cert_timestamp_list = BUF_memdup(list, list_len);
+  if (ctx->signed_cert_timestamp_list == NULL) {
+    return 0;
+  }
+  ctx->signed_cert_timestamp_list_length = list_len;
+
+  return 1;
+}
+
 int SSL_CTX_set_ocsp_response(SSL_CTX *ctx, const uint8_t *response,
                               size_t response_len) {
   OPENSSL_free(ctx->ocsp_response);
@@ -1739,6 +1753,7 @@
   OPENSSL_free(ctx->tlsext_ellipticcurvelist);
   OPENSSL_free(ctx->alpn_client_proto_list);
   OPENSSL_free(ctx->ocsp_response);
+  OPENSSL_free(ctx->signed_cert_timestamp_list);
   EVP_PKEY_free(ctx->tlsext_channel_id_private);
   BIO_free(ctx->keylog_bio);
 
diff --git a/ssl/t1_lib.c b/ssl/t1_lib.c
index 40b9752..36793f7 100644
--- a/ssl/t1_lib.c
+++ b/ssl/t1_lib.c
@@ -1545,13 +1545,20 @@
 
 static int ext_sct_parse_clienthello(SSL *ssl, uint8_t *out_alert,
                                      CBS *contents) {
-  /* The SCT extension is not supported as a server. */
-  return 1;
+  return contents == NULL || CBS_len(contents) == 0;
 }
 
 static int ext_sct_add_serverhello(SSL *ssl, CBB *out) {
-  /* The SCT extension is not supported as a server. */
-  return 1;
+  if (ssl->ctx->signed_cert_timestamp_list_length == 0) {
+    return 1;
+  }
+
+  CBB contents;
+  return CBB_add_u16(out, TLSEXT_TYPE_certificate_timestamp) &&
+         CBB_add_u16_length_prefixed(out, &contents) &&
+         CBB_add_bytes(&contents, ssl->ctx->signed_cert_timestamp_list,
+                       ssl->ctx->signed_cert_timestamp_list_length) &&
+         CBB_flush(out);
 }
 
 
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc
index 9b7fcd6..c30eb55 100644
--- a/ssl/test/bssl_shim.cc
+++ b/ssl/test/bssl_shim.cc
@@ -700,6 +700,13 @@
     SSL_CTX_set_cert_verify_callback(ssl_ctx.get(), VerifySucceed, NULL);
   }
 
+  if (!config->signed_cert_timestamps.empty() &&
+      !SSL_CTX_set_signed_cert_timestamp_list(
+          ssl_ctx.get(), (const uint8_t *)config->signed_cert_timestamps.data(),
+          config->signed_cert_timestamps.size())) {
+    return nullptr;
+  }
+
   return ssl_ctx;
 }
 
diff --git a/ssl/test/runner/common.go b/ssl/test/runner/common.go
index 273a75c..0b8bfe5 100644
--- a/ssl/test/runner/common.go
+++ b/ssl/test/runner/common.go
@@ -190,6 +190,7 @@
 	ChannelID                  *ecdsa.PublicKey      // the channel ID for this connection
 	SRTPProtectionProfile      uint16                // the negotiated DTLS-SRTP protection profile
 	TLSUnique                  []byte
+	SCTList                    []byte                // signed certificate timestamp list
 }
 
 // ClientAuthType declares the policy the server will follow for
diff --git a/ssl/test/runner/conn.go b/ssl/test/runner/conn.go
index 1b8700c..b23a104 100644
--- a/ssl/test/runner/conn.go
+++ b/ssl/test/runner/conn.go
@@ -40,6 +40,7 @@
 	extendedMasterSecret bool // whether this session used an extended master secret
 	cipherSuite          *cipherSuite
 	ocspResponse         []byte // stapled OCSP response
+	sctList              []byte // signed certificate timestamp list
 	peerCertificates     []*x509.Certificate
 	// verifiedChains contains the certificate chains that we built, as
 	// opposed to the ones presented by the server.
@@ -1343,6 +1344,7 @@
 		state.ChannelID = c.channelID
 		state.SRTPProtectionProfile = c.srtpProtectionProfile
 		state.TLSUnique = c.firstFinished[:]
+		state.SCTList = c.sctList
 	}
 
 	return state
diff --git a/ssl/test/runner/handshake_client.go b/ssl/test/runner/handshake_client.go
index c38334e..b96ced7 100644
--- a/ssl/test/runner/handshake_client.go
+++ b/ssl/test/runner/handshake_client.go
@@ -61,6 +61,7 @@
 		compressionMethods:      []uint8{compressionNone},
 		random:                  make([]byte, 32),
 		ocspStapling:            true,
+		sctListSupported:        true,
 		serverName:              c.config.ServerName,
 		supportedCurves:         c.config.curvePreferences(),
 		supportedPoints:         []uint8{pointFormatUncompressed},
@@ -371,6 +372,11 @@
 	copy(c.clientRandom[:], hs.hello.random)
 	copy(c.serverRandom[:], hs.serverHello.random)
 	copy(c.masterSecret[:], hs.masterSecret)
+
+	if len(hs.serverHello.sctList) > 0 {
+		c.sctList = hs.serverHello.sctList
+	}
+
 	return nil
 }
 
diff --git a/ssl/test/runner/handshake_messages.go b/ssl/test/runner/handshake_messages.go
index 7926533..da85e7a 100644
--- a/ssl/test/runner/handshake_messages.go
+++ b/ssl/test/runner/handshake_messages.go
@@ -989,14 +989,7 @@
 			}
 			m.srtpMasterKeyIdentifier = string(d[1:])
 		case extensionSignedCertificateTimestamp:
-			if length < 2 {
-				return false
-			}
-			l := int(data[0])<<8 | int(data[1])
-			if l != len(data)-2 {
-				return false
-			}
-			m.sctList = data[2:length]
+			m.sctList = data[:length]
 		case extensionCustom:
 			m.customExtension = string(data[:length])
 		}
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go
index 3c077bf..5259dae 100644
--- a/ssl/test/runner/runner.go
+++ b/ssl/test/runner/runner.go
@@ -157,6 +157,8 @@
 	expectedSRTPProtectionProfile uint16
 	// expectedOCSPResponse, if not nil, is the expected OCSP response to be received.
 	expectedOCSPResponse []uint8
+	// expectedSCTList, if not nil, is the expected SCT list to be received.
+	expectedSCTList []uint8
 	// messageLen is the length, in bytes, of the test message that will be
 	// sent.
 	messageLen int
@@ -334,6 +336,10 @@
 		return fmt.Errorf("OCSP Response mismatch")
 	}
 
+	if test.expectedSCTList != nil && !bytes.Equal(test.expectedSCTList, connState.SCTList) {
+		return fmt.Errorf("SCT list mismatch")
+	}
+
 	if test.exportKeyingMaterial > 0 {
 		actual := make([]byte, test.exportKeyingMaterial)
 		if _, err := io.ReadFull(tlsConn, actual); err != nil {
@@ -3428,7 +3434,8 @@
 	})
 	// Test SCT list.
 	testCases = append(testCases, testCase{
-		name: "SignedCertificateTimestampList",
+		name: "SignedCertificateTimestampList-Client",
+		testType: clientTest,
 		flags: []string{
 			"-enable-signed-cert-timestamps",
 			"-expect-signed-cert-timestamps",
@@ -3436,6 +3443,15 @@
 		},
 	})
 	testCases = append(testCases, testCase{
+		name: "SignedCertificateTimestampList-Server",
+		testType: serverTest,
+		flags: []string{
+			"-signed-cert-timestamps",
+			base64.StdEncoding.EncodeToString(testSCTList),
+		},
+		expectedSCTList: testSCTList,
+	})
+	testCases = append(testCases, testCase{
 		testType: clientTest,
 		name:     "ClientHelloPadding",
 		config: Config{
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc
index ad14ed3..fe6dc93 100644
--- a/ssl/test/test_config.cc
+++ b/ssl/test/test_config.cc
@@ -128,6 +128,7 @@
   { "-expect-signed-cert-timestamps",
     &TestConfig::expected_signed_cert_timestamps },
   { "-ocsp-response", &TestConfig::ocsp_response },
+  { "-signed-cert-timestamps", &TestConfig::signed_cert_timestamps },
 };
 
 const Flag<int> kIntFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h
index 5776095..76ee312 100644
--- a/ssl/test/test_config.h
+++ b/ssl/test/test_config.h
@@ -95,6 +95,7 @@
   bool verify_fail = false;
   bool verify_peer = false;
   bool expect_verify_result = false;
+  std::string signed_cert_timestamps;
 };
 
 bool ParseConfig(int argc, char **argv, TestConfig *out_config);