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