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;