summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/tls13esni.c
diff options
context:
space:
mode:
Diffstat (limited to 'security/nss/lib/ssl/tls13esni.c')
-rw-r--r--security/nss/lib/ssl/tls13esni.c844
1 files changed, 844 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13esni.c b/security/nss/lib/ssl/tls13esni.c
new file mode 100644
index 000000000..e2328769b
--- /dev/null
+++ b/security/nss/lib/ssl/tls13esni.c
@@ -0,0 +1,844 @@
+/* -*- 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(&copy->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, &copy, 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(&copy, 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;
+}