diff options
-rw-r--r-- | gtests/ssl_gtest/manifest.mn | 1 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_exporter_unittest.cc | 122 | ||||
-rw-r--r-- | gtests/ssl_gtest/ssl_gtest.gyp | 1 | ||||
-rw-r--r-- | lib/ssl/ssl.def | 6 | ||||
-rw-r--r-- | lib/ssl/ssl.h | 10 | ||||
-rw-r--r-- | lib/ssl/ssl3con.c | 4 | ||||
-rw-r--r-- | lib/ssl/sslimpl.h | 8 | ||||
-rw-r--r-- | lib/ssl/sslinfo.c | 59 | ||||
-rw-r--r-- | lib/ssl/tls13con.c | 15 | ||||
-rw-r--r-- | lib/ssl/tls13hkdf.c | 6 |
10 files changed, 226 insertions, 6 deletions
diff --git a/gtests/ssl_gtest/manifest.mn b/gtests/ssl_gtest/manifest.mn index fc50dc5c5..391db813b 100644 --- a/gtests/ssl_gtest/manifest.mn +++ b/gtests/ssl_gtest/manifest.mn @@ -22,6 +22,7 @@ CPPSRCS = \ ssl_drop_unittest.cc \ ssl_ecdh_unittest.cc \ ssl_ems_unittest.cc \ + ssl_exporter_unittest.cc \ ssl_extension_unittest.cc \ ssl_fuzz_unittest.cc \ ssl_gtest.cc \ diff --git a/gtests/ssl_gtest/ssl_exporter_unittest.cc b/gtests/ssl_gtest/ssl_exporter_unittest.cc new file mode 100644 index 000000000..0a0d9f25f --- /dev/null +++ b/gtests/ssl_gtest/ssl_exporter_unittest.cc @@ -0,0 +1,122 @@ +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set ts=2 et sw=2 tw=80: */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this file, + * You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "ssl.h" + +#include "gtest_utils.h" +#include "tls_connect.h" + +namespace nss_test { + +static const char* kExporterLabel = "EXPORTER-duck"; +static const uint8_t kExporterContext[] = {0x12, 0x34, 0x56}; + +static void ExportAndCompare(TlsAgent* client, TlsAgent* server, bool context) { + static const size_t exporter_len = 10; + uint8_t client_value[exporter_len] = {0}; + EXPECT_EQ(SECSuccess, + SSL_ExportKeyingMaterial( + client->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + context ? PR_TRUE : PR_FALSE, kExporterContext, + sizeof(kExporterContext), client_value, sizeof(client_value))); + uint8_t server_value[exporter_len] = {0xff}; + EXPECT_EQ(SECSuccess, + SSL_ExportKeyingMaterial( + server->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + context ? PR_TRUE : PR_FALSE, kExporterContext, + sizeof(kExporterContext), server_value, sizeof(server_value))); + EXPECT_EQ(0, memcmp(client_value, server_value, sizeof(client_value))); +} + +TEST_P(TlsConnectGeneric, ExporterBasic) { + EnsureTlsSetup(); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + } else { + server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + } + Connect(); + CheckKeys(); + ExportAndCompare(client_, server_, false); +} + +TEST_P(TlsConnectGeneric, ExporterContext) { + EnsureTlsSetup(); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + } else { + server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + } + Connect(); + CheckKeys(); + ExportAndCompare(client_, server_, true); +} + +// Bug 1312976 - SHA-384 doesn't work in 1.2 right now. +TEST_P(TlsConnectTls13, ExporterSha384) { + EnsureTlsSetup(); + client_->EnableSingleCipher(TLS_AES_256_GCM_SHA384); + Connect(); + CheckKeys(); + ExportAndCompare(client_, server_, false); +} + +TEST_P(TlsConnectTls13, ExporterContextEmptyIsSameAsNone) { + EnsureTlsSetup(); + if (version_ >= SSL_LIBRARY_VERSION_TLS_1_3) { + server_->EnableSingleCipher(TLS_AES_128_GCM_SHA256); + } else { + server_->EnableSingleCipher(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA); + } + Connect(); + CheckKeys(); + ExportAndCompare(client_, server_, false); +} + +// This has a weird signature so that it can be passed to the SNI callback. +int32_t RegularExporterShouldFail(TlsAgent* agent, const SECItem* srvNameArr, + PRUint32 srvNameArrSize) { + uint8_t val[10]; + EXPECT_EQ(SECFailure, SSL_ExportKeyingMaterial( + agent->ssl_fd(), kExporterLabel, + strlen(kExporterLabel), PR_TRUE, kExporterContext, + sizeof(kExporterContext), val, sizeof(val))) + << "regular exporter should fail"; + return 0; +} + +TEST_P(TlsConnectTls13, EarlyExporter) { + SetupForZeroRtt(); + client_->Set0RttEnabled(true); + server_->Set0RttEnabled(true); + ExpectResumption(RESUME_TICKET); + + client_->Handshake(); // Send ClientHello. + uint8_t client_value[10] = {0}; + RegularExporterShouldFail(client_, nullptr, 0); + EXPECT_EQ(SECSuccess, + SSL_ExportEarlyKeyingMaterial( + client_->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + kExporterContext, sizeof(kExporterContext), client_value, + sizeof(client_value))); + + server_->SetSniCallback(RegularExporterShouldFail); + server_->Handshake(); // Handle ClientHello. + uint8_t server_value[10] = {0}; + EXPECT_EQ(SECSuccess, + SSL_ExportEarlyKeyingMaterial( + server_->ssl_fd(), kExporterLabel, strlen(kExporterLabel), + kExporterContext, sizeof(kExporterContext), server_value, + sizeof(server_value))); + EXPECT_EQ(0, memcmp(client_value, server_value, sizeof(client_value))); + + Handshake(); + ExpectEarlyDataAccepted(true); + CheckConnected(); + SendReceive(); +} + +} // namespace nss_test diff --git a/gtests/ssl_gtest/ssl_gtest.gyp b/gtests/ssl_gtest/ssl_gtest.gyp index 08224658a..e232a8b7e 100644 --- a/gtests/ssl_gtest/ssl_gtest.gyp +++ b/gtests/ssl_gtest/ssl_gtest.gyp @@ -22,6 +22,7 @@ 'ssl_drop_unittest.cc', 'ssl_ecdh_unittest.cc', 'ssl_ems_unittest.cc', + 'ssl_exporter_unittest.cc', 'ssl_extension_unittest.cc', 'ssl_fuzz_unittest.cc', 'ssl_gtest.cc', diff --git a/lib/ssl/ssl.def b/lib/ssl/ssl.def index 8c77e1033..5fb92cf0e 100644 --- a/lib/ssl/ssl.def +++ b/lib/ssl/ssl.def @@ -220,3 +220,9 @@ SSL_SignatureSchemePrefGet; ;+ local: ;+*; ;+}; +;+NSS_3.29 { # NSS 3.29 release +;+ global: +SSL_ExportEarlyKeyingMaterial; +;+ local: +;+*; +;+}; diff --git a/lib/ssl/ssl.h b/lib/ssl/ssl.h index 49fff1a2c..b4af0e1f2 100644 --- a/lib/ssl/ssl.h +++ b/lib/ssl/ssl.h @@ -1216,6 +1216,16 @@ SSL_IMPORT SECStatus SSL_ExportKeyingMaterial(PRFileDesc *fd, unsigned char *out, unsigned int outLen); +/* Early exporters are used if 0-RTT is enabled. This is TLS 1.3 only. Note + * that in TLS 1.3, an empty context is equivalent to an absent context. */ +SSL_IMPORT SECStatus SSL_ExportEarlyKeyingMaterial(PRFileDesc *fd, + const char *label, + unsigned int labelLen, + const unsigned char *context, + unsigned int contextLen, + unsigned char *out, + unsigned int outLen); + /* ** Return a new reference to the certificate that was most recently sent ** to the peer on this SSL/TLS connection, or NULL if none has been sent. diff --git a/lib/ssl/ssl3con.c b/lib/ssl/ssl3con.c index f0f4ba51e..8f99643b3 100644 --- a/lib/ssl/ssl3con.c +++ b/lib/ssl/ssl3con.c @@ -13229,6 +13229,10 @@ ssl3_DestroySSL3Info(sslSocket *ss) PK11_FreeSymKey(ss->ssl3.hs.clientTrafficSecret); if (ss->ssl3.hs.serverTrafficSecret) PK11_FreeSymKey(ss->ssl3.hs.serverTrafficSecret); + if (ss->ssl3.hs.earlyExporterSecret) + PK11_FreeSymKey(ss->ssl3.hs.earlyExporterSecret); + if (ss->ssl3.hs.exporterSecret) + PK11_FreeSymKey(ss->ssl3.hs.exporterSecret); ss->ssl3.hs.zeroRttState = ssl_0rtt_none; /* Destroy TLS 1.3 buffered early data. */ diff --git a/lib/ssl/sslimpl.h b/lib/ssl/sslimpl.h index 8b7077c31..883b7d763 100644 --- a/lib/ssl/sslimpl.h +++ b/lib/ssl/sslimpl.h @@ -841,8 +841,8 @@ typedef struct SSL3HandshakeStateStr { * used for backoff (in ms) */ PRUint32 rtRetries; /* The retry counter */ SECItem srvVirtName; /* for server: name that was negotiated - * with a client. For client - is - * always set to NULL.*/ + * with a client. For client - is + * always set to NULL.*/ /* This group of values is used for TLS 1.3 and above */ PK11SymKey *currentSecret; /* The secret down the "left hand side" @@ -855,10 +855,12 @@ typedef struct SSL3HandshakeStateStr { PK11SymKey *serverHsTrafficSecret; /* traffic keys. */ PK11SymKey *clientTrafficSecret; /* The source keys for application */ PK11SymKey *serverTrafficSecret; /* traffic keys */ + PK11SymKey *earlyExporterSecret; /* for 0-RTT exporters */ + PK11SymKey *exporterSecret; /* for exporters */ /* The certificate request from the server. */ TLS13CertificateRequest *certificateRequest; PRCList cipherSpecs; /* The cipher specs in the sequence they - * will be applied. */ + * will be applied. */ ssl3CipherSpec *nullSpec; /* In case 0-RTT is rejected. */ sslZeroRttState zeroRttState; /* Are we doing a 0-RTT handshake? */ sslZeroRttIgnore zeroRttIgnore; /* Are we ignoring 0-RTT? */ diff --git a/lib/ssl/sslinfo.c b/lib/ssl/sslinfo.c index f3033d8e9..665109d65 100644 --- a/lib/ssl/sslinfo.c +++ b/lib/ssl/sslinfo.c @@ -1,9 +1,11 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ /* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "ssl.h" #include "sslimpl.h" #include "sslproto.h" +#include "tls13hkdf.h" static const char * ssl_GetCompressionMethodName(SSLCompressionMethod compression) @@ -373,6 +375,24 @@ SSL_GetNegotiatedHostInfo(PRFileDesc *fd) return sniName; } +static SECStatus +tls13_Exporter(sslSocket *ss, PK11SymKey *secret, + const char *label, unsigned int labelLen, + const unsigned char *context, unsigned int contextLen, + unsigned char *out, unsigned int outLen) +{ + if (!secret) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + return tls13_HkdfExpandLabelRaw(secret, + tls13_GetHash(ss), + context, contextLen, + label, labelLen, + out, outLen); +} + SECStatus SSL_ExportKeyingMaterial(PRFileDesc *fd, const char *label, unsigned int labelLen, @@ -392,11 +412,19 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd, return SECFailure; } - if (ss->version < SSL_LIBRARY_VERSION_3_1_TLS) { - PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION); + if (!label || !labelLen || !out || !outLen || + (hasContext && (!context || !contextLen))) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); return SECFailure; } + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + return tls13_Exporter(ss, ss->ssl3.hs.exporterSecret, + label, labelLen, + context, hasContext ? contextLen : 0, + out, outLen); + } + /* construct PRF arguments */ valLen = SSL3_RANDOM_LENGTH * 2; if (hasContext) { @@ -436,3 +464,30 @@ SSL_ExportKeyingMaterial(PRFileDesc *fd, PORT_ZFree(val, valLen); return rv; } + +SECStatus +SSL_ExportEarlyKeyingMaterial(PRFileDesc *fd, + const char *label, unsigned int labelLen, + const unsigned char *context, + unsigned int contextLen, + unsigned char *out, unsigned int outLen) +{ + sslSocket *ss; + + ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_ExportEarlyKeyingMaterial", + SSL_GETPID(), fd)); + return SECFailure; + } + + if (!label || !labelLen || !out || !outLen || + (!context && contextLen)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + return tls13_Exporter(ss, ss->ssl3.hs.earlyExporterSecret, + label, labelLen, context, contextLen, + out, outLen); +} diff --git a/lib/ssl/tls13con.c b/lib/ssl/tls13con.c index 3e5d8fda2..62e5d2afd 100644 --- a/lib/ssl/tls13con.c +++ b/lib/ssl/tls13con.c @@ -111,6 +111,7 @@ const char kHkdfLabelClient[] = "client"; const char kHkdfLabelServer[] = "server"; const char kHkdfLabelPskBinderKey[] = "resumption psk binder key"; const char kHkdfLabelEarlyTrafficSecret[] = "early traffic secret"; +const char kHkdfLabelEarlyExporterSecret[] = "early exporter master secret"; const char kHkdfLabelHandshakeTrafficSecret[] = "handshake traffic secret"; const char kHkdfLabelApplicationTrafficSecret[] = "application traffic secret"; const char kHkdfLabelFinishedSecret[] = "finished"; @@ -754,6 +755,13 @@ tls13_ComputeEarlySecrets(sslSocket *ss) if (rv != SECSuccess) { return SECFailure; } + + rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + NULL, kHkdfLabelEarlyExporterSecret, + &hashes, &ss->ssl3.hs.earlyExporterSecret); + if (rv != SECSuccess) { + return SECFailure; + } } else { PORT_Assert(!ss->ssl3.hs.resumptionMasterSecret); } @@ -844,6 +852,13 @@ tls13_ComputeApplicationSecrets(sslSocket *ss) return SECFailure; } + rv = tls13_DeriveSecret(ss, ss->ssl3.hs.currentSecret, + NULL, kHkdfLabelExporterMasterSecret, + NULL, &ss->ssl3.hs.exporterSecret); + if (rv != SECSuccess) { + return SECFailure; + } + return SECSuccess; } diff --git a/lib/ssl/tls13hkdf.c b/lib/ssl/tls13hkdf.c index 97c98a43e..7e69bb882 100644 --- a/lib/ssl/tls13hkdf.c +++ b/lib/ssl/tls13hkdf.c @@ -141,7 +141,11 @@ tls13_HkdfExpandLabel(PK11SymKey *prk, SSLHashType baseHash, const unsigned int kLabelPrefixLen = strlen(kLabelPrefix); if (handshakeHash) { - PORT_Assert(handshakeHashLen == kTlsHkdfInfo[baseHash].hashSize); + if (handshakeHashLen > 255) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } } else { PORT_Assert(!handshakeHashLen); } |