summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gtests/ssl_gtest/manifest.mn1
-rw-r--r--gtests/ssl_gtest/ssl_exporter_unittest.cc122
-rw-r--r--gtests/ssl_gtest/ssl_gtest.gyp1
-rw-r--r--lib/ssl/ssl.def6
-rw-r--r--lib/ssl/ssl.h10
-rw-r--r--lib/ssl/ssl3con.c4
-rw-r--r--lib/ssl/sslimpl.h8
-rw-r--r--lib/ssl/sslinfo.c59
-rw-r--r--lib/ssl/tls13con.c15
-rw-r--r--lib/ssl/tls13hkdf.c6
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);
}