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/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);