Commit cfd5fcba authored by EKR's avatar EKR

Bug 1494901 - Implement ESNI. r=mt

Phabricator: https://phabricator.services.mozilla.com/D6042
parent d3a99b24
#! gmake
#
#
# 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/.
......
......@@ -28,6 +28,7 @@
#include "prio.h"
#include "prnetdb.h"
#include "nss.h"
#include "nssb64.h"
#include "ocsp.h"
#include "ssl.h"
#include "sslproto.h"
......@@ -224,7 +225,8 @@ PrintUsageHeader()
" [-V [min-version]:[max-version]] [-K] [-T] [-U]\n"
" [-r N] [-w passwd] [-W pwfile] [-q [-t seconds]]\n"
" [-I groups] [-J signatureschemes]\n"
" [-A requestfile] [-L totalconnections] [-P {client,server}] [-Q]\n"
" [-A requestfile] [-L totalconnections] [-P {client,server}]\n"
" [-N encryptedSniKeys] [-Q]\n"
"\n",
progName);
}
......@@ -308,6 +310,7 @@ PrintParameterUsage()
fprintf(stderr, "%-20s Enable alternative TLS 1.3 handshake\n", "-X alt-server-hello");
fprintf(stderr, "%-20s Use DTLS\n", "-P {client, server}");
fprintf(stderr, "%-20s Exit after handshake\n", "-Q");
fprintf(stderr, "%-20s Encrypted SNI Keys\n", "-N");
}
static void
......@@ -985,6 +988,7 @@ PRBool stopAfterHandshake = PR_FALSE;
PRBool requestToExit = PR_FALSE;
char *versionString = NULL;
PRBool handshakeComplete = PR_FALSE;
char *encryptedSNIKeys = NULL;
static int
writeBytesToServer(PRFileDesc *s, const PRUint8 *buf, int nb)
......@@ -1424,6 +1428,26 @@ run()
}
}
if (encryptedSNIKeys) {
SECItem esniKeysBin = { siBuffer, NULL, 0 };
if (!NSSBase64_DecodeBuffer(NULL, &esniKeysBin, encryptedSNIKeys,
strlen(encryptedSNIKeys))) {
SECU_PrintError(progName, "ESNIKeys record is invalid base64");
error = 1;
goto done;
}
rv = SSL_EnableESNI(s, esniKeysBin.data, esniKeysBin.len,
"dummy.invalid");
SECITEM_FreeItem(&esniKeysBin, PR_FALSE);
if (rv < 0) {
SECU_PrintError(progName, "SSL_EnableESNI failed");
error = 1;
goto done;
}
}
serverCertAuth.dbHandle = CERT_GetDefaultCertDB();
SSL_AuthCertificateHook(s, ownAuthCertificate, &serverCertAuth);
......@@ -1683,7 +1707,7 @@ main(int argc, char **argv)
* Please leave some time before reusing these.
*/
optstate = PL_CreateOptState(argc, argv,
"46A:CDFGHI:J:KL:M:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
"46A:CDFGHI:J:KL:M:N:OP:QR:STUV:W:X:YZa:bc:d:fgh:m:n:op:qr:st:uvw:");
while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
switch (optstate->option) {
case '?':
......@@ -1760,6 +1784,10 @@ main(int argc, char **argv)
};
break;
case 'N':
encryptedSNIKeys = PORT_Strdup(optstate->value);
break;
case 'P':
useDTLS = PR_TRUE;
if (!strcmp(optstate->value, "server")) {
......@@ -2108,6 +2136,7 @@ done:
PORT_Free(pwdata.data);
PORT_Free(host);
PORT_Free(zeroRttData);
PORT_Free(encryptedSNIKeys);
if (enabledGroups) {
PORT_Free(enabledGroups);
......
......@@ -52,6 +52,7 @@ CPPSRCS = \
tls_hkdf_unittest.cc \
tls_filter.cc \
tls_protect.cc \
tls_esni_unittest.cc \
$(NULL)
INCLUDES += -I$(CORE_DEPTH)/gtests/google_test/gtest/include \
......
......@@ -206,4 +206,4 @@ static const uint8_t rsa8193[] = {
0x13, 0x34, 0x9d, 0x34, 0xb8, 0xef, 0x13, 0x3a, 0x20, 0xf5, 0x74, 0x02,
0x70, 0x3b, 0x41, 0x60, 0x1f, 0x5e, 0x76, 0x0a, 0xb1, 0x17, 0xd5, 0xcf,
0x79, 0xef, 0xf7, 0xab, 0xe7, 0xd6, 0x0f, 0xad, 0x85, 0x2c, 0x52, 0x67,
0xb5, 0xa0, 0x4a, 0xfd, 0xaf};
\ No newline at end of file
0xb5, 0xa0, 0x4a, 0xfd, 0xaf};
......@@ -44,28 +44,6 @@ class TlsExtensionTruncator : public TlsExtensionFilter {
size_t length_;
};
class TlsExtensionDamager : public TlsExtensionFilter {
public:
TlsExtensionDamager(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
size_t index)
: TlsExtensionFilter(a), extension_(extension), index_(index) {}
virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
const DataBuffer& input,
DataBuffer* output) {
if (extension_type != extension_) {
return KEEP;
}
*output = input;
output->data()[index_] += 73; // Increment selected for maximum damage
return CHANGE;
}
private:
uint16_t extension_;
size_t index_;
};
class TlsExtensionAppender : public TlsHandshakeFilter {
public:
TlsExtensionAppender(const std::shared_ptr<TlsAgent>& a,
......@@ -611,7 +589,6 @@ TEST_F(TlsExtensionTest13Stream, WrongServerKeyShare) {
EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
}
// TODO(ekr@rtfm.com): This is the wrong error code. See bug 1307269.
TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) {
const uint16_t wrong_group = 0xffff;
......@@ -625,10 +602,10 @@ TEST_F(TlsExtensionTest13Stream, UnknownServerKeyShare) {
DataBuffer buf(key_share, sizeof(key_share));
EnsureTlsSetup();
MakeTlsFilter<TlsExtensionReplacer>(server_, ssl_tls13_key_share_xtn, buf);
client_->ExpectSendAlert(kTlsAlertMissingExtension);
client_->ExpectSendAlert(kTlsAlertIllegalParameter);
server_->ExpectSendAlert(kTlsAlertBadRecordMac);
ConnectExpectFail();
EXPECT_EQ(SSL_ERROR_MISSING_KEY_SHARE, client_->error_code());
EXPECT_EQ(SSL_ERROR_RX_MALFORMED_KEY_SHARE, client_->error_code());
EXPECT_EQ(SSL_ERROR_BAD_MAC_READ, server_->error_code());
}
......
......@@ -51,6 +51,7 @@
'tls_connect.cc',
'tls_filter.cc',
'tls_hkdf_unittest.cc',
'tls_esni_unittest.cc',
'tls_protect.cc'
],
'dependencies': [
......
This diff is collapsed.
......@@ -878,6 +878,17 @@ PacketFilter::Action TlsExtensionDropper::FilterExtension(
return KEEP;
}
PacketFilter::Action TlsExtensionDamager::FilterExtension(
uint16_t extension_type, const DataBuffer& input, DataBuffer* output) {
if (extension_type != extension_) {
return KEEP;
}
*output = input;
output->data()[index_] += 73; // Increment selected for maximum damage
return CHANGE;
}
PacketFilter::Action TlsExtensionInjector::FilterHandshake(
const HandshakeHeader& header, const DataBuffer& input,
DataBuffer* output) {
......
......@@ -465,6 +465,20 @@ class TlsExtensionInjector : public TlsHandshakeFilter {
const DataBuffer data_;
};
class TlsExtensionDamager : public TlsExtensionFilter {
public:
TlsExtensionDamager(const std::shared_ptr<TlsAgent>& a, uint16_t extension,
size_t index)
: TlsExtensionFilter(a), extension_(extension), index_(index) {}
virtual PacketFilter::Action FilterExtension(uint16_t extension_type,
const DataBuffer& input,
DataBuffer* output);
private:
uint16_t extension_;
size_t index_;
};
typedef std::function<void(void)> VoidFunction;
class AfterRecordN : public TlsRecordFilter {
......
......@@ -552,3 +552,12 @@ ER3(SSL_ERROR_RX_MALFORMED_DTLS_ACK, (SSL_ERROR_BASE + 174),
ER3(SSL_ERROR_DH_KEY_TOO_LONG, (SSL_ERROR_BASE + 175),
"SSL received a DH key share that's too long (>8192 bit).")
ER3(SSL_ERROR_RX_MALFORMED_ESNI_KEYS, (SSL_ERROR_BASE + 176),
"SSL received a malformed ESNI keys structure")
ER3(SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, (SSL_ERROR_BASE + 177),
"SSL received a malformed ESNI extension")
ER3(SSL_ERROR_MISSING_ESNI_EXTENSION, (SSL_ERROR_BASE + 178),
"SSL did not receive an ESNI extension")
......@@ -60,3 +60,7 @@ endif
ifdef NSS_DISABLE_TLS_1_3
DEFINES += -DNSS_DISABLE_TLS_1_3
endif
ifeq (,$(filter-out DragonFly FreeBSD Linux NetBSD OpenBSD, $(OS_TARGET)))
CFLAGS += -std=gnu99
endif
#
# 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/.
......@@ -56,6 +55,7 @@ CSRCS = \
tls13replay.c \
sslcert.c \
sslgrp.c \
tls13esni.c \
$(NULL)
LIBRARY_NAME = ssl
......
......@@ -43,6 +43,7 @@
'ssltrace.c',
'sslver.c',
'tls13con.c',
'tls13esni.c',
'tls13exthandle.c',
'tls13hashstate.c',
'tls13hkdf.c',
......@@ -67,6 +68,11 @@
'UNSAFE_FUZZER_MODE',
],
}],
[ 'OS=="dragonfly" or OS=="freebsd" or OS=="netbsd" or OS=="openbsd" or OS=="linux"', {
'cflags': [
'-std=gnu99',
],
}],
],
'dependencies': [
'<(DEPTH)/exports.gyp:nss_exports',
......
......@@ -656,7 +656,7 @@ ssl_LookupCipherSuiteCfgMutable(ssl3CipherSuite suite,
return NULL;
}
const static ssl3CipherSuiteCfg *
const ssl3CipherSuiteCfg *
ssl_LookupCipherSuiteCfg(ssl3CipherSuite suite, const ssl3CipherSuiteCfg *suites)
{
return ssl_LookupCipherSuiteCfgMutable(suite,
......@@ -854,9 +854,9 @@ ssl3_config_match_init(sslSocket *ss)
/* Return PR_TRUE if suite is usable. This if the suite is permitted by policy,
* enabled, has a certificate (as needed), has a viable key agreement method, is
* usable with the negotiated TLS version, and is otherwise usable. */
static PRBool
config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
const SSLVersionRange *vrange, const sslSocket *ss)
PRBool
ssl3_config_match(const ssl3CipherSuiteCfg *suite, PRUint8 policy,
const SSLVersionRange *vrange, const sslSocket *ss)
{
const ssl3CipherSuiteDef *cipher_def;
const ssl3KEADef *kea_def;
......@@ -899,7 +899,7 @@ count_cipher_suites(sslSocket *ss, PRUint8 policy)
return 0;
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
if (config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss))
if (ssl3_config_match(&ss->cipherSuites[i], policy, &ss->vrange, ss))
count++;
}
if (count == 0) {
......@@ -4798,7 +4798,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
suite = ssl_LookupCipherSuiteCfg(sid->u.ssl3.cipherSuite,
ss->cipherSuites);
PORT_Assert(suite);
if (!suite || !config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
if (!suite || !ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
sidOK = PR_FALSE;
}
......@@ -4946,6 +4946,14 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
PR_RWLock_Rlock(sid->u.ssl3.lock);
}
/* Generate a new random if this is the first attempt. */
if (type == client_hello_initial) {
rv = ssl3_GetNewRandom(ss->ssl3.hs.client_random);
if (rv != SECSuccess) {
goto loser; /* err set by GetNewRandom. */
}
}
if (ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3 &&
type == client_hello_initial) {
rv = tls13_SetupClientHello(ss);
......@@ -4953,6 +4961,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
goto loser;
}
}
if (isTLS || (ss->firstHsDone && ss->peerRequestedProtection)) {
rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_client_hello);
if (rv != SECSuccess) {
......@@ -5024,13 +5033,6 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
goto loser; /* err set by ssl3_AppendHandshake* */
}
/* Generate a new random if this is the first attempt. */
if (type == client_hello_initial) {
rv = ssl3_GetNewRandom(ss->ssl3.hs.client_random);
if (rv != SECSuccess) {
goto loser; /* err set by GetNewRandom. */
}
}
rv = ssl3_AppendHandshake(ss, ss->ssl3.hs.client_random,
SSL3_RANDOM_LENGTH);
if (rv != SECSuccess) {
......@@ -5085,7 +5087,7 @@ ssl3_SendClientHello(sslSocket *ss, sslClientHelloType type)
}
for (i = 0; i < ssl_V3_SUITES_IMPLEMENTED; i++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[i];
if (config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
if (ssl3_config_match(suite, ss->ssl3.policy, &ss->vrange, ss)) {
actual_count++;
if (actual_count > num_suites) {
/* set error card removal/insertion error */
......@@ -6326,7 +6328,7 @@ ssl_ClientSetCipherSuite(sslSocket *ss, SSL3ProtocolVersion version,
ssl3CipherSuiteCfg *suiteCfg = &ss->cipherSuites[i];
if (suite == suiteCfg->cipher_suite) {
SSLVersionRange vrange = { version, version };
if (!config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
/* config_match already checks whether the cipher suite is
* acceptable for the version, but the check is repeated here
* in order to give a more precise error code. */
......@@ -7858,6 +7860,30 @@ ssl3_KEASupportsTickets(const ssl3KEADef *kea_def)
return PR_TRUE;
}
SECStatus
ssl3_NegotiateCipherSuiteInner(sslSocket *ss, const SECItem *suites,
PRUint16 version, PRUint16 *suitep)
{
unsigned int j;
unsigned int i;
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
SSLVersionRange vrange = { version, version };
if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) {
continue;
}
for (i = 0; i + 1 < suites->len; i += 2) {
PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
if (suite_i == suite->cipher_suite) {
*suitep = suite_i;
return SECSuccess;
}
}
}
return SECFailure;
}
/* Select a cipher suite.
**
** NOTE: This suite selection algorithm should be the same as the one in
......@@ -7876,24 +7902,16 @@ SECStatus
ssl3_NegotiateCipherSuite(sslSocket *ss, const SECItem *suites,
PRBool initHashes)
{
unsigned int j;
unsigned int i;
PRUint16 selected;
SECStatus rv;
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
SSLVersionRange vrange = { ss->version, ss->version };
if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
continue;
}
for (i = 0; i + 1 < suites->len; i += 2) {
PRUint16 suite_i = (suites->data[i] << 8) | suites->data[i + 1];
if (suite_i == suite->cipher_suite) {
ss->ssl3.hs.cipher_suite = suite_i;
return ssl3_SetupCipherSuite(ss, initHashes);
}
}
rv = ssl3_NegotiateCipherSuiteInner(ss, suites, ss->version, &selected);
if (rv != SECSuccess) {
return SECFailure;
}
return SECFailure;
ss->ssl3.hs.cipher_suite = selected;
return ssl3_SetupCipherSuite(ss, initHashes);
}
/*
......@@ -8025,9 +8043,12 @@ ssl3_ServerCallSNICallback(sslSocket *ss)
}
/* Need to tell the client that application has picked
* the name from the offered list and reconfigured the socket.
* Don't do this if we negotiated ESNI.
*/
ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn,
ssl_SendEmptyExtension);
if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) {
ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_server_name_xtn,
ssl_SendEmptyExtension);
}
} else {
/* Callback returned index outside of the boundary. */
PORT_Assert((unsigned int)ret < ss->xtnData.sniNameArrSize);
......@@ -8631,7 +8652,7 @@ ssl3_HandleClientHelloPart2(sslSocket *ss,
* The product policy won't change during the process lifetime.
* Implemented ("isPresent") shouldn't change for servers.
*/
if (!config_match(suite, ss->ssl3.policy, &vrange, ss))
if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss))
break;
#else
if (!suite->enabled)
......@@ -9013,7 +9034,7 @@ ssl3_HandleV2ClientHello(sslSocket *ss, unsigned char *buffer, unsigned int leng
for (j = 0; j < ssl_V3_SUITES_IMPLEMENTED; j++) {
ssl3CipherSuiteCfg *suite = &ss->cipherSuites[j];
SSLVersionRange vrange = { ss->version, ss->version };
if (!config_match(suite, ss->ssl3.policy, &vrange, ss)) {
if (!ssl3_config_match(suite, ss->ssl3.policy, &vrange, ss)) {
continue;
}
for (i = 0; i + 2 < suite_length; i += 3) {
......
......@@ -327,16 +327,13 @@ ssl3_HandleECDHClientKeyExchange(sslSocket *ss, PRUint8 *b,
** Take an encoded key share and make a public key out of it.
*/
SECStatus
ssl_ImportECDHKeyShare(sslSocket *ss, SECKEYPublicKey *peerKey,
ssl_ImportECDHKeyShare(SECKEYPublicKey *peerKey,
PRUint8 *b, PRUint32 length,
const sslNamedGroupDef *ecGroup)
{
SECStatus rv;
SECItem ecPoint = { siBuffer, NULL, 0 };
PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
if (!length) {
PORT_SetError(SSL_ERROR_RX_MALFORMED_ECDHE_KEY_SHARE);
return SECFailure;
......@@ -616,7 +613,7 @@ ssl3_HandleECDHServerKeyExchange(sslSocket *ss, PRUint8 *b, PRUint32 length)
peerKey->arena = arena;
/* create public key from point data */
rv = ssl_ImportECDHKeyShare(ss, peerKey, ec_point.data, ec_point.len,
rv = ssl_ImportECDHKeyShare(peerKey, ec_point.data, ec_point.len,
ecGroup);
if (rv != SECSuccess) {
/* error code is set */
......
......@@ -50,6 +50,7 @@ static const ssl3ExtensionHandler clientHelloHandlers[] = {
{ ssl_tls13_early_data_xtn, &tls13_ServerHandleEarlyDataXtn },
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ServerHandlePskModesXtn },
{ ssl_tls13_cookie_xtn, &tls13_ServerHandleCookieXtn },
{ ssl_tls13_encrypted_sni_xtn, &tls13_ServerHandleEsniXtn },
{ ssl_record_size_limit_xtn, &ssl_HandleRecordSizeLimitXtn },
{ 0, NULL }
};
......@@ -136,6 +137,7 @@ static const sslExtensionBuilder clientHelloSendersTLS[] =
{ ssl_signature_algorithms_xtn, &ssl3_SendSigAlgsXtn },
{ ssl_tls13_cookie_xtn, &tls13_ClientSendHrrCookieXtn },
{ ssl_tls13_psk_key_exchange_modes_xtn, &tls13_ClientSendPskModesXtn },
{ ssl_tls13_encrypted_sni_xtn, &tls13_ClientSendEsniXtn },
{ ssl_record_size_limit_xtn, &ssl_SendRecordSizeLimitXtn },
/* The pre_shared_key extension MUST be last. */
{ ssl_tls13_pre_shared_key_xtn, &tls13_ClientSendPreSharedKeyXtn },
......@@ -338,8 +340,6 @@ ssl3_ParseExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length)
return SECFailure; /* alert already sent */
}
SSL_TRC(10, ("%d: SSL3[%d]: parsing extension %d",
SSL_GETPID(), ss->fd, extension_type));
/* Check whether an extension has been sent multiple times. */
for (cursor = PR_NEXT_LINK(&ss->ssl3.hs.remoteExtensions);
cursor != &ss->ssl3.hs.remoteExtensions;
......@@ -357,6 +357,9 @@ ssl3_ParseExtensions(sslSocket *ss, PRUint8 **b, PRUint32 *length)
return rv; /* alert already sent */
}
SSL_TRC(10, ("%d: SSL3[%d]: parsed extension %d len=%u",
SSL_GETPID(), ss->fd, extension_type, extension_data.len));
extension = PORT_ZNew(TLSExtension);
if (!extension) {
return SECFailure;
......@@ -409,7 +412,9 @@ ssl_CallExtensionHandler(sslSocket *ss, SSLHandshakeType handshakeMessage,
/* Find extension_type in table of Hello Extension Handlers. */
for (; handler->ex_handler != NULL; ++handler) {
if (handler->ex_type == extension->type) {
rv = (*handler->ex_handler)(ss, &ss->xtnData, &extension->data);
SECItem tmp = extension->data;
rv = (*handler->ex_handler)(ss, &ss->xtnData, &tmp);
break;
}
}
......@@ -960,6 +965,8 @@ ssl3_DestroyExtensionData(TLSExtensionData *xtnData)
xtnData->certReqAuthorities.arena = NULL;
}
PORT_Free(xtnData->advertised);
ssl_FreeEphemeralKeyPair(xtnData->esniPrivateKey);
SECITEM_FreeItem(&xtnData->keyShareExtension, PR_FALSE);
}
/* Free everything that has been allocated and then reset back to
......
......@@ -11,6 +11,8 @@
#include "sslencode.h"
#define TLS13_ESNI_NONCE_SIZE 16
typedef enum {
sni_nametype_hostname
} SNINameType;
......@@ -101,6 +103,14 @@ struct TLSExtensionDataStr {
/* The record size limit set by the peer. Our value is kept in ss->opt. */
PRUint16 recordSizeLimit;
/* ESNI working state */
SECItem keyShareExtension;
ssl3CipherSuite esniSuite;
sslEphemeralKeyPair *esniPrivateKey;
/* Pointer into |ss->esniKeys->keyShares| */
TLS13KeyShareEntry *peerEsniShare;
PRUint8 esniNonce[TLS13_ESNI_NONCE_SIZE];
};
typedef struct TLSExtensionStr {
......
......@@ -15,30 +15,40 @@
#include "selfencrypt.h"
#include "ssl3ext.h"
#include "ssl3exthandle.h"
#include "tls13esni.h"
#include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */
PRBool
ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url)
{
PRNetAddr netAddr;
/* must have a hostname */
if (!url || !url[0]) {
return PR_FALSE;
}
/* must not be an IPv4 or IPv6 address */
if (PR_SUCCESS == PR_StringToNetAddr(url, &netAddr)) {
/* is an IP address (v4 or v6) */
return PR_FALSE;
}
return PR_TRUE;
}
/* Format an SNI extension, using the name from the socket's URL,
* unless that name is a dotted decimal string.
* Used by client and server.
*/
SECStatus
ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added)
ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url,
TLSExtensionData *xtnData,
sslBuffer *buf)
{
unsigned int len;
PRNetAddr netAddr;
SECStatus rv;
/* must have a hostname */
if (!ss->url || !ss->url[0]) {
return SECSuccess;
}
/* must not be an IPv4 or IPv6 address */
if (PR_SUCCESS == PR_StringToNetAddr(ss->url, &netAddr)) {
/* is an IP address (v4 or v6) */
return SECSuccess;
}
len = PORT_Strlen(ss->url);
len = PORT_Strlen(url);
/* length of server_name_list */
rv = sslBuffer_AppendNumber(buf, len + 3, 2);
if (rv != SECSuccess) {
......@@ -50,7 +60,33 @@ ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
return SECFailure;
}
/* HostName (length and value) */
rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)ss->url, len, 2);
rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)url, len, 2);
if (rv != SECSuccess) {
return SECFailure;
}
return SECSuccess;
}
SECStatus
ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added)
{
SECStatus rv;
const char *url = ss->url;
/* We only make an ESNI private key if we are going to
* send ESNI. */
if (ss->xtnData.esniPrivateKey != NULL) {
url = ss->esniKeys->dummySni;
}
if (!ssl_ShouldSendSNIExtension(ss, url)) {
return SECSuccess;
}
rv = ssl3_ClientFormatServerNameXtn(ss, url, xtnData, buf);
if (rv != SECSuccess) {
return SECFailure;
}
......@@ -59,7 +95,6 @@ ssl3_ClientSendServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
return SECSuccess;
}
/* Handle an incoming SNI extension. */
SECStatus
ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
SECItem *data)
......@@ -72,6 +107,13 @@ ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
return SECSuccess; /* ignore extension */
}
if (ssl3_ExtensionNegotiated(ss, ssl_tls13_encrypted_sni_xtn)) {
/* If we already have ESNI, make sure we don't overwrite
* the value. */
PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
return SECSuccess;
}
/* Server side - consume client data and register server sender. */
/* do not parse the data if don't have user extension handling function. */
if (!ss->sniSocketConfig) {
......
......@@ -91,6 +91,10 @@ SECStatus ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss,
SECItem *data);
SECStatus ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket,
/* out */ SECItem *appToken);
PRBool ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url);
SECStatus ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url,
TLSExtensionData *xtnData,
sslBuffer *buf);
SECStatus ssl3_ClientSendServerNameXtn(const sslSocket *ss,
TLSExtensionData *xtnData,
sslBuffer *buf, PRBool *added);
......
......@@ -264,6 +264,9 @@ typedef enum {
SSL_ERROR_BAD_RESUMPTION_TOKEN_ERROR = (SSL_ERROR_BASE + 173),
SSL_ERROR_RX_MALFORMED_DTLS_ACK = (SSL_ERROR_BASE + 174),
SSL_ERROR_DH_KEY_TOO_LONG = (SSL_ERROR_BASE + 175),
SSL_ERROR_RX_MALFORMED_ESNI_KEYS = (SSL_ERROR_BASE + 176),
SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION = (SSL_ERROR_BASE + 177),
SSL_ERROR_MISSING_ESNI_EXTENSION = (SSL_ERROR_BASE + 178),
SSL_ERROR_END_OF_LIST /* let the c compiler determine the value of this. */
} SSLErrorCodes;
#endif /* NO_SECURITY_ERROR_ENUM */
......
......@@ -452,8 +452,65 @@ typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)(
(PRFileDesc * _fd, PRUint32 _size), \
(fd, size))
/* Deprecated experimental APIs */
/* Set the ESNI key pair on a socket (server side)
*
* fd -- the socket
* record/recordLen -- the encoded DNS record (not base64)
*
* Important: the suites that are advertised in the record must
* be configured on, or this call will fail.
*/
#define SSL_SetESNIKeyPair(fd, \
privKey, record, recordLen) \
SSL_EXPERIMENTAL_API("SSL_SetESNIKeyPair", \
(PRFileDesc * _fd, \
SECKEYPrivateKey * _privKey, \
const PRUint8 *_record, unsigned int _recordLen), \
(fd, privKey, \
record, recordLen))
/* Set the ESNI keys on a client
*
* fd -- the socket
* ensikeys/esniKeysLen -- the ESNI key structure (not base64)
* dummyESNI -- the dummy ESNI to use (if any)
*/
#define SSL_EnableESNI(fd, esniKeys, esniKeysLen, dummySNI) \
SSL_EXPERIMENTAL_API("SSL_EnableESNI", \
(PRFileDesc * _fd, \
const PRUint8 *_esniKeys, \
unsigned int _esniKeysLen, \
const char *_dummySNI), \
(fd, esniKeys, esniKeysLen, dummySNI))
/*
* Generate an encoded ESNIKeys structure (presumably server side).
*
* cipherSuites -- the cipher suites that can be used
* cipherSuitesCount -- the number of suites in cipherSuites
* group -- the named group this key corresponds to
* pubKey -- the public key for the key pair
* pad -- the length to pad to
* notBefore/notAfter -- validity range
* out/outlen/maxlen -- where to output the data
*/
#define SSL_EncodeESNIKeys(cipherSuites, cipherSuiteCount, \