Add |SSL_export_traffic_secrets|. This allows an application to obtain the current TLS 1.3 traffic secrets for a connection. Change-Id: I8ad8d0559caba266f74081441dea54b22da3db20 Reviewed-on: https://boringssl-review.googlesource.com/c/33590 Commit-Queue: Adam Langley <agl@google.com> Reviewed-by: David Benjamin <davidben@google.com>
diff --git a/include/openssl/ssl.h b/include/openssl/ssl.h index 2f8163a..6898674 100644 --- a/include/openssl/ssl.h +++ b/include/openssl/ssl.h
@@ -4716,6 +4716,14 @@ OPENSSL_EXPORT bool SSL_serialize_handback(const SSL *ssl, CBB *out); OPENSSL_EXPORT bool SSL_apply_handback(SSL *ssl, Span<const uint8_t> handback); +// SSL_get_traffic_secrets sets |*out_read_traffic_secret| and +// |*out_write_traffic_secret| to reference the TLS 1.3 traffic secrets for +// |ssl|. This function is only valid on TLS 1.3 connections that have +// completed the handshake. It returns true on success and false on error. +OPENSSL_EXPORT bool SSL_get_traffic_secrets( + const SSL *ssl, Span<const uint8_t> *out_read_traffic_secret, + Span<const uint8_t> *out_write_traffic_secret); + BSSL_NAMESPACE_END } // extern C++
diff --git a/ssl/ssl_lib.cc b/ssl/ssl_lib.cc index b9c823d..ceeba89 100644 --- a/ssl/ssl_lib.cc +++ b/ssl/ssl_lib.cc
@@ -506,6 +506,27 @@ ssl->config->handoff = on; } +bool SSL_get_traffic_secrets(const SSL *ssl, + Span<const uint8_t> *out_read_traffic_secret, + Span<const uint8_t> *out_write_traffic_secret) { + if (SSL_version(ssl) < TLS1_3_VERSION) { + OPENSSL_PUT_ERROR(SSL, SSL_R_WRONG_SSL_VERSION); + return false; + } + + if (!ssl->s3->initial_handshake_complete) { + OPENSSL_PUT_ERROR(SSL, SSL_R_HANDSHAKE_NOT_COMPLETE); + return false; + } + + *out_read_traffic_secret = Span<const uint8_t>( + ssl->s3->read_traffic_secret, ssl->s3->read_traffic_secret_len); + *out_write_traffic_secret = Span<const uint8_t>( + ssl->s3->write_traffic_secret, ssl->s3->write_traffic_secret_len); + + return true; +} + BSSL_NAMESPACE_END using namespace bssl;
diff --git a/ssl/test/bssl_shim.cc b/ssl/test/bssl_shim.cc index 3632fc5..77ed796 100644 --- a/ssl/test/bssl_shim.cc +++ b/ssl/test/bssl_shim.cc
@@ -836,6 +836,23 @@ } } + if (config->export_traffic_secrets) { + bssl::Span<const uint8_t> read_secret, write_secret; + if (!SSL_get_traffic_secrets(ssl, &read_secret, &write_secret)) { + fprintf(stderr, "failed to export traffic secrets\n"); + return false; + } + + assert(read_secret.size() <= 0xffff); + assert(write_secret.size() == read_secret.size()); + const uint16_t secret_len = read_secret.size(); + if (WriteAll(ssl, &secret_len, sizeof(secret_len)) < 0 || + WriteAll(ssl, read_secret.data(), read_secret.size()) < 0 || + WriteAll(ssl, write_secret.data(), write_secret.size()) < 0) { + return false; + } + } + if (config->tls_unique) { uint8_t tls_unique[16]; size_t tls_unique_len;
diff --git a/ssl/test/runner/runner.go b/ssl/test/runner/runner.go index 477eae8..b5cc0a7 100644 --- a/ssl/test/runner/runner.go +++ b/ssl/test/runner/runner.go
@@ -22,6 +22,7 @@ "crypto/x509" "crypto/x509/pkix" "encoding/base64" + "encoding/binary" "encoding/hex" "encoding/json" "encoding/pem" @@ -490,6 +491,9 @@ // expectedQUICTransportParams contains the QUIC transport // parameters that are expected to be sent by the peer. expectedQUICTransportParams []byte + // exportTrafficSecrets, if true, configures the test to export the TLS 1.3 + // traffic secrets and confirms that they match. + exportTrafficSecrets bool } var testCases []testCase @@ -768,6 +772,32 @@ } } + if test.exportTrafficSecrets { + secretLenBytes := make([]byte, 2) + if _, err := io.ReadFull(tlsConn, secretLenBytes); err != nil { + return err + } + secretLen := binary.LittleEndian.Uint16(secretLenBytes) + + theirReadSecret := make([]byte, secretLen) + theirWriteSecret := make([]byte, secretLen) + if _, err := io.ReadFull(tlsConn, theirReadSecret); err != nil { + return err + } + if _, err := io.ReadFull(tlsConn, theirWriteSecret); err != nil { + return err + } + + myReadSecret := tlsConn.in.trafficSecret + myWriteSecret := tlsConn.out.trafficSecret + if !bytes.Equal(myWriteSecret, theirReadSecret) { + return fmt.Errorf("read traffic-secret mismatch; got %x, wanted %x", theirReadSecret, myWriteSecret) + } + if !bytes.Equal(myReadSecret, theirWriteSecret) { + return fmt.Errorf("write traffic-secret mismatch; got %x, wanted %x", theirWriteSecret, myReadSecret) + } + } + if test.testTLSUnique { var peersValue [12]byte if _, err := io.ReadFull(tlsConn, peersValue[:]); err != nil { @@ -1123,6 +1153,10 @@ flags = append(flags, "-export-context", test.exportContext) } + if test.exportTrafficSecrets { + flags = append(flags, "-export-traffic-secrets") + } + if test.expectResumeRejected { flags = append(flags, "-expect-session-miss") } @@ -10521,6 +10555,24 @@ }) } +func addExportTrafficSecretsTests() { + for _, cipherSuite := range []testCipherSuite{ + // Test a SHA-256 and SHA-384 based cipher suite. + {"AEAD-AES128-GCM-SHA256", TLS_AES_128_GCM_SHA256}, + {"AEAD-AES256-GCM-SHA384", TLS_AES_256_GCM_SHA384}, + } { + + testCases = append(testCases, testCase{ + name: "ExportTrafficSecrets-" + cipherSuite.name, + config: Config{ + MinVersion: VersionTLS13, + CipherSuites: []uint16{cipherSuite.id}, + }, + exportTrafficSecrets: true, + }) + } +} + func addTLSUniqueTests() { for _, isClient := range []bool{false, true} { for _, isResumption := range []bool{false, true} { @@ -15076,6 +15128,7 @@ addSignatureAlgorithmTests() addDTLSRetransmitTests() addExportKeyingMaterialTests() + addExportTrafficSecretsTests() addTLSUniqueTests() addCustomExtensionTests() addRSAClientKeyExchangeTests()
diff --git a/ssl/test/test_config.cc b/ssl/test/test_config.cc index 9a5c9b2..bed0501 100644 --- a/ssl/test/test_config.cc +++ b/ssl/test/test_config.cc
@@ -147,6 +147,7 @@ { "-reverify-on-resume", &TestConfig::reverify_on_resume }, { "-jdk11-workaround", &TestConfig::jdk11_workaround }, { "-server-preference", &TestConfig::server_preference }, + { "-export-traffic-secrets", &TestConfig::export_traffic_secrets }, }; const Flag<std::string> kStringFlags[] = {
diff --git a/ssl/test/test_config.h b/ssl/test/test_config.h index 0e842c0..0d0753e 100644 --- a/ssl/test/test_config.h +++ b/ssl/test/test_config.h
@@ -171,6 +171,7 @@ std::string handshaker_path; bool jdk11_workaround = false; bool server_preference = false; + bool export_traffic_secrets = false; int argc; char **argv;