| /* -*- 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; |
| } |