diff options
Diffstat (limited to 'security/nss/lib/ssl/tls13esni.c')
-rw-r--r-- | security/nss/lib/ssl/tls13esni.c | 844 |
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(©->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; +} |