mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-08-30 03:08:38 +09:00
844 lines
22 KiB
C
844 lines
22 KiB
C
/* -*- 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/. */
|
|
|
|
#define TLS13_ESNI_VERSION 0xff01
|
|
|
|
/*
|
|
* struct {
|
|
* uint16 version;
|
|
* uint8 checksum[4];
|
|
* KeyShareEntry keys<4..2^16-1>;
|
|
* CipherSuite cipher_suites<2..2^16-2>;
|
|
* uint16 padded_length;
|
|
* uint64 not_before;
|
|
* uint64 not_after;
|
|
* Extension extensions<0..2^16-1>;
|
|
* } ESNIKeys;
|
|
*/
|
|
#include "nss.h"
|
|
#include "pk11func.h"
|
|
#include "ssl.h"
|
|
#include "sslproto.h"
|
|
#include "sslimpl.h"
|
|
#include "ssl3exthandle.h"
|
|
#include "tls13esni.h"
|
|
#include "tls13exthandle.h"
|
|
#include "tls13hkdf.h"
|
|
|
|
const char kHkdfPurposeEsniKey[] = "esni key";
|
|
const char kHkdfPurposeEsniIv[] = "esni iv";
|
|
|
|
void
|
|
tls13_DestroyESNIKeys(sslEsniKeys *keys)
|
|
{
|
|
if (!keys) {
|
|
return;
|
|
}
|
|
SECITEM_FreeItem(&keys->data, PR_FALSE);
|
|
PORT_Free((void *)keys->dummySni);
|
|
tls13_DestroyKeyShares(&keys->keyShares);
|
|
ssl_FreeEphemeralKeyPair(keys->privKey);
|
|
SECITEM_FreeItem(&keys->suites, PR_FALSE);
|
|
PORT_ZFree(keys, sizeof(sslEsniKeys));
|
|
}
|
|
|
|
sslEsniKeys *
|
|
tls13_CopyESNIKeys(sslEsniKeys *okeys)
|
|
{
|
|
sslEsniKeys *nkeys;
|
|
SECStatus rv;
|
|
|
|
PORT_Assert(okeys);
|
|
|
|
nkeys = PORT_ZNew(sslEsniKeys);
|
|
if (!nkeys) {
|
|
return NULL;
|
|
}
|
|
PR_INIT_CLIST(&nkeys->keyShares);
|
|
rv = SECITEM_CopyItem(NULL, &nkeys->data, &okeys->data);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
if (okeys->dummySni) {
|
|
nkeys->dummySni = PORT_Strdup(okeys->dummySni);
|
|
if (!nkeys->dummySni) {
|
|
goto loser;
|
|
}
|
|
}
|
|
for (PRCList *cur_p = PR_LIST_HEAD(&okeys->keyShares);
|
|
cur_p != &okeys->keyShares;
|
|
cur_p = PR_NEXT_LINK(cur_p)) {
|
|
TLS13KeyShareEntry *copy = tls13_CopyKeyShareEntry(
|
|
(TLS13KeyShareEntry *)cur_p);
|
|
if (!copy) {
|
|
goto loser;
|
|
}
|
|
PR_APPEND_LINK(©->link, &nkeys->keyShares);
|
|
}
|
|
if (okeys->privKey) {
|
|
nkeys->privKey = ssl_CopyEphemeralKeyPair(okeys->privKey);
|
|
if (!nkeys->privKey) {
|
|
goto loser;
|
|
}
|
|
}
|
|
rv = SECITEM_CopyItem(NULL, &nkeys->suites, &okeys->suites);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
nkeys->paddedLength = okeys->paddedLength;
|
|
nkeys->notBefore = okeys->notBefore;
|
|
nkeys->notAfter = okeys->notAfter;
|
|
return nkeys;
|
|
|
|
loser:
|
|
tls13_DestroyESNIKeys(nkeys);
|
|
return NULL;
|
|
}
|
|
|
|
/* Checksum is a 4-byte array. */
|
|
static SECStatus
|
|
tls13_ComputeESNIKeysChecksum(const PRUint8 *buf, unsigned int len,
|
|
PRUint8 *checksum)
|
|
{
|
|
SECItem copy;
|
|
SECStatus rv;
|
|
PRUint8 sha256[32];
|
|
|
|
rv = SECITEM_MakeItem(NULL, ©, buf, len);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Stomp the checksum. */
|
|
PORT_Memset(copy.data + 2, 0, 4);
|
|
|
|
rv = PK11_HashBuf(ssl3_HashTypeToOID(ssl_hash_sha256),
|
|
sha256,
|
|
copy.data, copy.len);
|
|
SECITEM_FreeItem(©, PR_FALSE);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
PORT_Memcpy(checksum, sha256, 4);
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
tls13_DecodeESNIKeys(SECItem *data, sslEsniKeys **keysp)
|
|
{
|
|
SECStatus rv;
|
|
sslReadBuffer tmp;
|
|
PRUint64 tmpn;
|
|
sslEsniKeys *keys;
|
|
PRUint8 checksum[4];
|
|
sslReader rdr = SSL_READER(data->data, data->len);
|
|
|
|
rv = sslRead_ReadNumber(&rdr, 2, &tmpn);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
if (tmpn != TLS13_ESNI_VERSION) {
|
|
PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION);
|
|
return SECFailure;
|
|
}
|
|
keys = PORT_ZNew(sslEsniKeys);
|
|
if (!keys) {
|
|
return SECFailure;
|
|
}
|
|
PR_INIT_CLIST(&keys->keyShares);
|
|
|
|
/* Make a copy. */
|
|
rv = SECITEM_CopyItem(NULL, &keys->data, data);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = tls13_ComputeESNIKeysChecksum(data->data, data->len, checksum);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Read and check checksum. */
|
|
rv = sslRead_Read(&rdr, 4, &tmp);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
if (0 != NSS_SecureMemcmp(tmp.buf, checksum, 4)) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Parse the key shares. */
|
|
rv = sslRead_ReadVariable(&rdr, 2, &tmp);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
sslReader rdr2 = SSL_READER(tmp.buf, tmp.len);
|
|
while (SSL_READER_REMAINING(&rdr2)) {
|
|
TLS13KeyShareEntry *ks = NULL;
|
|
|
|
rv = tls13_DecodeKeyShareEntry(&rdr2, &ks);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
if (ks) {
|
|
PR_APPEND_LINK(&ks->link, &keys->keyShares);
|
|
}
|
|
}
|
|
|
|
/* Parse cipher suites. */
|
|
rv = sslRead_ReadVariable(&rdr, 2, &tmp);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
/* This can't be odd. */
|
|
if (tmp.len & 1) {
|
|
goto loser;
|
|
}
|
|
rv = SECITEM_MakeItem(NULL, &keys->suites, (PRUint8 *)tmp.buf, tmp.len);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Padded Length */
|
|
rv = sslRead_ReadNumber(&rdr, 2, &tmpn);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
keys->paddedLength = (PRUint16)tmpn;
|
|
|
|
/* Not Before */
|
|
rv = sslRead_ReadNumber(&rdr, 8, &keys->notBefore);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Not After */
|
|
rv = sslRead_ReadNumber(&rdr, 8, &keys->notAfter);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Extensions, which we ignore. */
|
|
rv = sslRead_ReadVariable(&rdr, 2, &tmp);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Check that this is empty. */
|
|
if (SSL_READER_REMAINING(&rdr) > 0) {
|
|
goto loser;
|
|
}
|
|
|
|
*keysp = keys;
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
tls13_DestroyESNIKeys(keys);
|
|
PORT_SetError(SSL_ERROR_RX_MALFORMED_ESNI_KEYS);
|
|
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Encode an ESNI keys structure. We only allow one key
|
|
* share. */
|
|
SECStatus
|
|
SSLExp_EncodeESNIKeys(PRUint16 *cipherSuites, unsigned int cipherSuiteCount,
|
|
SSLNamedGroup group, SECKEYPublicKey *pubKey,
|
|
PRUint16 pad, PRUint64 notBefore, PRUint64 notAfter,
|
|
PRUint8 *out, unsigned int *outlen, unsigned int maxlen)
|
|
{
|
|
unsigned int savedOffset;
|
|
SECStatus rv;
|
|
sslBuffer b = SSL_BUFFER_EMPTY;
|
|
|
|
rv = sslBuffer_AppendNumber(&b, TLS13_ESNI_VERSION, 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = sslBuffer_Skip(&b, 4, &savedOffset);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Length of vector. */
|
|
rv = sslBuffer_AppendNumber(
|
|
&b, tls13_SizeOfKeyShareEntry(pubKey), 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Our one key share. */
|
|
rv = tls13_EncodeKeyShareEntry(&b, group, pubKey);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Cipher suites. */
|
|
rv = sslBuffer_AppendNumber(&b, cipherSuiteCount * 2, 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
for (unsigned int i = 0; i < cipherSuiteCount; i++) {
|
|
rv = sslBuffer_AppendNumber(&b, cipherSuites[i], 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
/* Padding Length. Fixed for now. */
|
|
rv = sslBuffer_AppendNumber(&b, pad, 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Start time. */
|
|
rv = sslBuffer_AppendNumber(&b, notBefore, 8);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* End time. */
|
|
rv = sslBuffer_AppendNumber(&b, notAfter, 8);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* No extensions. */
|
|
rv = sslBuffer_AppendNumber(&b, 0, 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = tls13_ComputeESNIKeysChecksum(SSL_BUFFER_BASE(&b),
|
|
SSL_BUFFER_LEN(&b),
|
|
SSL_BUFFER_BASE(&b) + 2);
|
|
if (rv != SECSuccess) {
|
|
PORT_Assert(PR_FALSE);
|
|
goto loser;
|
|
}
|
|
|
|
if (SSL_BUFFER_LEN(&b) > maxlen) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
PORT_Memcpy(out, SSL_BUFFER_BASE(&b), SSL_BUFFER_LEN(&b));
|
|
*outlen = SSL_BUFFER_LEN(&b);
|
|
|
|
sslBuffer_Clear(&b);
|
|
return SECSuccess;
|
|
loser:
|
|
sslBuffer_Clear(&b);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus
|
|
SSLExp_SetESNIKeyPair(PRFileDesc *fd,
|
|
SECKEYPrivateKey *privKey,
|
|
const PRUint8 *record, unsigned int recordLen)
|
|
{
|
|
sslSocket *ss;
|
|
SECStatus rv;
|
|
sslEsniKeys *keys = NULL;
|
|
SECKEYPublicKey *pubKey = NULL;
|
|
SECItem data = { siBuffer, CONST_CAST(PRUint8, record), recordLen };
|
|
PLArenaPool *arena = NULL;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in %s",
|
|
SSL_GETPID(), fd, __FUNCTION__));
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = tls13_DecodeESNIKeys(&data, &keys);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Check the cipher suites. */
|
|
(void)ssl3_config_match_init(ss);
|
|
/* Make sure the cipher suite is OK. */
|
|
SSLVersionRange vrange = { SSL_LIBRARY_VERSION_TLS_1_3,
|
|
SSL_LIBRARY_VERSION_TLS_1_3 };
|
|
|
|
sslReader csrdr = SSL_READER(keys->suites.data,
|
|
keys->suites.len);
|
|
while (SSL_READER_REMAINING(&csrdr)) {
|
|
PRUint64 asuite;
|
|
|
|
rv = sslRead_ReadNumber(&csrdr, 2, &asuite);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
const ssl3CipherSuiteCfg *suiteCfg =
|
|
ssl_LookupCipherSuiteCfg(asuite, ss->cipherSuites);
|
|
if (!ssl3_config_match(suiteCfg, ss->ssl3.policy, &vrange, ss)) {
|
|
/* Illegal suite. */
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
}
|
|
|
|
if (PR_CLIST_IS_EMPTY(&keys->keyShares)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
if (PR_PREV_LINK(&keys->keyShares) != PR_NEXT_LINK(&keys->keyShares)) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
TLS13KeyShareEntry *entry = (TLS13KeyShareEntry *)PR_LIST_HEAD(
|
|
&keys->keyShares);
|
|
if (entry->group->keaType != ssl_kea_ecdh) {
|
|
PORT_SetError(SEC_ERROR_INVALID_ARGS);
|
|
goto loser;
|
|
}
|
|
arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
|
|
if (!arena) {
|
|
goto loser;
|
|
}
|
|
pubKey = PORT_ArenaZNew(arena, SECKEYPublicKey);
|
|
if (!pubKey) {
|
|
goto loser;
|
|
}
|
|
pubKey->arena = arena;
|
|
arena = NULL; /* From here, this will be destroyed with the pubkey. */
|
|
/* Dummy PKCS11 values because this key isn't on a slot. */
|
|
pubKey->pkcs11Slot = NULL;
|
|
pubKey->pkcs11ID = CK_INVALID_HANDLE;
|
|
rv = ssl_ImportECDHKeyShare(pubKey,
|
|
entry->key_exchange.data,
|
|
entry->key_exchange.len,
|
|
entry->group);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
privKey = SECKEY_CopyPrivateKey(privKey);
|
|
if (!privKey) {
|
|
goto loser;
|
|
}
|
|
keys->privKey = ssl_NewEphemeralKeyPair(entry->group, privKey, pubKey);
|
|
if (!keys->privKey) {
|
|
goto loser;
|
|
}
|
|
pubKey = NULL;
|
|
ss->esniKeys = keys;
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
PORT_FreeArena(arena, PR_FALSE);
|
|
SECKEY_DestroyPublicKey(pubKey);
|
|
tls13_DestroyESNIKeys(keys);
|
|
return SECFailure;
|
|
}
|
|
|
|
SECStatus
|
|
SSLExp_EnableESNI(PRFileDesc *fd,
|
|
const PRUint8 *esniKeys,
|
|
unsigned int esniKeysLen,
|
|
const char *dummySNI)
|
|
{
|
|
sslSocket *ss;
|
|
sslEsniKeys *keys = NULL;
|
|
SECItem data = { siBuffer, CONST_CAST(PRUint8, esniKeys), esniKeysLen };
|
|
SECStatus rv;
|
|
|
|
ss = ssl_FindSocket(fd);
|
|
if (!ss) {
|
|
SSL_DBG(("%d: SSL[%d]: bad socket in %s",
|
|
SSL_GETPID(), fd, __FUNCTION__));
|
|
return SECFailure;
|
|
}
|
|
|
|
rv = tls13_DecodeESNIKeys(&data, &keys);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
if (dummySNI) {
|
|
keys->dummySni = PORT_Strdup(dummySNI);
|
|
if (!keys->dummySni) {
|
|
tls13_DestroyESNIKeys(keys);
|
|
return SECFailure;
|
|
}
|
|
}
|
|
|
|
/* Delete in case it was set before. */
|
|
tls13_DestroyESNIKeys(ss->esniKeys);
|
|
ss->esniKeys = keys;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* struct {
|
|
* opaque record_digest<0..2^16-1>;
|
|
* KeyShareEntry esni_key_share;
|
|
* Random client_hello_random;
|
|
* } ESNIContents;
|
|
*/
|
|
SECStatus
|
|
tls13_ComputeESNIKeys(const sslSocket *ss,
|
|
TLS13KeyShareEntry *entry,
|
|
sslKeyPair *keyPair,
|
|
const ssl3CipherSuiteDef *suite,
|
|
const PRUint8 *esniKeysHash,
|
|
const PRUint8 *keyShareBuf,
|
|
unsigned int keyShareBufLen,
|
|
const PRUint8 *clientRandom,
|
|
ssl3KeyMaterial *keyMat)
|
|
{
|
|
PK11SymKey *Z = NULL;
|
|
PK11SymKey *Zx = NULL;
|
|
SECStatus ret = SECFailure;
|
|
PRUint8 esniContentsBuf[256]; /* Just big enough. */
|
|
sslBuffer esniContents = SSL_BUFFER(esniContentsBuf);
|
|
PRUint8 hash[64];
|
|
const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suite);
|
|
size_t keySize = cipherDef->key_size;
|
|
size_t ivSize = cipherDef->iv_size +
|
|
cipherDef->explicit_nonce_size; /* This isn't always going to
|
|
* work, but it does for
|
|
* AES-GCM */
|
|
unsigned int hashSize = tls13_GetHashSizeForHash(suite->prf_hash);
|
|
SECStatus rv;
|
|
|
|
rv = tls13_HandleKeyShare(CONST_CAST(sslSocket, ss), entry, keyPair,
|
|
suite->prf_hash, &Z);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = tls13_HkdfExtract(NULL, Z, suite->prf_hash, &Zx);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Encode ESNIContents. */
|
|
rv = sslBuffer_AppendVariable(&esniContents,
|
|
esniKeysHash, hashSize, 2);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = sslBuffer_Append(&esniContents, keyShareBuf, keyShareBufLen);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = sslBuffer_Append(&esniContents, clientRandom, SSL3_RANDOM_LENGTH);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
PORT_Assert(hashSize <= sizeof(hash));
|
|
rv = PK11_HashBuf(ssl3_HashTypeToOID(suite->prf_hash),
|
|
hash,
|
|
SSL_BUFFER_BASE(&esniContents),
|
|
SSL_BUFFER_LEN(&esniContents));
|
|
;
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = tls13_HkdfExpandLabel(Zx, suite->prf_hash,
|
|
hash, hashSize,
|
|
kHkdfPurposeEsniKey, strlen(kHkdfPurposeEsniKey),
|
|
ssl3_Alg2Mech(cipherDef->calg),
|
|
keySize,
|
|
&keyMat->key);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
rv = tls13_HkdfExpandLabelRaw(Zx, suite->prf_hash,
|
|
hash, hashSize,
|
|
kHkdfPurposeEsniIv, strlen(kHkdfPurposeEsniIv),
|
|
keyMat->iv, ivSize);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
ret = SECSuccess;
|
|
|
|
loser:
|
|
PK11_FreeSymKey(Z);
|
|
PK11_FreeSymKey(Zx);
|
|
return ret;
|
|
}
|
|
|
|
/* Set up ESNI. This generates a private key as a side effect. */
|
|
SECStatus
|
|
tls13_ClientSetupESNI(sslSocket *ss)
|
|
{
|
|
ssl3CipherSuite suite;
|
|
sslEphemeralKeyPair *keyPair;
|
|
size_t i;
|
|
PRCList *cur;
|
|
SECStatus rv;
|
|
TLS13KeyShareEntry *share;
|
|
const sslNamedGroupDef *group = NULL;
|
|
PRTime now = PR_Now() / PR_USEC_PER_SEC;
|
|
|
|
if (!ss->esniKeys) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
if ((ss->esniKeys->notBefore > now) || (ss->esniKeys->notAfter < now)) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* If we're not sending SNI, don't send ESNI. */
|
|
if (!ssl_ShouldSendSNIExtension(ss, ss->url)) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
/* Pick the group. */
|
|
for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
|
|
for (cur = PR_NEXT_LINK(&ss->esniKeys->keyShares);
|
|
cur != &ss->esniKeys->keyShares;
|
|
cur = PR_NEXT_LINK(cur)) {
|
|
if (!ss->namedGroupPreferences[i]) {
|
|
continue;
|
|
}
|
|
share = (TLS13KeyShareEntry *)cur;
|
|
if (share->group->name == ss->namedGroupPreferences[i]->name) {
|
|
group = ss->namedGroupPreferences[i];
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!group) {
|
|
/* No compatible group. */
|
|
return SECSuccess;
|
|
}
|
|
|
|
rv = ssl3_NegotiateCipherSuiteInner(ss, &ss->esniKeys->suites,
|
|
SSL_LIBRARY_VERSION_TLS_1_3, &suite);
|
|
if (rv != SECSuccess) {
|
|
return SECSuccess;
|
|
}
|
|
|
|
rv = tls13_CreateKeyShare(ss, group, &keyPair);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
ss->xtnData.esniPrivateKey = keyPair;
|
|
ss->xtnData.esniSuite = suite;
|
|
ss->xtnData.peerEsniShare = share;
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
/*
|
|
* struct {
|
|
* CipherSuite suite;
|
|
* KeyShareEntry key_share;
|
|
* opaque record_digest<0..2^16-1>;
|
|
* opaque encrypted_sni<0..2^16-1>;
|
|
* } ClientEncryptedSNI;
|
|
*
|
|
* struct {
|
|
* ServerNameList sni;
|
|
* opaque zeros[ESNIKeys.padded_length - length(sni)];
|
|
* } PaddedServerNameList;
|
|
*
|
|
* struct {
|
|
* uint8 nonce[16];
|
|
* PaddedServerNameList realSNI;
|
|
* } ClientESNIInner;
|
|
*/
|
|
SECStatus
|
|
tls13_FormatEsniAADInput(sslBuffer *aadInput,
|
|
PRUint8 *keyShare, unsigned int keyShareLen)
|
|
{
|
|
SECStatus rv;
|
|
|
|
/* 8 bytes of 0 for the sequence number. */
|
|
rv = sslBuffer_AppendNumber(aadInput, 0, 8);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
/* Key share. */
|
|
PORT_Assert(keyShareLen > 0);
|
|
rv = sslBuffer_Append(aadInput, keyShare, keyShareLen);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
|
|
return SECSuccess;
|
|
}
|
|
|
|
static SECStatus
|
|
tls13_ServerGetEsniAEAD(const sslSocket *ss, PRUint64 suite,
|
|
const ssl3CipherSuiteDef **suiteDefp,
|
|
SSLAEADCipher *aeadp)
|
|
{
|
|
SECStatus rv;
|
|
const ssl3CipherSuiteDef *suiteDef;
|
|
SSLAEADCipher aead;
|
|
|
|
/* Check against the suite list for ESNI */
|
|
PRBool csMatch = PR_FALSE;
|
|
sslReader csrdr = SSL_READER(ss->esniKeys->suites.data,
|
|
ss->esniKeys->suites.len);
|
|
while (SSL_READER_REMAINING(&csrdr)) {
|
|
PRUint64 asuite;
|
|
|
|
rv = sslRead_ReadNumber(&csrdr, 2, &asuite);
|
|
if (rv != SECSuccess) {
|
|
return SECFailure;
|
|
}
|
|
if (asuite == suite) {
|
|
csMatch = PR_TRUE;
|
|
break;
|
|
}
|
|
}
|
|
if (!csMatch) {
|
|
return SECFailure;
|
|
}
|
|
|
|
suiteDef = ssl_LookupCipherSuiteDef(suite);
|
|
PORT_Assert(suiteDef);
|
|
if (!suiteDef) {
|
|
return SECFailure;
|
|
}
|
|
aead = tls13_GetAead(ssl_GetBulkCipherDef(suiteDef));
|
|
if (!aead) {
|
|
return SECFailure;
|
|
}
|
|
|
|
*suiteDefp = suiteDef;
|
|
*aeadp = aead;
|
|
return SECSuccess;
|
|
}
|
|
|
|
SECStatus
|
|
tls13_ServerDecryptEsniXtn(const sslSocket *ss, PRUint8 *in, unsigned int inLen,
|
|
PRUint8 *out, int *outLen, int maxLen)
|
|
{
|
|
sslReader rdr = SSL_READER(in, inLen);
|
|
PRUint64 suite;
|
|
const ssl3CipherSuiteDef *suiteDef;
|
|
SSLAEADCipher aead = NULL;
|
|
TLSExtension *keyShareExtension;
|
|
TLS13KeyShareEntry *entry = NULL;
|
|
ssl3KeyMaterial keyMat = { NULL };
|
|
|
|
sslBuffer aadInput = SSL_BUFFER_EMPTY;
|
|
const PRUint8 *keyShareBuf;
|
|
sslReadBuffer buf;
|
|
unsigned int keyShareBufLen;
|
|
PRUint8 hash[64];
|
|
SECStatus rv;
|
|
|
|
/* Read the cipher suite. */
|
|
rv = sslRead_ReadNumber(&rdr, 2, &suite);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Find the AEAD */
|
|
rv = tls13_ServerGetEsniAEAD(ss, suite, &suiteDef, &aead);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Note where the KeyShare starts. */
|
|
keyShareBuf = SSL_READER_CURRENT(&rdr);
|
|
rv = tls13_DecodeKeyShareEntry(&rdr, &entry);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
keyShareBufLen = SSL_READER_CURRENT(&rdr) - keyShareBuf;
|
|
if (!entry || entry->group->name != ss->esniKeys->privKey->group->name) {
|
|
goto loser;
|
|
}
|
|
|
|
/* The hash of the ESNIKeys structure. */
|
|
rv = sslRead_ReadVariable(&rdr, 2, &buf);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Check that the hash matches. */
|
|
unsigned int hashLen = tls13_GetHashSizeForHash(suiteDef->prf_hash);
|
|
PORT_Assert(hashLen <= sizeof(hash));
|
|
rv = PK11_HashBuf(ssl3_HashTypeToOID(suiteDef->prf_hash),
|
|
hash,
|
|
ss->esniKeys->data.data, ss->esniKeys->data.len);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
if (buf.len != hashLen) {
|
|
/* This is malformed. */
|
|
goto loser;
|
|
}
|
|
if (0 != NSS_SecureMemcmp(hash, buf.buf, hashLen)) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = tls13_ComputeESNIKeys(ss, entry,
|
|
ss->esniKeys->privKey->keys,
|
|
suiteDef,
|
|
hash, keyShareBuf, keyShareBufLen,
|
|
((sslSocket *)ss)->ssl3.hs.client_random,
|
|
&keyMat);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Read the ciphertext. */
|
|
rv = sslRead_ReadVariable(&rdr, 2, &buf);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Check that this is empty. */
|
|
if (SSL_READER_REMAINING(&rdr) > 0) {
|
|
goto loser;
|
|
}
|
|
|
|
/* Find the key share extension. */
|
|
keyShareExtension = ssl3_FindExtension(CONST_CAST(sslSocket, ss),
|
|
ssl_tls13_key_share_xtn);
|
|
if (!keyShareExtension) {
|
|
goto loser;
|
|
}
|
|
rv = tls13_FormatEsniAADInput(&aadInput,
|
|
keyShareExtension->data.data,
|
|
keyShareExtension->data.len);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
rv = aead(&keyMat, PR_TRUE /* Decrypt */,
|
|
out, outLen, maxLen,
|
|
buf.buf, buf.len,
|
|
SSL_BUFFER_BASE(&aadInput),
|
|
SSL_BUFFER_LEN(&aadInput));
|
|
sslBuffer_Clear(&aadInput);
|
|
if (rv != SECSuccess) {
|
|
goto loser;
|
|
}
|
|
|
|
ssl_DestroyKeyMaterial(&keyMat);
|
|
tls13_DestroyKeyShareEntry(entry);
|
|
return SECSuccess;
|
|
|
|
loser:
|
|
FATAL_ERROR(CONST_CAST(sslSocket, ss), SSL_ERROR_RX_MALFORMED_ESNI_EXTENSION, illegal_parameter);
|
|
ssl_DestroyKeyMaterial(&keyMat); /* Safe because zeroed. */
|
|
if (entry) {
|
|
tls13_DestroyKeyShareEntry(entry);
|
|
}
|
|
return SECFailure;
|
|
}
|