diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/ssl/tls13con.c | 7210 |
1 files changed, 7210 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13con.c b/security/nss/lib/ssl/tls13con.c new file mode 100644 index 0000000000..2a3b8994a9 --- /dev/null +++ b/security/nss/lib/ssl/tls13con.c @@ -0,0 +1,7210 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * TLS 1.3 Protocol + * + * 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/. */ + +#include "sslt.h" +#include "stdarg.h" +#include "cert.h" +#include "ssl.h" +#include "keyhi.h" +#include "pk11func.h" +#include "prerr.h" +#include "secitem.h" +#include "secmod.h" +#include "sslimpl.h" +#include "sslproto.h" +#include "sslerr.h" +#include "ssl3exthandle.h" +#include "tls13hkdf.h" +#include "tls13con.h" +#include "tls13err.h" +#include "tls13ech.h" +#include "tls13exthandle.h" +#include "tls13hashstate.h" +#include "tls13subcerts.h" +#include "tls13psk.h" + +static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch, + SSLSecretDirection install, + PRBool deleteSecret); +static SECStatus tls13_SendServerHelloSequence(sslSocket *ss); +static SECStatus tls13_SendEncryptedExtensions(sslSocket *ss); +static void tls13_SetKeyExchangeType(sslSocket *ss, const sslNamedGroupDef *group); +static SECStatus tls13_HandleClientKeyShare(sslSocket *ss, + TLS13KeyShareEntry *peerShare); +static SECStatus tls13_SendHelloRetryRequest( + sslSocket *ss, const sslNamedGroupDef *selectedGroup, + const PRUint8 *token, unsigned int tokenLen); + +static SECStatus tls13_HandleServerKeyShare(sslSocket *ss); +static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b, + PRUint32 length); +static SECStatus tls13_SendCertificate(sslSocket *ss); +static SECStatus tls13_HandleCertificateDecode( + sslSocket *ss, PRUint8 *b, PRUint32 length); +static SECStatus tls13_HandleCertificate( + sslSocket *ss, PRUint8 *b, PRUint32 length, PRBool alreadyHashed); +static SECStatus tls13_ReinjectHandshakeTranscript(sslSocket *ss); +static SECStatus tls13_SendCertificateRequest(sslSocket *ss); +static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, + PRUint32 length); +static SECStatus +tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey); +static SECStatus tls13_HandleCertificateVerify( + sslSocket *ss, PRUint8 *b, PRUint32 length); +static SECStatus tls13_RecoverWrappedSharedSecret(sslSocket *ss, + sslSessionID *sid); +static SECStatus +tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key, + const char *prefix, + const char *suffix, + const char *keylogLabel, + PK11SymKey **dest); +SECStatus +tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, + const char *label, + unsigned int labelLen, + const SSL3Hashes *hashes, + PK11SymKey **dest, + SSLHashType hash); +static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss); +static SECStatus tls13_HandleEndOfEarlyData(sslSocket *ss, const PRUint8 *b, + PRUint32 length); +static SECStatus tls13_MaybeHandleSuppressedEndOfEarlyData(sslSocket *ss); +static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey); +static SECStatus tls13_ComputePskBinderHash(sslSocket *ss, PRUint8 *b, size_t length, + SSL3Hashes *hashes, SSLHashType type); +static SECStatus tls13_VerifyFinished(sslSocket *ss, SSLHandshakeType message, + PK11SymKey *secret, + PRUint8 *b, PRUint32 length, + const SSL3Hashes *hashes); +static SECStatus tls13_ClientHandleFinished(sslSocket *ss, + PRUint8 *b, PRUint32 length); +static SECStatus tls13_ServerHandleFinished(sslSocket *ss, + PRUint8 *b, PRUint32 length); +static SECStatus tls13_SendNewSessionTicket(sslSocket *ss, + const PRUint8 *appToken, + unsigned int appTokenLen); +static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, + PRUint32 length); +static SECStatus tls13_ComputeEarlySecretsWithPsk(sslSocket *ss); +static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss); +static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss); +static SECStatus tls13_ComputeFinalSecrets(sslSocket *ss); +static SECStatus tls13_ComputeFinished( + sslSocket *ss, PK11SymKey *baseKey, SSLHashType hashType, + const SSL3Hashes *hashes, PRBool sending, PRUint8 *output, + unsigned int *outputLen, unsigned int maxOutputLen); +static SECStatus tls13_SendClientSecondRound(sslSocket *ss); +static SECStatus tls13_SendClientSecondFlight(sslSocket *ss); +static SECStatus tls13_FinishHandshake(sslSocket *ss); + +const char kHkdfLabelClient[] = "c"; +const char kHkdfLabelServer[] = "s"; +const char kHkdfLabelDerivedSecret[] = "derived"; +const char kHkdfLabelResPskBinderKey[] = "res binder"; +const char kHkdfLabelExtPskBinderKey[] = "ext binder"; +const char kHkdfLabelEarlyTrafficSecret[] = "e traffic"; +const char kHkdfLabelEarlyExporterSecret[] = "e exp master"; +const char kHkdfLabelHandshakeTrafficSecret[] = "hs traffic"; +const char kHkdfLabelApplicationTrafficSecret[] = "ap traffic"; +const char kHkdfLabelFinishedSecret[] = "finished"; +const char kHkdfLabelResumptionMasterSecret[] = "res master"; +const char kHkdfLabelExporterMasterSecret[] = "exp master"; +const char kHkdfLabelResumption[] = "resumption"; +const char kHkdfLabelTrafficUpdate[] = "traffic upd"; +const char kHkdfPurposeKey[] = "key"; +const char kHkdfPurposeSn[] = "sn"; +const char kHkdfPurposeIv[] = "iv"; + +const char keylogLabelClientEarlyTrafficSecret[] = "CLIENT_EARLY_TRAFFIC_SECRET"; +const char keylogLabelClientHsTrafficSecret[] = "CLIENT_HANDSHAKE_TRAFFIC_SECRET"; +const char keylogLabelServerHsTrafficSecret[] = "SERVER_HANDSHAKE_TRAFFIC_SECRET"; +const char keylogLabelClientTrafficSecret[] = "CLIENT_TRAFFIC_SECRET_0"; +const char keylogLabelServerTrafficSecret[] = "SERVER_TRAFFIC_SECRET_0"; +const char keylogLabelEarlyExporterSecret[] = "EARLY_EXPORTER_SECRET"; +const char keylogLabelExporterSecret[] = "EXPORTER_SECRET"; + +/* Belt and suspenders in case we ever add a TLS 1.4. */ +PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <= + SSL_LIBRARY_VERSION_TLS_1_3); + +void +tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc) +{ + PORT_Assert(desc != internal_error); /* These should never happen */ + (void)SSL3_SendAlert(ss, alert_fatal, desc); + PORT_SetError(prError); +} + +#ifdef TRACE +#define STATE_CASE(a) \ + case a: \ + return #a +static char * +tls13_HandshakeState(SSL3WaitState st) +{ + switch (st) { + STATE_CASE(idle_handshake); + STATE_CASE(wait_client_hello); + STATE_CASE(wait_end_of_early_data); + STATE_CASE(wait_client_cert); + STATE_CASE(wait_client_key); + STATE_CASE(wait_cert_verify); + STATE_CASE(wait_change_cipher); + STATE_CASE(wait_finished); + STATE_CASE(wait_server_hello); + STATE_CASE(wait_certificate_status); + STATE_CASE(wait_server_cert); + STATE_CASE(wait_server_key); + STATE_CASE(wait_cert_request); + STATE_CASE(wait_hello_done); + STATE_CASE(wait_new_session_ticket); + STATE_CASE(wait_encrypted_extensions); + default: + break; + } + PORT_Assert(0); + return "unknown"; +} +#endif + +#define TLS13_WAIT_STATE_MASK 0x80 + +#define TLS13_BASE_WAIT_STATE(ws) (ws & ~TLS13_WAIT_STATE_MASK) +/* We don't mask idle_handshake because other parts of the code use it*/ +#define TLS13_WAIT_STATE(ws) (((ws == idle_handshake) || (ws == wait_server_hello)) ? ws : ws | TLS13_WAIT_STATE_MASK) +#define TLS13_CHECK_HS_STATE(ss, err, ...) \ + tls13_CheckHsState(ss, err, #err, __func__, __FILE__, __LINE__, \ + __VA_ARGS__, \ + wait_invalid) +void +tls13_SetHsState(sslSocket *ss, SSL3WaitState ws, + const char *func, const char *file, int line) +{ +#ifdef TRACE + const char *new_state_name = + tls13_HandshakeState(ws); + + SSL_TRC(3, ("%d: TLS13[%d]: %s state change from %s->%s in %s (%s:%d)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)), + new_state_name, + func, file, line)); +#endif + + ss->ssl3.hs.ws = TLS13_WAIT_STATE(ws); +} + +static PRBool +tls13_InHsStateV(sslSocket *ss, va_list ap) +{ + SSL3WaitState ws; + + while ((ws = va_arg(ap, SSL3WaitState)) != wait_invalid) { + if (TLS13_WAIT_STATE(ws) == ss->ssl3.hs.ws) { + return PR_TRUE; + } + } + return PR_FALSE; +} + +PRBool +tls13_InHsState(sslSocket *ss, ...) +{ + PRBool found; + va_list ap; + + va_start(ap, ss); + found = tls13_InHsStateV(ss, ap); + va_end(ap); + + return found; +} + +static SECStatus +tls13_CheckHsState(sslSocket *ss, int err, const char *error_name, + const char *func, const char *file, int line, + ...) +{ + va_list ap; + va_start(ap, line); + if (tls13_InHsStateV(ss, ap)) { + va_end(ap); + return SECSuccess; + } + va_end(ap); + + SSL_TRC(3, ("%d: TLS13[%d]: error %s state is (%s) at %s (%s:%d)", + SSL_GETPID(), ss->fd, + error_name, + tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)), + func, file, line)); + tls13_FatalError(ss, err, unexpected_message); + return SECFailure; +} + +PRBool +tls13_IsPostHandshake(const sslSocket *ss) +{ + return ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && ss->firstHsDone; +} + +SSLHashType +tls13_GetHashForCipherSuite(ssl3CipherSuite suite) +{ + const ssl3CipherSuiteDef *cipherDef = + ssl_LookupCipherSuiteDef(suite); + PORT_Assert(cipherDef); + if (!cipherDef) { + return ssl_hash_none; + } + return cipherDef->prf_hash; +} + +SSLHashType +tls13_GetHash(const sslSocket *ss) +{ + /* suite_def may not be set yet when doing EPSK 0-Rtt. */ + if (!ss->ssl3.hs.suite_def) { + if (ss->xtnData.selectedPsk) { + return ss->xtnData.selectedPsk->hash; + } + /* This should never happen. */ + PORT_Assert(0); + return ssl_hash_none; + } + + /* All TLS 1.3 cipher suites must have an explict PRF hash. */ + PORT_Assert(ss->ssl3.hs.suite_def->prf_hash != ssl_hash_none); + return ss->ssl3.hs.suite_def->prf_hash; +} + +SECStatus +tls13_GetHashAndCipher(PRUint16 version, PRUint16 cipherSuite, + SSLHashType *hash, const ssl3BulkCipherDef **cipher) +{ + if (version < SSL_LIBRARY_VERSION_TLS_1_3) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + // Lookup and check the suite. + SSLVersionRange vrange = { version, version }; + if (!ssl3_CipherSuiteAllowedForVersionRange(cipherSuite, &vrange)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + const ssl3CipherSuiteDef *suiteDef = ssl_LookupCipherSuiteDef(cipherSuite); + const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suiteDef); + if (cipherDef->type != type_aead) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + *hash = suiteDef->prf_hash; + if (cipher != NULL) { + *cipher = cipherDef; + } + return SECSuccess; +} + +unsigned int +tls13_GetHashSizeForHash(SSLHashType hash) +{ + switch (hash) { + case ssl_hash_sha256: + return 32; + case ssl_hash_sha384: + return 48; + default: + PORT_Assert(0); + } + return 32; +} + +unsigned int +tls13_GetHashSize(const sslSocket *ss) +{ + return tls13_GetHashSizeForHash(tls13_GetHash(ss)); +} + +static CK_MECHANISM_TYPE +tls13_GetHmacMechanismFromHash(SSLHashType hashType) +{ + switch (hashType) { + case ssl_hash_sha256: + return CKM_SHA256_HMAC; + case ssl_hash_sha384: + return CKM_SHA384_HMAC; + default: + PORT_Assert(0); + } + return CKM_SHA256_HMAC; +} + +static CK_MECHANISM_TYPE +tls13_GetHmacMechanism(const sslSocket *ss) +{ + return tls13_GetHmacMechanismFromHash(tls13_GetHash(ss)); +} + +SECStatus +tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes, + const PRUint8 *buf, unsigned int len, + SSLHashType hash) +{ + SECStatus rv; + + rv = PK11_HashBuf(ssl3_HashTypeToOID(hash), hashes->u.raw, buf, len); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + hashes->len = tls13_GetHashSizeForHash(hash); + + return SECSuccess; +} + +static SECStatus +tls13_CreateKEMKeyPair(sslSocket *ss, const sslNamedGroupDef *groupDef, + sslKeyPair **outKeyPair) +{ + PORT_Assert(groupDef); + if (groupDef->name != ssl_grp_kem_xyber768d00) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + sslKeyPair *keyPair = NULL; + SECKEYPrivateKey *privKey = NULL; + SECKEYPublicKey *pubKey = NULL; + CK_MECHANISM_TYPE mechanism = CKM_NSS_KYBER_KEY_PAIR_GEN; + CK_NSS_KEM_PARAMETER_SET_TYPE paramSet = CKP_NSS_KYBER_768_ROUND3; + + PK11SlotInfo *slot = PK11_GetBestSlot(mechanism, ss->pkcs11PinArg); + if (!slot) { + goto loser; + } + + privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism, + ¶mSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE, + CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg); + PK11_FreeSlot(slot); + if (!privKey || !pubKey) { + goto loser; + } + + keyPair = ssl_NewKeyPair(privKey, pubKey); + if (!keyPair) { + goto loser; + } + + SSL_TRC(50, ("%d: SSL[%d]: Create Kyber ephemeral key %d", + SSL_GETPID(), ss ? ss->fd : NULL, groupDef->name)); + PRINT_BUF(50, (ss, "Public Key", pubKey->u.kyber.publicValue.data, + pubKey->u.kyber.publicValue.len)); +#ifdef TRACE + if (ssl_trace >= 50) { + SECItem d = { siBuffer, NULL, 0 }; + SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_VALUE, &d); + if (rv == SECSuccess) { + PRINT_BUF(50, (ss, "Private Key", d.data, d.len)); + SECITEM_FreeItem(&d, PR_FALSE); + } else { + SSL_TRC(50, ("Error extracting private key")); + } + } +#endif + + *outKeyPair = keyPair; + return SECSuccess; + +loser: + SECKEY_DestroyPrivateKey(privKey); + SECKEY_DestroyPublicKey(pubKey); + ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL); + return SECFailure; +} + +SECStatus +tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef, + sslEphemeralKeyPair **outKeyPair) +{ + SECStatus rv; + const ssl3DHParams *params; + sslEphemeralKeyPair *keyPair = NULL; + + PORT_Assert(groupDef); + switch (groupDef->keaType) { + case ssl_kea_ecdh_hybrid: + if (groupDef->name != ssl_grp_kem_xyber768d00) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + rv = ssl_CreateECDHEphemeralKeyPair(ss, ssl_LookupNamedGroup(ssl_grp_ec_curve25519), &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + keyPair->group = groupDef; + break; + case ssl_kea_ecdh: + rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + break; + case ssl_kea_dh: + params = ssl_GetDHEParams(groupDef); + PORT_Assert(params->name != ssl_grp_ffdhe_custom); + rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + break; + default: + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + // If we're creating an ECDH + KEM hybrid share and we're the client, then + // we still need to generate the KEM key pair. Otherwise we're done. + if (groupDef->keaType == ssl_kea_ecdh_hybrid && !ss->sec.isServer) { + rv = tls13_CreateKEMKeyPair(ss, groupDef, &keyPair->kemKeys); + if (rv != SECSuccess) { + ssl_FreeEphemeralKeyPair(keyPair); + return SECFailure; + } + } + + *outKeyPair = keyPair; + return SECSuccess; +} + +SECStatus +tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef) +{ + sslEphemeralKeyPair *keyPair = NULL; + SECStatus rv; + + rv = tls13_CreateKeyShare(ss, groupDef, &keyPair); + if (rv != SECSuccess) { + return SECFailure; + } + PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs); + return SECSuccess; +} + +SECStatus +SSL_SendAdditionalKeyShares(PRFileDesc *fd, unsigned int count) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + ss->additionalShares = count; + return SECSuccess; +} + +/* + * Generate shares for ECDHE and FFDHE. This picks the first enabled group of + * the requisite type and creates a share for that. + * + * Called from ssl3_SendClientHello. + */ +SECStatus +tls13_SetupClientHello(sslSocket *ss, sslClientHelloType chType) +{ + unsigned int i; + SSL3Statistics *ssl3stats = SSL_GetStatistics(); + NewSessionTicket *session_ticket = NULL; + sslSessionID *sid = ss->sec.ci.sid; + unsigned int numShares = 0; + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + rv = tls13_ClientSetupEch(ss, chType); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Everything below here is only run on the first CH. */ + if (chType != client_hello_initial) { + return SECSuccess; + } + + rv = tls13_ClientGreaseSetup(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Select the first enabled group. + * TODO(ekr@rtfm.com): be smarter about offering the group + * that the other side negotiated if we are resuming. */ + PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); + for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) { + if (!ss->namedGroupPreferences[i]) { + continue; + } + rv = tls13_AddKeyShare(ss, ss->namedGroupPreferences[i]); + if (rv != SECSuccess) { + return SECFailure; + } + if (++numShares > ss->additionalShares) { + break; + } + } + + if (PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)) { + PORT_SetError(SSL_ERROR_NO_CIPHERS_SUPPORTED); + return SECFailure; + } + + /* Try to do stateless resumption, if we can. */ + if (sid->cached != never_cached && + sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + /* The caller must be holding sid->u.ssl3.lock for reading. */ + session_ticket = &sid->u.ssl3.locked.sessionTicket; + PORT_Assert(session_ticket && session_ticket->ticket.data); + + if (ssl_TicketTimeValid(ss, session_ticket)) { + ss->statelessResume = PR_TRUE; + } + + if (ss->statelessResume) { + PORT_Assert(ss->sec.ci.sid); + rv = tls13_RecoverWrappedSharedSecret(ss, ss->sec.ci.sid); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + SSL_AtomicIncrementLong(&ssl3stats->sch_sid_cache_not_ok); + ssl_UncacheSessionID(ss); + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = NULL; + return SECFailure; + } + + ss->ssl3.hs.cipher_suite = ss->sec.ci.sid->u.ssl3.cipherSuite; + rv = ssl3_SetupCipherSuite(ss, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; + } + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)); + } + } + + /* Derive the binder keys if any PSKs. */ + if (!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)) { + /* If an External PSK specified a suite, use that. */ + sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks); + if (!ss->statelessResume && + psk->type == ssl_psk_external && + psk->zeroRttSuite != TLS_NULL_WITH_NULL_NULL) { + ss->ssl3.hs.cipher_suite = psk->zeroRttSuite; + } + + rv = tls13_ComputeEarlySecretsWithPsk(ss); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + } + + return SECSuccess; +} + +static SECStatus +tls13_ImportDHEKeyShare(SECKEYPublicKey *peerKey, + PRUint8 *b, PRUint32 length, + SECKEYPublicKey *pubKey) +{ + SECStatus rv; + SECItem publicValue = { siBuffer, NULL, 0 }; + + publicValue.data = b; + publicValue.len = length; + if (!ssl_IsValidDHEShare(&pubKey->u.dh.prime, &publicValue)) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE); + return SECFailure; + } + + peerKey->keyType = dhKey; + rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.prime, + &pubKey->u.dh.prime); + if (rv != SECSuccess) + return SECFailure; + rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.base, + &pubKey->u.dh.base); + if (rv != SECSuccess) + return SECFailure; + rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.publicValue, + &publicValue); + if (rv != SECSuccess) + return SECFailure; + + return SECSuccess; +} + +static SECStatus +tls13_ImportKEMKeyShare(SECKEYPublicKey *peerKey, TLS13KeyShareEntry *entry) +{ + SECItem pk = { siBuffer, NULL, 0 }; + SECStatus rv; + + if (entry->group->name != ssl_grp_kem_xyber768d00) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); + return SECFailure; + } + pk.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES; + pk.len = entry->key_exchange.len - X25519_PUBLIC_KEY_BYTES; + + peerKey->keyType = kyberKey; + peerKey->u.kyber.params = params_kyber768_round3; + + rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.kyber.publicValue, &pk); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_HandleKEMCiphertext(sslSocket *ss, TLS13KeyShareEntry *entry, sslKeyPair *keyPair, PK11SymKey **outKey) +{ + SECItem ct = { siBuffer, NULL, 0 }; + SECStatus rv; + + switch (entry->group->name) { + case ssl_grp_kem_xyber768d00: + if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) { + ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); + return SECFailure; + } + ct.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES; + ct.len = entry->key_exchange.len - X25519_PUBLIC_KEY_BYTES; + break; + default: + PORT_Assert(0); + ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = PK11_Decapsulate(keyPair->privKey, &ct, CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE, CKF_DERIVE, outKey); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE); + } + return rv; +} + +static SECStatus +tls13_HandleKEMKey(sslSocket *ss, + TLS13KeyShareEntry *entry, + PK11SymKey **key, + SECItem **ciphertext) +{ + PORTCheapArenaPool arena; + SECKEYPublicKey *peerKey; + CK_OBJECT_HANDLE handle; + SECStatus rv; + + PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); + peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey); + if (peerKey == NULL) { + goto loser; + } + peerKey->arena = &arena.arena; + peerKey->pkcs11Slot = NULL; + peerKey->pkcs11ID = CK_INVALID_HANDLE; + + rv = tls13_ImportKEMKeyShare(peerKey, entry); + if (rv != SECSuccess) { + goto loser; + } + + PK11SlotInfo *slot = PK11_GetBestSlot(CKM_NSS_KYBER, ss->pkcs11PinArg); + if (!slot) { + goto loser; + } + + handle = PK11_ImportPublicKey(slot, peerKey, PR_FALSE); + PK11_FreeSlot(slot); /* peerKey holds a slot reference on success. */ + if (handle == CK_INVALID_HANDLE) { + goto loser; + } + + rv = PK11_Encapsulate(peerKey, + CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE, + CKF_DERIVE, key, ciphertext); + + /* Destroy the imported public key */ + PORT_Assert(peerKey->pkcs11Slot); + PK11_DestroyObject(peerKey->pkcs11Slot, peerKey->pkcs11ID); + PK11_FreeSlot(peerKey->pkcs11Slot); + + PORT_DestroyCheapArena(&arena); + return SECSuccess; + +loser: + PORT_DestroyCheapArena(&arena); + return SECFailure; +} + +SECStatus +tls13_HandleKeyShare(sslSocket *ss, + TLS13KeyShareEntry *entry, + sslKeyPair *keyPair, + SSLHashType hash, + PK11SymKey **out) +{ + PORTCheapArenaPool arena; + SECKEYPublicKey *peerKey; + CK_MECHANISM_TYPE mechanism; + PK11SymKey *key; + SECStatus rv; + int keySize = 0; + + PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE); + peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey); + if (peerKey == NULL) { + goto loser; + } + peerKey->arena = &arena.arena; + peerKey->pkcs11Slot = NULL; + peerKey->pkcs11ID = CK_INVALID_HANDLE; + + switch (entry->group->keaType) { + case ssl_kea_ecdh_hybrid: + if (entry->group->name != ssl_grp_kem_xyber768d00 || entry->key_exchange.len < X25519_PUBLIC_KEY_BYTES) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE); + goto loser; + } + rv = ssl_ImportECDHKeyShare(peerKey, + entry->key_exchange.data, + X25519_PUBLIC_KEY_BYTES, + ssl_LookupNamedGroup(ssl_grp_ec_curve25519)); + mechanism = CKM_ECDH1_DERIVE; + break; + case ssl_kea_ecdh: + rv = ssl_ImportECDHKeyShare(peerKey, + entry->key_exchange.data, + entry->key_exchange.len, + entry->group); + mechanism = CKM_ECDH1_DERIVE; + break; + case ssl_kea_dh: + rv = tls13_ImportDHEKeyShare(peerKey, + entry->key_exchange.data, + entry->key_exchange.len, + keyPair->pubKey); + mechanism = CKM_DH_PKCS_DERIVE; + keySize = peerKey->u.dh.publicValue.len; + break; + default: + PORT_Assert(0); + goto loser; + } + if (rv != SECSuccess) { + goto loser; + } + + key = PK11_PubDeriveWithKDF( + keyPair->privKey, peerKey, PR_FALSE, NULL, NULL, mechanism, + CKM_HKDF_DERIVE, CKA_DERIVE, keySize, CKD_NULL, NULL, NULL); + if (!key) { + ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE); + goto loser; + } + + *out = key; + PORT_DestroyCheapArena(&arena); + return SECSuccess; + +loser: + PORT_DestroyCheapArena(&arena); + return SECFailure; +} + +static PRBool +tls13_UseServerSecret(sslSocket *ss, SSLSecretDirection direction) +{ + return ss->sec.isServer == (direction == ssl_secret_write); +} + +static PK11SymKey ** +tls13_TrafficSecretRef(sslSocket *ss, SSLSecretDirection direction) +{ + if (tls13_UseServerSecret(ss, direction)) { + return &ss->ssl3.hs.serverTrafficSecret; + } + return &ss->ssl3.hs.clientTrafficSecret; +} + +SECStatus +tls13_UpdateTrafficKeys(sslSocket *ss, SSLSecretDirection direction) +{ + PK11SymKey **secret; + PK11SymKey *updatedSecret; + PRUint16 epoch; + SECStatus rv; + + secret = tls13_TrafficSecretRef(ss, direction); + rv = tls13_HkdfExpandLabel(*secret, tls13_GetHash(ss), + NULL, 0, + kHkdfLabelTrafficUpdate, + strlen(kHkdfLabelTrafficUpdate), + tls13_GetHmacMechanism(ss), + tls13_GetHashSize(ss), + ss->protocolVariant, + &updatedSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + PK11_FreeSymKey(*secret); + *secret = updatedSecret; + + ssl_GetSpecReadLock(ss); + if (direction == ssl_secret_read) { + epoch = ss->ssl3.crSpec->epoch; + } else { + epoch = ss->ssl3.cwSpec->epoch; + } + ssl_ReleaseSpecReadLock(ss); + + if (epoch == PR_UINT16_MAX) { + /* Good chance that this is an overflow from too many updates. */ + FATAL_ERROR(ss, SSL_ERROR_TOO_MANY_KEY_UPDATES, internal_error); + return SECFailure; + } + ++epoch; + + if (ss->secretCallback) { + ss->secretCallback(ss->fd, epoch, direction, updatedSecret, + ss->secretCallbackArg); + } + rv = tls13_SetCipherSpec(ss, epoch, direction, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + return SECSuccess; +} + +SECStatus +tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: %s send key update, response %s", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + (request == update_requested) ? "requested" + : "not requested")); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(!ss->sec.isServer || !ss->ssl3.clientCertRequested); + + if (!tls13_IsPostHandshake(ss)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_LIBRARY_FAILURE, + idle_handshake); + if (rv != SECSuccess) { + return SECFailure; + } + + if (IS_DTLS(ss)) { + rv = dtls13_MaybeSendKeyUpdate(ss, request, buffer); + if (rv != SECSuccess) { + /* Error code set already. */ + return SECFailure; + } + return rv; + } + + ssl_GetXmitBufLock(ss); + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_key_update, 1); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + rv = ssl3_AppendHandshakeNumber(ss, request, 1); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + + /* If we have been asked to buffer, then do so. This allows us to coalesce + * a KeyUpdate with a pending write. */ + rv = ssl3_FlushHandshake(ss, buffer ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0); + if (rv != SECSuccess) { + goto loser; /* error code set by ssl3_FlushHandshake */ + } + ssl_ReleaseXmitBufLock(ss); + + rv = tls13_UpdateTrafficKeys(ss, ssl_secret_write); + if (rv != SECSuccess) { + goto loser; /* error code set by tls13_UpdateTrafficKeys */ + } + + return SECSuccess; + +loser: + ssl_ReleaseXmitBufLock(ss); + return SECFailure; +} + +SECStatus +SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate) +{ + SECStatus rv; + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + if (!tls13_IsPostHandshake(ss)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ss->ssl3.clientCertRequested) { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS, + idle_handshake); + if (rv != SECSuccess) { + return SECFailure; + } + + ssl_GetSSL3HandshakeLock(ss); + rv = tls13_SendKeyUpdate(ss, requestUpdate ? update_requested : update_not_requested, + PR_FALSE /* don't buffer */); + + /* Remember that we are the ones that initiated this KeyUpdate. */ + if (rv == SECSuccess) { + ss->ssl3.peerRequestedKeyUpdate = PR_FALSE; + } + ssl_ReleaseSSL3HandshakeLock(ss); + return rv; +} + +SECStatus +SSLExp_SetCertificateCompressionAlgorithm(PRFileDesc *fd, SSLCertificateCompressionAlgorithm alg) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; /* Code already set. */ + } + + ssl_GetSSL3HandshakeLock(ss); + if (ss->ssl3.supportedCertCompressionAlgorithmsCount == MAX_SUPPORTED_CERTIFICATE_COMPRESSION_ALGS) { + goto loser; + } + + /* Reserved ID */ + if (alg.id == 0) { + goto loser; + } + + if (alg.encode == NULL && alg.decode == NULL) { + goto loser; + } + + /* Checking that we have not yet registed an algorithm with the same ID. */ + for (int i = 0; i < ss->ssl3.supportedCertCompressionAlgorithmsCount; i++) { + if (ss->ssl3.supportedCertCompressionAlgorithms[i].id == alg.id) { + goto loser; + } + } + + PORT_Memcpy(&ss->ssl3.supportedCertCompressionAlgorithms + [ss->ssl3.supportedCertCompressionAlgorithmsCount], + &alg, sizeof(alg)); + ss->ssl3.supportedCertCompressionAlgorithmsCount += 1; + ssl_ReleaseSSL3HandshakeLock(ss); + return SECSuccess; + +loser: + PORT_SetError(SEC_ERROR_INVALID_ARGS); + ssl_ReleaseSSL3HandshakeLock(ss); + return SECFailure; +} + +/* + * enum { + * update_not_requested(0), update_requested(1), (255) + * } KeyUpdateRequest; + * + * struct { + * KeyUpdateRequest request_update; + * } KeyUpdate; + */ + +/* If we're handing the DTLS1.3 message, we silently fail if there is a parsing problem. */ +static SECStatus +tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length) +{ + SECStatus rv; + PRUint32 update; + + SSL_TRC(3, ("%d: TLS13[%d]: %s handle key update", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (!tls13_IsPostHandshake(ss)) { + FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, unexpected_message); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, + idle_handshake); + if (rv != SECSuccess) { + /* We should never be idle_handshake prior to firstHsDone. */ + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeNumber(ss, &update, 1, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Error code set already. */ + } + if (length != 0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error); + return SECFailure; + } + if (!(update == update_requested || + update == update_not_requested)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error); + return SECFailure; + } + + if (IS_DTLS(ss)) { + return dtls13_HandleKeyUpdate(ss, b, length, update); + } + + rv = tls13_UpdateTrafficKeys(ss, ssl_secret_read); + if (rv != SECSuccess) { + return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */ + } + + if (update == update_requested) { + PRBool sendUpdate; + if (ss->ssl3.clientCertRequested) { + /* Post-handshake auth is in progress; defer sending a key update. */ + ss->ssl3.hs.keyUpdateDeferred = PR_TRUE; + ss->ssl3.hs.deferredKeyUpdateRequest = update_not_requested; + sendUpdate = PR_FALSE; + } else if (ss->ssl3.peerRequestedKeyUpdate) { + /* Only send an update if we have sent with the current spec. This + * prevents us from being forced to crank forward pointlessly. */ + ssl_GetSpecReadLock(ss); + sendUpdate = ss->ssl3.cwSpec->nextSeqNum > 0; + ssl_ReleaseSpecReadLock(ss); + } else { + sendUpdate = PR_TRUE; + } + if (sendUpdate) { + /* Respond immediately (don't buffer). */ + rv = tls13_SendKeyUpdate(ss, update_not_requested, PR_FALSE); + if (rv != SECSuccess) { + return SECFailure; /* Error already set. */ + } + } + ss->ssl3.peerRequestedKeyUpdate = PR_TRUE; + } + + return SECSuccess; +} + +SECStatus +SSLExp_SendCertificateRequest(PRFileDesc *fd) +{ + SECStatus rv; + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + /* Not supported. */ + if (IS_DTLS(ss)) { + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION); + return SECFailure; + } + + if (!tls13_IsPostHandshake(ss)) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + if (ss->ssl3.clientCertRequested) { + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return SECFailure; + } + + /* Disallow a CertificateRequest if this connection uses an external PSK. */ + if (ss->sec.authType == ssl_auth_psk) { + PORT_SetError(SSL_ERROR_FEATURE_DISABLED); + return SECFailure; + } + + rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS, + idle_handshake); + if (rv != SECSuccess) { + return SECFailure; + } + + if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_post_handshake_auth_xtn)) { + PORT_SetError(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION); + return SECFailure; + } + + ssl_GetSSL3HandshakeLock(ss); + + rv = tls13_SendCertificateRequest(ss); + if (rv == SECSuccess) { + ssl_GetXmitBufLock(ss); + rv = ssl3_FlushHandshake(ss, 0); + ssl_ReleaseXmitBufLock(ss); + ss->ssl3.clientCertRequested = PR_TRUE; + } + + ssl_ReleaseSSL3HandshakeLock(ss); + return rv; +} + +SECStatus +tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + if (ss->sec.isServer && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) { + SSL_TRC(3, ("%d: TLS13[%d]: successfully decrypted handshake after " + "failed 0-RTT", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none; + } + + /* TODO(ekr@rtfm.com): Would it be better to check all the states here? */ + switch (ss->ssl3.hs.msg_type) { + case ssl_hs_certificate: + return tls13_HandleCertificate(ss, b, length, PR_FALSE); + case ssl_hs_compressed_certificate: + return tls13_HandleCertificateDecode(ss, b, length); + case ssl_hs_certificate_request: + return tls13_HandleCertificateRequest(ss, b, length); + + case ssl_hs_certificate_verify: + return tls13_HandleCertificateVerify(ss, b, length); + + case ssl_hs_encrypted_extensions: + return tls13_HandleEncryptedExtensions(ss, b, length); + + case ssl_hs_new_session_ticket: + return tls13_HandleNewSessionTicket(ss, b, length); + + case ssl_hs_finished: + if (ss->sec.isServer) { + return tls13_ServerHandleFinished(ss, b, length); + } else { + return tls13_ClientHandleFinished(ss, b, length); + } + + case ssl_hs_end_of_early_data: + return tls13_HandleEndOfEarlyData(ss, b, length); + + case ssl_hs_key_update: + return tls13_HandleKeyUpdate(ss, b, length); + + default: + FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message); + return SECFailure; + } + + PORT_Assert(0); /* Unreached */ + return SECFailure; +} + +static SECStatus +tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid) +{ + PK11SymKey *wrapKey; /* wrapping key */ + SECItem wrappedMS = { siBuffer, NULL, 0 }; + SSLHashType hashType; + + SSL_TRC(3, ("%d: TLS13[%d]: recovering static secret (%s)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + /* Now find the hash used as the PRF for the previous handshake. */ + hashType = tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite); + + /* If we are the server, we compute the wrapping key, but if we + * are the client, its coordinates are stored with the ticket. */ + if (ss->sec.isServer) { + wrapKey = ssl3_GetWrappingKey(ss, NULL, + sid->u.ssl3.masterWrapMech, + ss->pkcs11PinArg); + } else { + PK11SlotInfo *slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID, + sid->u.ssl3.masterSlotID); + if (!slot) + return SECFailure; + + wrapKey = PK11_GetWrapKey(slot, + sid->u.ssl3.masterWrapIndex, + sid->u.ssl3.masterWrapMech, + sid->u.ssl3.masterWrapSeries, + ss->pkcs11PinArg); + PK11_FreeSlot(slot); + } + if (!wrapKey) { + return SECFailure; + } + + wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret; + wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len; + + PK11SymKey *unwrappedPsk = ssl_unwrapSymKey(wrapKey, sid->u.ssl3.masterWrapMech, + NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE, + CKA_DERIVE, tls13_GetHashSizeForHash(hashType), + CKF_SIGN | CKF_VERIFY, ss->pkcs11PinArg); + PK11_FreeSymKey(wrapKey); + if (!unwrappedPsk) { + return SECFailure; + } + sslPsk *rpsk = tls13_MakePsk(unwrappedPsk, ssl_psk_resume, hashType, NULL); + if (!rpsk) { + PK11_FreeSymKey(unwrappedPsk); + return SECFailure; + } + if (sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) { + rpsk->maxEarlyData = sid->u.ssl3.locked.sessionTicket.max_early_data_size; + rpsk->zeroRttSuite = sid->u.ssl3.cipherSuite; + } + PRINT_KEY(50, (ss, "Recovered RMS", rpsk->key)); + PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks) || + ((sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks))->type != ssl_psk_resume); + + if (ss->sec.isServer) { + /* In server, we couldn't select the RPSK in the extension handler + * since it was not unwrapped yet. We're committed now, so select + * it and add it to the list (to ensure it is freed). */ + ss->xtnData.selectedPsk = rpsk; + } + PR_APPEND_LINK(&rpsk->link, &ss->ssl3.hs.psks); + + return SECSuccess; +} + +/* Key Derivation Functions. + * + * 0 + * | + * v + * PSK -> HKDF-Extract = Early Secret + * | + * +-----> Derive-Secret(., "ext binder" | "res binder", "") + * | = binder_key + * | + * +-----> Derive-Secret(., "c e traffic", + * | ClientHello) + * | = client_early_traffic_secret + * | + * +-----> Derive-Secret(., "e exp master", + * | ClientHello) + * | = early_exporter_secret + * v + * Derive-Secret(., "derived", "") + * | + * v + *(EC)DHE -> HKDF-Extract = Handshake Secret + * | + * +-----> Derive-Secret(., "c hs traffic", + * | ClientHello...ServerHello) + * | = client_handshake_traffic_secret + * | + * +-----> Derive-Secret(., "s hs traffic", + * | ClientHello...ServerHello) + * | = server_handshake_traffic_secret + * v + * Derive-Secret(., "derived", "") + * | + * v + * 0 -> HKDF-Extract = Master Secret + * | + * +-----> Derive-Secret(., "c ap traffic", + * | ClientHello...Server Finished) + * | = client_traffic_secret_0 + * | + * +-----> Derive-Secret(., "s ap traffic", + * | ClientHello...Server Finished) + * | = server_traffic_secret_0 + * | + * +-----> Derive-Secret(., "exp master", + * | ClientHello...Server Finished) + * | = exporter_secret + * | + * +-----> Derive-Secret(., "res master", + * ClientHello...Client Finished) + * = resumption_master_secret + * + */ +static SECStatus +tls13_ComputeEarlySecretsWithPsk(sslSocket *ss) +{ + SECStatus rv; + + SSL_TRC(5, ("%d: TLS13[%d]: compute early secrets (%s)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + PORT_Assert(!ss->ssl3.hs.currentSecret); + sslPsk *psk = NULL; + + if (ss->sec.isServer) { + psk = ss->xtnData.selectedPsk; + } else { + /* Client to use the first PSK for early secrets. */ + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)); + psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks); + } + PORT_Assert(psk && psk->key); + PORT_Assert(psk->hash != ssl_hash_none); + + PK11SymKey *earlySecret = NULL; + rv = tls13_HkdfExtract(NULL, psk->key, psk->hash, &earlySecret); + if (rv != SECSuccess) { + return SECFailure; + } + + /* No longer need the raw input key */ + PK11_FreeSymKey(psk->key); + psk->key = NULL; + const char *label = (psk->type == ssl_psk_resume) ? kHkdfLabelResPskBinderKey : kHkdfLabelExtPskBinderKey; + rv = tls13_DeriveSecretNullHash(ss, earlySecret, + label, strlen(label), + &psk->binderKey, psk->hash); + if (rv != SECSuccess) { + PK11_FreeSymKey(earlySecret); + return SECFailure; + } + ss->ssl3.hs.currentSecret = earlySecret; + + return SECSuccess; +} + +/* This derives the early traffic and early exporter secrets. */ +static SECStatus +tls13_DeriveEarlySecrets(sslSocket *ss) +{ + SECStatus rv; + PORT_Assert(ss->ssl3.hs.currentSecret); + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, + kHkdfLabelEarlyTrafficSecret, + keylogLabelClientEarlyTrafficSecret, + &ss->ssl3.hs.clientEarlyTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->secretCallback) { + ss->secretCallback(ss->fd, (PRUint16)TrafficKeyEarlyApplicationData, + ss->sec.isServer ? ssl_secret_read : ssl_secret_write, + ss->ssl3.hs.clientEarlyTrafficSecret, + ss->secretCallbackArg); + } + + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + NULL, kHkdfLabelEarlyExporterSecret, + keylogLabelEarlyExporterSecret, + &ss->ssl3.hs.earlyExporterSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_ComputeHandshakeSecret(sslSocket *ss) +{ + SECStatus rv; + PK11SymKey *derivedSecret = NULL; + PK11SymKey *newSecret = NULL; + SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secret (%s)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + /* If no PSK, generate the default early secret. */ + if (!ss->ssl3.hs.currentSecret) { + PORT_Assert(!ss->xtnData.selectedPsk); + rv = tls13_HkdfExtract(NULL, NULL, + tls13_GetHash(ss), &ss->ssl3.hs.currentSecret); + if (rv != SECSuccess) { + return SECFailure; + } + } + PORT_Assert(ss->ssl3.hs.currentSecret); + PORT_Assert(ss->ssl3.hs.dheSecret); + + /* Derive-Secret(., "derived", "") */ + rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelDerivedSecret, + strlen(kHkdfLabelDerivedSecret), + &derivedSecret, tls13_GetHash(ss)); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + + /* HKDF-Extract(ECDHE, .) = Handshake Secret */ + rv = tls13_HkdfExtract(derivedSecret, ss->ssl3.hs.dheSecret, + tls13_GetHash(ss), &newSecret); + PK11_FreeSymKey(derivedSecret); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + + PK11_FreeSymKey(ss->ssl3.hs.currentSecret); + ss->ssl3.hs.currentSecret = newSecret; + return SECSuccess; +} + +static SECStatus +tls13_ComputeHandshakeSecrets(sslSocket *ss) +{ + SECStatus rv; + PK11SymKey *derivedSecret = NULL; + PK11SymKey *newSecret = NULL; + + PK11_FreeSymKey(ss->ssl3.hs.dheSecret); + ss->ssl3.hs.dheSecret = NULL; + + SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secrets (%s)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + /* Now compute |*HsTrafficSecret| */ + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, + kHkdfLabelHandshakeTrafficSecret, + keylogLabelClientHsTrafficSecret, + &ss->ssl3.hs.clientHsTrafficSecret); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelServer, + kHkdfLabelHandshakeTrafficSecret, + keylogLabelServerHsTrafficSecret, + &ss->ssl3.hs.serverHsTrafficSecret); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + + if (ss->secretCallback) { + SSLSecretDirection dir = + ss->sec.isServer ? ssl_secret_read : ssl_secret_write; + ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir, + ss->ssl3.hs.clientHsTrafficSecret, + ss->secretCallbackArg); + dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read; + ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir, + ss->ssl3.hs.serverHsTrafficSecret, + ss->secretCallbackArg); + } + + SSL_TRC(5, ("%d: TLS13[%d]: compute master secret (%s)", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + /* Crank HKDF forward to make master secret, which we + * stuff in current secret. */ + rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelDerivedSecret, + strlen(kHkdfLabelDerivedSecret), + &derivedSecret, tls13_GetHash(ss)); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return rv; + } + rv = tls13_HkdfExtract(derivedSecret, + NULL, + tls13_GetHash(ss), + &newSecret); + PK11_FreeSymKey(derivedSecret); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + PK11_FreeSymKey(ss->ssl3.hs.currentSecret); + ss->ssl3.hs.currentSecret = newSecret; + + return SECSuccess; +} + +static SECStatus +tls13_ComputeApplicationSecrets(sslSocket *ss) +{ + SECStatus rv; + + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelClient, + kHkdfLabelApplicationTrafficSecret, + keylogLabelClientTrafficSecret, + &ss->ssl3.hs.clientTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; + } + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + kHkdfLabelServer, + kHkdfLabelApplicationTrafficSecret, + keylogLabelServerTrafficSecret, + &ss->ssl3.hs.serverTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->secretCallback) { + SSLSecretDirection dir = + ss->sec.isServer ? ssl_secret_read : ssl_secret_write; + ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData, + dir, ss->ssl3.hs.clientTrafficSecret, + ss->secretCallbackArg); + dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read; + ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData, + dir, ss->ssl3.hs.serverTrafficSecret, + ss->secretCallbackArg); + } + + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + NULL, kHkdfLabelExporterMasterSecret, + keylogLabelExporterSecret, + &ss->ssl3.hs.exporterSecret); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_ComputeFinalSecrets(sslSocket *ss) +{ + SECStatus rv; + + PORT_Assert(!ss->ssl3.crSpec->masterSecret); + PORT_Assert(!ss->ssl3.cwSpec->masterSecret); + PORT_Assert(ss->ssl3.hs.currentSecret); + rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret, + NULL, kHkdfLabelResumptionMasterSecret, + NULL, + &ss->ssl3.hs.resumptionMasterSecret); + PK11_FreeSymKey(ss->ssl3.hs.currentSecret); + ss->ssl3.hs.currentSecret = NULL; + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +static void +tls13_RestoreCipherInfo(sslSocket *ss, sslSessionID *sid) +{ + /* Set these to match the cached value. + * TODO(ekr@rtfm.com): Make a version with the "true" values. + * Bug 1256137. + */ + ss->sec.authType = sid->authType; + ss->sec.authKeyBits = sid->authKeyBits; + ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup); + ss->sec.signatureScheme = sid->sigScheme; +} + +/* Check whether resumption-PSK is allowed. */ +static PRBool +tls13_CanResume(sslSocket *ss, const sslSessionID *sid) +{ + const sslServerCert *sc; + + if (!sid) { + return PR_FALSE; + } + + if (sid->version != ss->version) { + return PR_FALSE; + } + +#ifdef UNSAFE_FUZZER_MODE + /* When fuzzing, sid could contain garbage that will crash tls13_GetHashForCipherSuite. + * Do a direct comparison of cipher suites. This makes us refuse to resume when the + * protocol allows it, but resumption is discretionary anyway. */ + if (sid->u.ssl3.cipherSuite != ss->ssl3.hs.cipher_suite) { +#else + if (tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite) != tls13_GetHashForCipherSuite(ss->ssl3.hs.cipher_suite)) { +#endif + return PR_FALSE; + } + + /* Server sids don't remember the server cert we previously sent, but they + * do remember the type of certificate we originally used, so we can locate + * it again, provided that the current ssl socket has had its server certs + * configured the same as the previous one. */ + sc = ssl_FindServerCert(ss, sid->authType, sid->namedCurve); + if (!sc || !sc->serverCert) { + return PR_FALSE; + } + + return PR_TRUE; +} + +static PRBool +tls13_CanNegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid) +{ + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_sent); + sslPsk *psk = ss->xtnData.selectedPsk; + + if (!ss->opt.enable0RttData) { + return PR_FALSE; + } + if (!psk) { + return PR_FALSE; + } + if (psk->zeroRttSuite == TLS_NULL_WITH_NULL_NULL) { + return PR_FALSE; + } + if (!psk->maxEarlyData) { + return PR_FALSE; + } + if (ss->ssl3.hs.cipher_suite != psk->zeroRttSuite) { + return PR_FALSE; + } + if (psk->type == ssl_psk_resume) { + if (!sid) { + return PR_FALSE; + } + PORT_Assert(sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data); + PORT_Assert(ss->statelessResume); + if (!ss->statelessResume) { + return PR_FALSE; + } + if (SECITEM_CompareItem(&ss->xtnData.nextProto, + &sid->u.ssl3.alpnSelection) != 0) { + return PR_FALSE; + } + } else if (psk->type != ssl_psk_external) { + PORT_Assert(0); + return PR_FALSE; + } + + if (tls13_IsReplay(ss, sid)) { + return PR_FALSE; + } + + return PR_TRUE; +} + +/* Called from tls13_HandleClientHelloPart2 to update the state of 0-RTT handling. + * + * 0-RTT is only permitted if: + * 1. The early data extension was present. + * 2. We are resuming a session. + * 3. The 0-RTT option is set. + * 4. The ticket allowed 0-RTT. + * 5. We negotiated the same ALPN value as in the ticket. + */ +static void +tls13_NegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid) +{ + SSL_TRC(3, ("%d: TLS13[%d]: negotiate 0-RTT %p", + SSL_GETPID(), ss->fd, sid)); + + /* tls13_ServerHandleEarlyDataXtn sets this to ssl_0rtt_sent, so this will + * be ssl_0rtt_none unless early_data is present. */ + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_none) { + return; + } + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored) { + /* HelloRetryRequest causes 0-RTT to be ignored. On the second + * ClientHello, reset the ignore state so that decryption failure is + * handled normally. */ + if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr) { + PORT_Assert(ss->ssl3.hs.helloRetry); + ss->ssl3.hs.zeroRttState = ssl_0rtt_none; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none; + } else { + SSL_TRC(3, ("%d: TLS13[%d]: application ignored 0-RTT", + SSL_GETPID(), ss->fd)); + } + return; + } + + if (!tls13_CanNegotiateZeroRtt(ss, sid)) { + SSL_TRC(3, ("%d: TLS13[%d]: ignore 0-RTT", SSL_GETPID(), ss->fd)); + ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial; + return; + } + + SSL_TRC(3, ("%d: TLS13[%d]: enable 0-RTT", SSL_GETPID(), ss->fd)); + PORT_Assert(ss->xtnData.selectedPsk); + ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none; + ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite; + ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_0rtt_cipher_suite; +} + +/* Check if the offered group is acceptable. */ +static PRBool +tls13_isGroupAcceptable(const sslNamedGroupDef *offered, + const sslNamedGroupDef *preferredGroup) +{ + /* We accept epsilon (e) bits around the offered group size. */ + const unsigned int e = 2; + + PORT_Assert(offered); + PORT_Assert(preferredGroup); + + if (offered->bits >= preferredGroup->bits - e && + offered->bits <= preferredGroup->bits + e) { + return PR_TRUE; + } + + return PR_FALSE; +} + +/* Find remote key share for given group and return it. + * Returns NULL if no key share is found. */ +static TLS13KeyShareEntry * +tls13_FindKeyShareEntry(sslSocket *ss, const sslNamedGroupDef *group) +{ + PRCList *cur_p = PR_NEXT_LINK(&ss->xtnData.remoteKeyShares); + while (cur_p != &ss->xtnData.remoteKeyShares) { + TLS13KeyShareEntry *offer = (TLS13KeyShareEntry *)cur_p; + if (offer->group == group) { + return offer; + } + cur_p = PR_NEXT_LINK(cur_p); + } + return NULL; +} + +static SECStatus +tls13_NegotiateKeyExchange(sslSocket *ss, + const sslNamedGroupDef **requestedGroup, + TLS13KeyShareEntry **clientShare) +{ + unsigned int index; + TLS13KeyShareEntry *entry = NULL; + const sslNamedGroupDef *preferredGroup = NULL; + + /* We insist on DHE. */ + if (ssl3_ExtensionNegotiated(ss, ssl_tls13_pre_shared_key_xtn)) { + if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_psk_key_exchange_modes_xtn)) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES, + missing_extension); + return SECFailure; + } + /* Since the server insists on DHE to provide forward secracy, for + * every other PskKem value but DHE stateless resumption is disabled, + * this includes other specified and GREASE values. */ + if (!memchr(ss->xtnData.psk_ke_modes.data, tls13_psk_dh_ke, + ss->xtnData.psk_ke_modes.len)) { + SSL_TRC(3, ("%d: TLS13[%d]: client offered PSK without DH", + SSL_GETPID(), ss->fd)); + ss->statelessResume = PR_FALSE; + } + } + + /* Now figure out which key share we like the best out of the + * mutually supported groups, regardless of what the client offered + * for key shares. + */ + if (!ssl3_ExtensionNegotiated(ss, ssl_supported_groups_xtn)) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_SUPPORTED_GROUPS_EXTENSION, + missing_extension); + return SECFailure; + } + + SSL_TRC(3, ("%d: TLS13[%d]: selected KE = %s", SSL_GETPID(), + ss->fd, ss->statelessResume || ss->xtnData.selectedPsk ? "PSK + (EC)DHE" : "(EC)DHE")); + + /* Find the preferred group and an according client key share available. */ + for (index = 0; index < SSL_NAMED_GROUP_COUNT; ++index) { + /* Continue to the next group if this one is not enabled. */ + if (!ss->namedGroupPreferences[index]) { + /* There's a gap in the preferred groups list. Assume this is a group + * that's not supported by the client but preferred by the server. */ + if (preferredGroup) { + entry = NULL; + break; + } + continue; + } + + /* Check if the client sent a key share for this group. */ + entry = tls13_FindKeyShareEntry(ss, ss->namedGroupPreferences[index]); + + if (preferredGroup) { + /* We already found our preferred group but the group didn't have a share. */ + if (entry) { + /* The client sent a key share with group ss->namedGroupPreferences[index] */ + if (tls13_isGroupAcceptable(ss->namedGroupPreferences[index], + preferredGroup)) { + /* This is not the preferred group, but it's acceptable */ + preferredGroup = ss->namedGroupPreferences[index]; + } else { + /* The proposed group is not acceptable. */ + entry = NULL; + } + } + break; + } else { + /* The first enabled group is the preferred group. */ + preferredGroup = ss->namedGroupPreferences[index]; + if (entry) { + break; + } + } + } + + if (!preferredGroup) { + FATAL_ERROR(ss, SSL_ERROR_NO_CYPHER_OVERLAP, handshake_failure); + return SECFailure; + } + SSL_TRC(3, ("%d: TLS13[%d]: group = %d", SSL_GETPID(), ss->fd, + preferredGroup->name)); + + /* Either provide a share, or provide a group that should be requested in a + * HelloRetryRequest, but not both. */ + if (entry) { + PORT_Assert(preferredGroup == entry->group); + *clientShare = entry; + *requestedGroup = NULL; + } else { + *clientShare = NULL; + *requestedGroup = preferredGroup; + } + return SECSuccess; +} + +SECStatus +tls13_SelectServerCert(sslSocket *ss) +{ + PRCList *cursor; + SECStatus rv; + + if (!ssl3_ExtensionNegotiated(ss, ssl_signature_algorithms_xtn)) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION, + missing_extension); + return SECFailure; + } + + /* This picks the first certificate that has: + * a) the right authentication method, and + * b) the right named curve (EC only) + * + * We might want to do some sort of ranking here later. For now, it's all + * based on what order they are configured in. */ + for (cursor = PR_NEXT_LINK(&ss->serverCerts); + cursor != &ss->serverCerts; + cursor = PR_NEXT_LINK(cursor)) { + sslServerCert *cert = (sslServerCert *)cursor; + + if (SSL_CERT_IS_ONLY(cert, ssl_auth_rsa_decrypt)) { + continue; + } + + rv = ssl_PickSignatureScheme(ss, + cert->serverCert, + cert->serverKeyPair->pubKey, + cert->serverKeyPair->privKey, + ss->xtnData.sigSchemes, + ss->xtnData.numSigSchemes, + PR_FALSE, + &ss->ssl3.hs.signatureScheme); + if (rv == SECSuccess) { + /* Found one. */ + ss->sec.serverCert = cert; + + /* If we can use a delegated credential (DC) for authentication in + * the current handshake, then commit to using it now. We'll send a + * DC as an extension and use the DC private key to sign the + * handshake. + * + * This sets the signature scheme to be the signature scheme + * indicated by the DC. + */ + rv = tls13_MaybeSetDelegatedCredential(ss); + if (rv != SECSuccess) { + return SECFailure; /* Failure indicates an internal error. */ + } + + ss->sec.authType = ss->ssl3.hs.kea_def_mutable.authKeyType = + ssl_SignatureSchemeToAuthType(ss->ssl3.hs.signatureScheme); + ss->sec.authKeyBits = cert->serverKeyBits; + return SECSuccess; + } + } + + FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM, + handshake_failure); + return SECFailure; +} + +/* Note: |requestedGroup| is non-NULL when we send a key_share extension. */ +static SECStatus +tls13_MaybeSendHelloRetry(sslSocket *ss, const sslNamedGroupDef *requestedGroup, + PRBool *hrrSent) +{ + SSLHelloRetryRequestAction action = ssl_hello_retry_accept; + PRUint8 token[256] = { 0 }; + unsigned int tokenLen = 0; + SECStatus rv; + + if (ss->hrrCallback) { + action = ss->hrrCallback(!ss->ssl3.hs.helloRetry, + ss->xtnData.applicationToken.data, + ss->xtnData.applicationToken.len, + token, &tokenLen, sizeof(token), + ss->hrrCallbackArg); + } + + /* These use SSL3_SendAlert directly to avoid an assertion in + * tls13_FatalError(), which is ordinarily OK. */ + if (action == ssl_hello_retry_request && ss->ssl3.hs.helloRetry) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR); + return SECFailure; + } + + if (action != ssl_hello_retry_request && tokenLen) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR); + return SECFailure; + } + + if (tokenLen > sizeof(token)) { + (void)SSL3_SendAlert(ss, alert_fatal, internal_error); + PORT_SetError(SSL_ERROR_APP_CALLBACK_ERROR); + return SECFailure; + } + + if (action == ssl_hello_retry_fail) { + FATAL_ERROR(ss, SSL_ERROR_APPLICATION_ABORT, handshake_failure); + return SECFailure; + } + + if (action == ssl_hello_retry_reject_0rtt) { + ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial; + } + + if (!requestedGroup && action != ssl_hello_retry_request) { + return SECSuccess; + } + + rv = tls13_SendHelloRetryRequest(ss, requestedGroup, token, tokenLen); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + /* We may have received ECH, but have to start over with CH2. */ + ss->ssl3.hs.echAccepted = PR_FALSE; + PK11_HPKE_DestroyContext(ss->ssl3.hs.echHpkeCtx, PR_TRUE); + ss->ssl3.hs.echHpkeCtx = NULL; + + *hrrSent = PR_TRUE; + return SECSuccess; +} + +static SECStatus +tls13_NegotiateAuthentication(sslSocket *ss) +{ + if (ss->statelessResume) { + SSL_TRC(3, ("%d: TLS13[%d]: selected resumption PSK authentication", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.signatureScheme = ssl_sig_none; + ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_auth_psk; + /* Overwritten by tls13_RestoreCipherInfo. */ + ss->sec.authType = ssl_auth_psk; + return SECSuccess; + } else if (ss->xtnData.selectedPsk) { + /* If the EPSK doesn't specify a suite, use what was negotiated. + * Else, only use the EPSK if we negotiated that suite. */ + if (ss->xtnData.selectedPsk->zeroRttSuite == TLS_NULL_WITH_NULL_NULL || + ss->ssl3.hs.cipher_suite == ss->xtnData.selectedPsk->zeroRttSuite) { + SSL_TRC(3, ("%d: TLS13[%d]: selected external PSK authentication", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.signatureScheme = ssl_sig_none; + ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_auth_psk; + ss->sec.authType = ssl_auth_psk; + return SECSuccess; + } + } + + /* If there were PSKs, they are no longer needed. */ + if (ss->xtnData.selectedPsk) { + tls13_DestroyPskList(&ss->ssl3.hs.psks); + ss->xtnData.selectedPsk = NULL; + } + + SSL_TRC(3, ("%d: TLS13[%d]: selected certificate authentication", + SSL_GETPID(), ss->fd)); + SECStatus rv = tls13_SelectServerCert(ss); + if (rv != SECSuccess) { + return SECFailure; + } + return SECSuccess; +} +/* Called from ssl3_HandleClientHello after we have parsed the + * ClientHello and are sure that we are going to do TLS 1.3 + * or fail. */ +SECStatus +tls13_HandleClientHelloPart2(sslSocket *ss, + const SECItem *suites, + sslSessionID *sid, + const PRUint8 *msg, + unsigned int len) +{ + SECStatus rv; + SSL3Statistics *ssl3stats = SSL_GetStatistics(); + const sslNamedGroupDef *requestedGroup = NULL; + TLS13KeyShareEntry *clientShare = NULL; + ssl3CipherSuite previousCipherSuite = 0; + const sslNamedGroupDef *previousGroup = NULL; + PRBool hrr = PR_FALSE; + PRBool previousOfferedEch; + + /* If the legacy_version field is set to 0x300 or smaller, + * reject the connection with protocol_version alert. */ + if (ss->clientHelloVersion <= SSL_LIBRARY_VERSION_3_0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, protocol_version); + goto loser; + } + + ss->ssl3.hs.endOfFlight = PR_TRUE; + + if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) { + ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; + } + + /* Negotiate cipher suite. */ + rv = ssl3_NegotiateCipherSuite(ss, suites, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), handshake_failure); + goto loser; + } + + /* If we are going around again, then we should make sure that the cipher + * suite selection doesn't change. That's a sign of client shennanigans. */ + if (ss->ssl3.hs.helloRetry) { + + /* Update sequence numbers before checking the cookie so that any alerts + * we generate are sent with the right sequence numbers. */ + if (IS_DTLS(ss)) { + /* Count the first ClientHello and the HelloRetryRequest. */ + ss->ssl3.hs.sendMessageSeq = 1; + ss->ssl3.hs.recvMessageSeq = 1; + ssl_GetSpecWriteLock(ss); + /* Increase the write sequence number. The read sequence number + * will be reset after this to early data or handshake. */ + ss->ssl3.cwSpec->nextSeqNum = 1; + ssl_ReleaseSpecWriteLock(ss); + } + + if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_cookie_xtn) || + !ss->xtnData.cookie.len) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_COOKIE_EXTENSION, + missing_extension); + goto loser; + } + PRINT_BUF(50, (ss, "Client sent cookie", + ss->xtnData.cookie.data, ss->xtnData.cookie.len)); + + rv = tls13_HandleHrrCookie(ss, ss->xtnData.cookie.data, + ss->xtnData.cookie.len, + &previousCipherSuite, + &previousGroup, + &previousOfferedEch, NULL, PR_TRUE); + + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, illegal_parameter); + goto loser; + } + } + + /* Now merge the ClientHello into the hash state. */ + rv = ssl_HashHandshakeMessage(ss, ssl_hs_client_hello, msg, len); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + + /* Now create a synthetic kea_def that we can tweak. */ + ss->ssl3.hs.kea_def_mutable = *ss->ssl3.hs.kea_def; + ss->ssl3.hs.kea_def = &ss->ssl3.hs.kea_def_mutable; + + /* Note: We call this quite a bit earlier than with TLS 1.2 and + * before. */ + rv = ssl3_ServerCallSNICallback(ss); + if (rv != SECSuccess) { + goto loser; /* An alert has already been sent. */ + } + + /* Check if we could in principle resume. */ + if (ss->statelessResume) { + PORT_Assert(sid); + if (!sid) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + if (!tls13_CanResume(ss, sid)) { + ss->statelessResume = PR_FALSE; + } + } + + /* Select key exchange. */ + rv = tls13_NegotiateKeyExchange(ss, &requestedGroup, &clientShare); + if (rv != SECSuccess) { + goto loser; + } + /* We should get either one of these, but not both. */ + PORT_Assert((requestedGroup && !clientShare) || + (!requestedGroup && clientShare)); + + /* After HelloRetryRequest, check consistency of cipher and group. */ + if (ss->ssl3.hs.helloRetry) { + PORT_Assert(previousCipherSuite); + if (ss->ssl3.hs.cipher_suite != previousCipherSuite) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, + illegal_parameter); + goto loser; + } + if (!clientShare) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, + illegal_parameter); + goto loser; + } + + /* CH1/CH2 must either both include ECH, or both exclude it. */ + if (previousOfferedEch != (ss->xtnData.ech != NULL)) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, + previousOfferedEch ? missing_extension : illegal_parameter); + goto loser; + } + + /* If we requested a new key share, check that the client provided just + * one of the right type. */ + if (previousGroup) { + if (PR_PREV_LINK(&ss->xtnData.remoteKeyShares) != + PR_NEXT_LINK(&ss->xtnData.remoteKeyShares)) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, + illegal_parameter); + goto loser; + } + if (clientShare->group != previousGroup) { + FATAL_ERROR(ss, SSL_ERROR_BAD_2ND_CLIENT_HELLO, + illegal_parameter); + goto loser; + } + } + } + + rv = tls13_MaybeSendHelloRetry(ss, requestedGroup, &hrr); + if (rv != SECSuccess) { + goto loser; + } + if (hrr) { + if (sid) { /* Free the sid. */ + ssl_UncacheSessionID(ss); + ssl_FreeSID(sid); + } + PORT_Assert(ss->ssl3.hs.helloRetry); + return SECSuccess; + } + + /* Select the authentication (this is also handshake shape). */ + rv = tls13_NegotiateAuthentication(ss); + if (rv != SECSuccess) { + goto loser; + } + + if (ss->sec.authType == ssl_auth_psk) { + if (ss->statelessResume) { + /* We are now committed to trying to resume. */ + PORT_Assert(sid); + /* Check that the negotiated SNI and the cached SNI match. */ + if (SECITEM_CompareItem(&sid->u.ssl3.srvName, + &ss->ssl3.hs.srvVirtName) != SECEqual) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, + handshake_failure); + goto loser; + } + + ss->sec.serverCert = ssl_FindServerCert(ss, sid->authType, + sid->namedCurve); + PORT_Assert(ss->sec.serverCert); + + rv = tls13_RecoverWrappedSharedSecret(ss, sid); + if (rv != SECSuccess) { + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok); + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + tls13_RestoreCipherInfo(ss, sid); + + ss->sec.localCert = CERT_DupCertificate(ss->sec.serverCert->serverCert); + if (sid->peerCert != NULL) { + ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); + } + } else if (sid) { + /* We should never have a SID in the non-resumption case. */ + PORT_Assert(0); + ssl_UncacheSessionID(ss); + ssl_FreeSID(sid); + sid = NULL; + } + ssl3_RegisterExtensionSender( + ss, &ss->xtnData, + ssl_tls13_pre_shared_key_xtn, tls13_ServerSendPreSharedKeyXtn); + tls13_NegotiateZeroRtt(ss, sid); + + rv = tls13_ComputeEarlySecretsWithPsk(ss); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + } else { + if (sid) { /* we had a sid, but it's no longer valid, free it */ + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok); + ssl_UncacheSessionID(ss); + ssl_FreeSID(sid); + sid = NULL; + } + tls13_NegotiateZeroRtt(ss, NULL); + } + + if (ss->statelessResume) { + PORT_Assert(ss->xtnData.selectedPsk); + PORT_Assert(ss->ssl3.hs.kea_def_mutable.authKeyType == ssl_auth_psk); + } + + /* Now that we have the binder key, check the binder. */ + if (ss->xtnData.selectedPsk) { + SSL3Hashes hashes; + PORT_Assert(ss->ssl3.hs.messages.len > ss->xtnData.pskBindersLen); + rv = tls13_ComputePskBinderHash( + ss, + ss->ssl3.hs.messages.buf, + ss->ssl3.hs.messages.len - ss->xtnData.pskBindersLen, + &hashes, tls13_GetHash(ss)); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + + PORT_Assert(ss->xtnData.selectedPsk->hash == tls13_GetHash(ss)); + PORT_Assert(ss->ssl3.hs.suite_def); + rv = tls13_VerifyFinished(ss, ssl_hs_client_hello, + ss->xtnData.selectedPsk->binderKey, + ss->xtnData.pskBinder.data, + ss->xtnData.pskBinder.len, + &hashes); + } + if (rv != SECSuccess) { + goto loser; + } + + /* This needs to go after we verify the psk binder. */ + rv = ssl3_InitHandshakeHashes(ss); + if (rv != SECSuccess) { + goto loser; + } + + /* If this is TLS 1.3 we are expecting a ClientKeyShare + * extension. Missing/absent extension cause failure + * below. */ + rv = tls13_HandleClientKeyShare(ss, clientShare); + if (rv != SECSuccess) { + goto loser; /* An alert was sent already. */ + } + + /* From this point we are either committed to resumption, or not. */ + if (ss->statelessResume) { + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_hits); + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_stateless_resumes); + } else { + if (sid) { + /* We had a sid, but it's no longer valid, free it. */ + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_not_ok); + ssl_UncacheSessionID(ss); + ssl_FreeSID(sid); + } else if (!ss->xtnData.selectedPsk) { + SSL_AtomicIncrementLong(&ssl3stats->hch_sid_cache_misses); + } + + sid = ssl3_NewSessionID(ss, PR_TRUE); + if (!sid) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; + } + } + /* Take ownership of the session. */ + ss->sec.ci.sid = sid; + sid = NULL; + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { + rv = tls13_DeriveEarlySecrets(ss); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + } + + ssl_GetXmitBufLock(ss); + rv = tls13_SendServerHelloSequence(ss); + ssl_ReleaseXmitBufLock(ss); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), handshake_failure); + return SECFailure; + } + + /* We're done with PSKs */ + tls13_DestroyPskList(&ss->ssl3.hs.psks); + ss->xtnData.selectedPsk = NULL; + + return SECSuccess; + +loser: + if (sid) { + ssl_UncacheSessionID(ss); + ssl_FreeSID(sid); + } + return SECFailure; +} + +SECStatus +SSLExp_HelloRetryRequestCallback(PRFileDesc *fd, + SSLHelloRetryRequestCallback cb, void *arg) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; /* Code already set. */ + } + + ss->hrrCallback = cb; + ss->hrrCallbackArg = arg; + return SECSuccess; +} + +/* + * struct { + * ProtocolVersion server_version; + * CipherSuite cipher_suite; + * Extension extensions<2..2^16-1>; + * } HelloRetryRequest; + * + * Note: this function takes an empty buffer and returns + * a non-empty one on success, in which case the caller must + * eventually clean up. + */ +SECStatus +tls13_ConstructHelloRetryRequest(sslSocket *ss, + ssl3CipherSuite cipherSuite, + const sslNamedGroupDef *selectedGroup, + PRUint8 *cookie, unsigned int cookieLen, + const PRUint8 *cookieGreaseEchSignal, + sslBuffer *buffer) +{ + SECStatus rv; + sslBuffer extensionsBuf = SSL_BUFFER_EMPTY; + PORT_Assert(buffer->len == 0); + + /* Note: cookie is pointing to a stack variable, so is only valid + * now. */ + ss->xtnData.selectedGroup = selectedGroup; + ss->xtnData.cookie.data = cookie; + ss->xtnData.cookie.len = cookieLen; + + /* Set restored ss->ssl3.hs.greaseEchBuf value for ECH HRR extension + * reconstruction. */ + if (cookieGreaseEchSignal) { + PORT_Assert(!ss->ssl3.hs.greaseEchBuf.len); + rv = sslBuffer_Append(&ss->ssl3.hs.greaseEchBuf, + cookieGreaseEchSignal, + TLS13_ECH_SIGNAL_LEN); + if (rv != SECSuccess) { + goto loser; + } + } + rv = ssl_ConstructExtensions(ss, &extensionsBuf, + ssl_hs_hello_retry_request); + /* Reset ss->ssl3.hs.greaseEchBuf if it was changed. */ + if (cookieGreaseEchSignal) { + sslBuffer_Clear(&ss->ssl3.hs.greaseEchBuf); + } + if (rv != SECSuccess) { + goto loser; + } + /* These extensions can't be empty. */ + PORT_Assert(SSL_BUFFER_LEN(&extensionsBuf) > 0); + + /* Clean up cookie so we're not pointing at random memory. */ + ss->xtnData.cookie.data = NULL; + ss->xtnData.cookie.len = 0; + + rv = ssl_ConstructServerHello(ss, PR_TRUE, &extensionsBuf, buffer); + if (rv != SECSuccess) { + goto loser; + } + sslBuffer_Clear(&extensionsBuf); + return SECSuccess; + +loser: + sslBuffer_Clear(&extensionsBuf); + sslBuffer_Clear(buffer); + return SECFailure; +} + +static SECStatus +tls13_SendHelloRetryRequest(sslSocket *ss, + const sslNamedGroupDef *requestedGroup, + const PRUint8 *appToken, unsigned int appTokenLen) +{ + SECStatus rv; + unsigned int cookieLen; + PRUint8 cookie[1024]; + sslBuffer messageBuf = SSL_BUFFER_EMPTY; + + SSL_TRC(3, ("%d: TLS13[%d]: send hello retry request handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* If an ECH backend or shared-mode server accepted ECH when offered, + * the HRR extension's payload must be set to 8 zero bytes, these are + * overwritten with the accept_confirmation value after the handshake + * transcript calculation. + * If a client-facing or shared-mode server did not accept ECH when offered + * OR if ECH GREASE is enabled on the server and a ECH extension was + * received, a 8 byte random value is set as the extension's payload + * [draft-ietf-tls-esni-14, Section 7]. + * + * The (temporary) payload is written to the extension in tls13exthandle.c/ + * tls13_ServerSendHrrEchXtn(). */ + if (ss->xtnData.ech) { + PRUint8 echGreaseRaw[TLS13_ECH_SIGNAL_LEN] = { 0 }; + if (!(ss->ssl3.hs.echAccepted || + (ss->opt.enableTls13BackendEch && + ss->xtnData.ech && + ss->xtnData.ech->receivedInnerXtn))) { + rv = PK11_GenerateRandom(echGreaseRaw, TLS13_ECH_SIGNAL_LEN); + if (rv != SECSuccess) { + return SECFailure; + } + SSL_TRC(100, ("Generated random value for ECH HRR GREASE.")); + } + sslBuffer echGreaseBuffer = SSL_BUFFER_EMPTY; + rv = sslBuffer_Append(&echGreaseBuffer, echGreaseRaw, sizeof(echGreaseRaw)); + if (rv != SECSuccess) { + return SECFailure; + } + /* HRR GREASE/accept_confirmation zero bytes placeholder buffer. */ + ss->ssl3.hs.greaseEchBuf = echGreaseBuffer; + } + + /* Compute the cookie we are going to need. */ + rv = tls13_MakeHrrCookie(ss, requestedGroup, + appToken, appTokenLen, + cookie, &cookieLen, sizeof(cookie)); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + /* Now build the body of the message. */ + rv = tls13_ConstructHelloRetryRequest(ss, ss->ssl3.hs.cipher_suite, + requestedGroup, + cookie, cookieLen, + NULL, &messageBuf); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + /* And send it. */ + ssl_GetXmitBufLock(ss); + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_server_hello, + SSL_BUFFER_LEN(&messageBuf)); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl3_AppendBufferToHandshake(ss, &messageBuf); + if (rv != SECSuccess) { + goto loser; + } + sslBuffer_Clear(&messageBuf); /* Done with messageBuf */ + + if (ss->ssl3.hs.fakeSid.len) { + PRInt32 sent; + + PORT_Assert(!IS_DTLS(ss)); + rv = ssl3_SendChangeCipherSpecsInt(ss); + if (rv != SECSuccess) { + goto loser; + } + /* ssl3_SendChangeCipherSpecsInt() only flushes to the output buffer, so we + * have to force a send. */ + sent = ssl_SendSavedWriteData(ss); + if (sent < 0 && PORT_GetError() != PR_WOULD_BLOCK_ERROR) { + PORT_SetError(SSL_ERROR_SOCKET_WRITE_FAILURE); + goto loser; + } + } else { + rv = ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + goto loser; /* error code set by ssl3_FlushHandshake */ + } + } + + /* We depend on this being exactly one record and one message. */ + PORT_Assert(!IS_DTLS(ss) || (ss->ssl3.hs.sendMessageSeq == 1 && + ss->ssl3.cwSpec->nextSeqNum == 1)); + ssl_ReleaseXmitBufLock(ss); + + ss->ssl3.hs.helloRetry = PR_TRUE; + + /* We received early data but have to ignore it because we sent a retry. */ + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) { + ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_hrr; + } + + return SECSuccess; + +loser: + sslBuffer_Clear(&messageBuf); + ssl_ReleaseXmitBufLock(ss); + return SECFailure; +} + +/* Called from tls13_HandleClientHello. + * + * Caller must hold Handshake and RecvBuf locks. + */ + +static SECStatus +tls13_HandleClientKeyShare(sslSocket *ss, TLS13KeyShareEntry *peerShare) +{ + SECStatus rv; + sslEphemeralKeyPair *keyPair; /* ours */ + SECItem *ciphertext = NULL; + PK11SymKey *dheSecret = NULL; + PK11SymKey *kemSecret = NULL; + + SSL_TRC(3, ("%d: TLS13[%d]: handle client_key_share handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(peerShare); + + tls13_SetKeyExchangeType(ss, peerShare->group); + + /* Generate our key */ + rv = tls13_AddKeyShare(ss, peerShare->group); + if (rv != SECSuccess) { + return rv; + } + + /* We should have exactly one key share. */ + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)); + PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) == + PR_NEXT_LINK(&ss->ephemeralKeyPairs)); + + keyPair = ((sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs)); + ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->keys->pubKey); + + /* Register the sender */ + rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, ssl_tls13_key_share_xtn, + tls13_ServerSendKeyShareXtn); + if (rv != SECSuccess) { + return SECFailure; /* Error code set already. */ + } + + rv = tls13_HandleKeyShare(ss, peerShare, keyPair->keys, + tls13_GetHash(ss), + &dheSecret); + if (rv != SECSuccess) { + goto loser; /* Error code already set. */ + } + + if (peerShare->group->keaType == ssl_kea_ecdh_hybrid) { + rv = tls13_HandleKEMKey(ss, peerShare, &kemSecret, &ciphertext); + if (rv != SECSuccess) { + goto loser; /* Error set by tls13_HandleKEMKey */ + } + // We may need to handle different "combiners" here in the future. For + // now this is specific to xyber768d00. + PORT_Assert(peerShare->group->name == ssl_grp_kem_xyber768d00); + ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); + if (!ss->ssl3.hs.dheSecret) { + goto loser; /* Error set by PK11_ConcatSymKeys */ + } + keyPair->kemCt = ciphertext; + PK11_FreeSymKey(dheSecret); + PK11_FreeSymKey(kemSecret); + } else { + ss->ssl3.hs.dheSecret = dheSecret; + } + + return SECSuccess; + +loser: + SECITEM_FreeItem(ciphertext, PR_TRUE); + PK11_FreeSymKey(dheSecret); + PK11_FreeSymKey(kemSecret); + FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); + return SECFailure; +} + +/* + * [draft-ietf-tls-tls13-11] Section 6.3.3.2 + * + * opaque DistinguishedName<1..2^16-1>; + * + * struct { + * opaque certificate_extension_oid<1..2^8-1>; + * opaque certificate_extension_values<0..2^16-1>; + * } CertificateExtension; + * + * struct { + * opaque certificate_request_context<0..2^8-1>; + * SignatureAndHashAlgorithm + * supported_signature_algorithms<2..2^16-2>; + * DistinguishedName certificate_authorities<0..2^16-1>; + * CertificateExtension certificate_extensions<0..2^16-1>; + * } CertificateRequest; + */ +static SECStatus +tls13_SendCertificateRequest(sslSocket *ss) +{ + SECStatus rv; + sslBuffer extensionBuf = SSL_BUFFER_EMPTY; + unsigned int offset = 0; + + SSL_TRC(3, ("%d: TLS13[%d]: begin send certificate_request", + SSL_GETPID(), ss->fd)); + + if (ss->firstHsDone) { + PORT_Assert(ss->ssl3.hs.shaPostHandshake == NULL); + ss->ssl3.hs.shaPostHandshake = PK11_CloneContext(ss->ssl3.hs.sha); + if (ss->ssl3.hs.shaPostHandshake == NULL) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + } + + rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_certificate_request); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + /* We should always have at least one of these. */ + PORT_Assert(SSL_BUFFER_LEN(&extensionBuf) > 0); + + /* Create a new request context for post-handshake authentication */ + if (ss->firstHsDone) { + PRUint8 context[16]; + SECItem contextItem = { siBuffer, context, sizeof(context) }; + + rv = PK11_GenerateRandom(context, sizeof(context)); + if (rv != SECSuccess) { + goto loser; + } + + SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE); + rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &contextItem); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + goto loser; + } + + offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf); + } + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_request, + 1 + /* request context length */ + ss->xtnData.certReqContext.len + + 2 + /* extension length */ + SSL_BUFFER_LEN(&extensionBuf)); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + /* Context. */ + rv = ssl3_AppendHandshakeVariable(ss, ss->xtnData.certReqContext.data, + ss->xtnData.certReqContext.len, 1); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + /* Extensions. */ + rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensionBuf, 2); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + if (ss->firstHsDone) { + rv = ssl3_UpdatePostHandshakeHashes(ss, + SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset, + SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset); + if (rv != SECSuccess) { + goto loser; + } + } + + sslBuffer_Clear(&extensionBuf); + return SECSuccess; + +loser: + sslBuffer_Clear(&extensionBuf); + return SECFailure; +} + +/* [draft-ietf-tls-tls13; S 4.4.1] says: + * + * Transcript-Hash(ClientHello1, HelloRetryRequest, ... MN) = + * Hash(message_hash || // Handshake type + * 00 00 Hash.length || // Handshake message length + * Hash(ClientHello1) || // Hash of ClientHello1 + * HelloRetryRequest ... MN) + * + * For an ECH handshake, the process occurs for the outer + * transcript in |ss->ssl3.hs.messages| and the inner + * transcript in |ss->ssl3.hs.echInnerMessages|. + */ +static SECStatus +tls13_ReinjectHandshakeTranscript(sslSocket *ss) +{ + SSL3Hashes hashes; + SSL3Hashes echInnerHashes; + SECStatus rv; + + /* First compute the hash. */ + rv = tls13_ComputeHash(ss, &hashes, + ss->ssl3.hs.messages.buf, + ss->ssl3.hs.messages.len, + tls13_GetHash(ss)); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->ssl3.hs.echHpkeCtx) { + rv = tls13_ComputeHash(ss, &echInnerHashes, + ss->ssl3.hs.echInnerMessages.buf, + ss->ssl3.hs.echInnerMessages.len, + tls13_GetHash(ss)); + if (rv != SECSuccess) { + return SECFailure; + } + } + + ssl3_RestartHandshakeHashes(ss); + + /* Reinject the message. The Default context variant updates + * the default hash state. Use it for both non-ECH and ECH Outer. */ + rv = ssl_HashHandshakeMessageDefault(ss, ssl_hs_message_hash, + hashes.u.raw, hashes.len); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->ssl3.hs.echHpkeCtx) { + rv = ssl_HashHandshakeMessageEchInner(ss, ssl_hs_message_hash, + echInnerHashes.u.raw, + echInnerHashes.len); + if (rv != SECSuccess) { + return SECFailure; + } + } + + return SECSuccess; +} +static unsigned int +ssl_ListCount(PRCList *list) +{ + unsigned int c = 0; + PRCList *cur; + for (cur = PR_NEXT_LINK(list); cur != list; cur = PR_NEXT_LINK(cur)) { + ++c; + } + return c; +} + +/* + * savedMsg contains the HelloRetryRequest message. When its extensions are parsed + * in ssl3_HandleParsedExtensions, the handler for ECH HRR extensions (tls13_ClientHandleHrrEchXtn) + * will take a reference into the message buffer. + * + * This reference is then used in tls13_MaybeHandleEchSignal in order to compute + * the transcript for the ECH signal calculation. This was felt to be preferable + * to re-parsing the HelloRetryRequest message in order to create the transcript. + * + * Consequently, savedMsg should not be moved or mutated between these + * function calls. + */ +SECStatus +tls13_HandleHelloRetryRequest(sslSocket *ss, const PRUint8 *savedMsg, + PRUint32 savedLength) +{ + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: handle hello retry request", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { + FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_HELLO_RETRY_REQUEST, + unexpected_message); + return SECFailure; + } + PORT_Assert(ss->ssl3.hs.ws == wait_server_hello); + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) { + ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored; + /* Restore the null cipher spec for writing. */ + ssl_GetSpecWriteLock(ss); + ssl_CipherSpecRelease(ss->ssl3.cwSpec); + ss->ssl3.cwSpec = ssl_FindCipherSpecByEpoch(ss, ssl_secret_write, + TrafficKeyClearText); + PORT_Assert(ss->ssl3.cwSpec); + ssl_ReleaseSpecWriteLock(ss); + } else { + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none); + } + /* Set the spec version, because we want to send CH now with 0303 */ + tls13_SetSpecRecordVersion(ss, ss->ssl3.cwSpec); + + /* Extensions must contain more than just supported_versions. This will + * ensure that a HelloRetryRequest isn't a no-op: we must have at least two + * extensions, supported_versions plus one other. That other must be one + * that we understand and recognize as being valid for HelloRetryRequest, + * and should alter our next Client Hello. */ + unsigned int requiredExtensions = 1; + /* The ECH HRR extension is a no-op from the client's perspective. */ + if (ss->xtnData.ech) { + requiredExtensions++; + } + if (ssl_ListCount(&ss->ssl3.hs.remoteExtensions) <= requiredExtensions) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST, + decode_error); + return SECFailure; + } + + rv = ssl3_HandleParsedExtensions(ss, ssl_hs_hello_retry_request); + ssl3_DestroyRemoteExtensions(&ss->ssl3.hs.remoteExtensions); + if (rv != SECSuccess) { + return SECFailure; /* Error code set below */ + } + rv = tls13_MaybeHandleEchSignal(ss, savedMsg, savedLength, PR_TRUE); + if (rv != SECSuccess) { + return SECFailure; + } + ss->ssl3.hs.helloRetry = PR_TRUE; + rv = tls13_ReinjectHandshakeTranscript(ss); + if (rv != SECSuccess) { + return rv; + } + + rv = ssl_HashHandshakeMessage(ss, ssl_hs_server_hello, + savedMsg, savedLength); + if (rv != SECSuccess) { + return SECFailure; + } + + ssl_GetXmitBufLock(ss); + if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss) && + ss->ssl3.hs.zeroRttState == ssl_0rtt_none) { + rv = ssl3_SendChangeCipherSpecsInt(ss); + if (rv != SECSuccess) { + goto loser; + } + } + + rv = ssl3_SendClientHello(ss, client_hello_retry); + if (rv != SECSuccess) { + goto loser; + } + + ssl_ReleaseXmitBufLock(ss); + return SECSuccess; + +loser: + ssl_ReleaseXmitBufLock(ss); + return SECFailure; +} + +static SECStatus +tls13_SendPostHandshakeCertificate(sslSocket *ss) +{ + SECStatus rv; + if (ss->ssl3.hs.restartTarget) { + PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget"); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (ss->ssl3.hs.clientCertificatePending) { + SSL_TRC(3, ("%d: TLS13[%d]: deferring tls13_SendClientSecondFlight because" + " certificate authentication is still pending.", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.restartTarget = tls13_SendPostHandshakeCertificate; + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return SECFailure; + } + + ssl_GetXmitBufLock(ss); + rv = tls13_SendClientSecondFlight(ss); + ssl_ReleaseXmitBufLock(ss); + PORT_Assert(ss->ssl3.hs.ws == idle_handshake); + PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL); + PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE); + ss->ssl3.hs.shaPostHandshake = NULL; + if (rv != SECSuccess) { + return SECFailure; + } + return rv; +} + +static SECStatus +tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + SECItem context = { siBuffer, NULL, 0 }; + SECItem extensionsData = { siBuffer, NULL, 0 }; + + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_request sequence", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* Client */ + if (ss->opt.enablePostHandshakeAuth) { + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST, + wait_cert_request, idle_handshake); + } else { + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST, + wait_cert_request); + } + if (rv != SECSuccess) { + return SECFailure; + } + + /* MUST NOT combine external PSKs with certificate authentication. */ + if (ss->sec.authType == ssl_auth_psk) { + FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_CERT_REQUEST, unexpected_message); + return SECFailure; + } + + if (tls13_IsPostHandshake(ss)) { + PORT_Assert(ss->ssl3.hs.shaPostHandshake == NULL); + ss->ssl3.hs.shaPostHandshake = PK11_CloneContext(ss->ssl3.hs.sha); + if (ss->ssl3.hs.shaPostHandshake == NULL) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate_request, b, length); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + /* clean up anything left from previous handshake. */ + if (ss->ssl3.clientCertChain != NULL) { + CERT_DestroyCertificateList(ss->ssl3.clientCertChain); + ss->ssl3.clientCertChain = NULL; + } + if (ss->ssl3.clientCertificate != NULL) { + CERT_DestroyCertificate(ss->ssl3.clientCertificate); + ss->ssl3.clientCertificate = NULL; + } + if (ss->ssl3.clientPrivateKey != NULL) { + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); + ss->ssl3.clientPrivateKey = NULL; + } + if (ss->ssl3.hs.clientAuthSignatureSchemes != NULL) { + PORT_Free(ss->ssl3.hs.clientAuthSignatureSchemes); + ss->ssl3.hs.clientAuthSignatureSchemes = NULL; + ss->ssl3.hs.clientAuthSignatureSchemesLen = 0; + } + SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE); + ss->xtnData.certReqContext.data = NULL; + } else { + PORT_Assert(ss->ssl3.clientCertChain == NULL); + PORT_Assert(ss->ssl3.clientCertificate == NULL); + PORT_Assert(ss->ssl3.clientPrivateKey == NULL); + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemes == NULL); + PORT_Assert(ss->ssl3.hs.clientAuthSignatureSchemesLen == 0); + PORT_Assert(!ss->ssl3.hs.clientCertRequested); + PORT_Assert(ss->xtnData.certReqContext.data == NULL); + } + + rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Unless it is a post-handshake client auth, the certificate + * request context must be empty. */ + if (!tls13_IsPostHandshake(ss) && context.len > 0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, illegal_parameter); + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeVariable(ss, &extensionsData, 2, &b, &length); + if (rv != SECSuccess) { + return SECFailure; + } + + if (length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_REQUEST, decode_error); + return SECFailure; + } + + /* Process all the extensions. */ + rv = ssl3_HandleExtensions(ss, &extensionsData.data, &extensionsData.len, + ssl_hs_certificate_request); + if (rv != SECSuccess) { + return SECFailure; + } + + if (!ss->xtnData.numSigSchemes) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_SIGNATURE_ALGORITHMS_EXTENSION, + missing_extension); + return SECFailure; + } + + rv = SECITEM_CopyItem(NULL, &ss->xtnData.certReqContext, &context); + if (rv != SECSuccess) { + return SECFailure; + } + + ss->ssl3.hs.clientCertRequested = PR_TRUE; + + if (ss->firstHsDone) { + + /* Request a client certificate. */ + rv = ssl3_BeginHandleCertificateRequest( + ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, + &ss->xtnData.certReqAuthorities); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return rv; + } + rv = tls13_SendPostHandshakeCertificate(ss); + } else { + TLS13_SET_HS_STATE(ss, wait_server_cert); + } + return SECSuccess; +} + +PRBool +tls13_ShouldRequestClientAuth(sslSocket *ss) +{ + /* Even if we are configured to request a certificate, we can't + * if this handshake used a PSK, even when we are resuming. */ + return ss->opt.requestCertificate && + ss->ssl3.hs.kea_def->authKeyType != ssl_auth_psk; +} + +static SECStatus +tls13_SendEncryptedServerSequence(sslSocket *ss) +{ + SECStatus rv; + + rv = tls13_ComputeHandshakeSecrets(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake, + ssl_secret_write, PR_FALSE); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { + rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, + ssl_tls13_early_data_xtn, + ssl_SendEmptyExtension); + if (rv != SECSuccess) { + return SECFailure; /* Error code set already. */ + } + } + + rv = tls13_SendEncryptedExtensions(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + if (tls13_ShouldRequestClientAuth(ss)) { + rv = tls13_SendCertificateRequest(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + } + if (ss->ssl3.hs.signatureScheme != ssl_sig_none) { + SECKEYPrivateKey *svrPrivKey; + + rv = tls13_SendCertificate(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + if (tls13_IsSigningWithDelegatedCredential(ss)) { + SSL_TRC(3, ("%d: TLS13[%d]: Signing with delegated credential", + SSL_GETPID(), ss->fd)); + svrPrivKey = ss->sec.serverCert->delegCredKeyPair->privKey; + } else { + svrPrivKey = ss->sec.serverCert->serverKeyPair->privKey; + } + + rv = tls13_SendCertificateVerify(ss, svrPrivKey); + if (rv != SECSuccess) { + return SECFailure; /* err code is set. */ + } + } + + rv = tls13_SendFinished(ss, ss->ssl3.hs.serverHsTrafficSecret); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + return SECSuccess; +} + +/* Called from: ssl3_HandleClientHello */ +static SECStatus +tls13_SendServerHelloSequence(sslSocket *ss) +{ + SECStatus rv; + PRErrorCode err = 0; + + SSL_TRC(3, ("%d: TLS13[%d]: begin send server_hello sequence", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + rv = ssl3_RegisterExtensionSender(ss, &ss->xtnData, + ssl_tls13_supported_versions_xtn, + tls13_ServerSendSupportedVersionsXtn); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = tls13_ComputeHandshakeSecret(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + rv = ssl3_SendServerHello(ss); + if (rv != SECSuccess) { + return rv; /* err code is set. */ + } + + if (ss->ssl3.hs.fakeSid.len) { + PORT_Assert(!IS_DTLS(ss)); + SECITEM_FreeItem(&ss->ssl3.hs.fakeSid, PR_FALSE); + if (!ss->ssl3.hs.helloRetry) { + rv = ssl3_SendChangeCipherSpecsInt(ss); + if (rv != SECSuccess) { + return rv; + } + } + } + + rv = tls13_SendEncryptedServerSequence(ss); + if (rv != SECSuccess) { + err = PORT_GetError(); + } + /* Even if we get an error, since the ServerHello was successfully + * serialized, we should give it a chance to reach the network. This gives + * the client a chance to perform the key exchange and decrypt the alert + * we're about to send. */ + rv |= ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + if (err) { + PORT_SetError(err); + } + return SECFailure; + } + + /* Compute the rest of the secrets except for the resumption + * and exporter secret. */ + rv = tls13_ComputeApplicationSecrets(ss); + if (rv != SECSuccess) { + LOG_ERROR(ss, PORT_GetError()); + return SECFailure; + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, + ssl_secret_write, PR_FALSE); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (IS_DTLS(ss)) { + /* We need this for reading ACKs. */ + ssl_CipherSpecAddRef(ss->ssl3.crSpec); + } + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { + rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData, + ssl_secret_read, PR_TRUE); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + TLS13_SET_HS_STATE(ss, wait_end_of_early_data); + } else { + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none || + ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored); + + rv = tls13_SetCipherSpec(ss, + TrafficKeyHandshake, + ssl_secret_read, PR_FALSE); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (tls13_ShouldRequestClientAuth(ss)) { + TLS13_SET_HS_STATE(ss, wait_client_cert); + } else { + TLS13_SET_HS_STATE(ss, wait_finished); + } + } + + /* Here we set a baseline value for our RTT estimation. + * This value is updated when we get a response from the client. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss); + return SECSuccess; +} + +SECStatus +tls13_HandleServerHelloPart2(sslSocket *ss, const PRUint8 *savedMsg, PRUint32 savedLength) +{ + SECStatus rv; + sslSessionID *sid = ss->sec.ci.sid; + SSL3Statistics *ssl3stats = SSL_GetStatistics(); + + if (ssl3_ExtensionNegotiated(ss, ssl_tls13_pre_shared_key_xtn)) { + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)); + PORT_Assert(ss->xtnData.selectedPsk); + + if (ss->xtnData.selectedPsk->type != ssl_psk_resume) { + ss->statelessResume = PR_FALSE; + } + } else { + /* We may have offered a PSK. If the server didn't negotiate + * it, clear this state to re-extract the Early Secret. */ + if (ss->ssl3.hs.currentSecret) { + /* We might have dropped incompatible PSKs on HRR + * (see RFC8466, Section 4.1.4). */ + PORT_Assert(ss->ssl3.hs.helloRetry || + ssl3_ExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)); + PK11_FreeSymKey(ss->ssl3.hs.currentSecret); + ss->ssl3.hs.currentSecret = NULL; + } + ss->statelessResume = PR_FALSE; + ss->xtnData.selectedPsk = NULL; + } + + if (ss->statelessResume) { + PORT_Assert(sid->version >= SSL_LIBRARY_VERSION_TLS_1_3); + if (tls13_GetHash(ss) != + tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, + illegal_parameter); + return SECFailure; + } + } + + /* Now create a synthetic kea_def that we can tweak. */ + ss->ssl3.hs.kea_def_mutable = *ss->ssl3.hs.kea_def; + ss->ssl3.hs.kea_def = &ss->ssl3.hs.kea_def_mutable; + + if (ss->xtnData.selectedPsk) { + ss->ssl3.hs.kea_def_mutable.authKeyType = ssl_auth_psk; + if (ss->statelessResume) { + tls13_RestoreCipherInfo(ss, sid); + if (sid->peerCert) { + ss->sec.peerCert = CERT_DupCertificate(sid->peerCert); + } + + SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_hits); + SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_stateless_resumes); + } else { + ss->sec.authType = ssl_auth_psk; + } + } else { + if (ss->statelessResume && + ssl3_ExtensionAdvertised(ss, ssl_tls13_pre_shared_key_xtn)) { + SSL_AtomicIncrementLong(&ssl3stats->hsh_sid_cache_misses); + } + if (sid->cached == in_client_cache) { + /* If we tried to resume and failed, let's not try again. */ + ssl_UncacheSessionID(ss); + } + } + + /* Discard current SID and make a new one, though it may eventually + * end up looking a lot like the old one. + */ + ssl_FreeSID(sid); + ss->sec.ci.sid = sid = ssl3_NewSessionID(ss, PR_FALSE); + if (sid == NULL) { + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; + } + if (ss->statelessResume) { + PORT_Assert(ss->sec.peerCert); + sid->peerCert = CERT_DupCertificate(ss->sec.peerCert); + } + sid->version = ss->version; + + rv = tls13_HandleServerKeyShare(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = tls13_ComputeHandshakeSecret(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + rv = tls13_MaybeHandleEchSignal(ss, savedMsg, savedLength, PR_FALSE); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + rv = tls13_ComputeHandshakeSecrets(ss); + if (rv != SECSuccess) { + return SECFailure; /* error code is set. */ + } + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) { + /* When we send 0-RTT, we saved the null spec in case we needed it to + * send another ClientHello in response to a HelloRetryRequest. Now + * that we won't be receiving a HelloRetryRequest, release the spec. */ + ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_write, TrafficKeyClearText); + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake, + ssl_secret_read, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error); + return SECFailure; + } + TLS13_SET_HS_STATE(ss, wait_encrypted_extensions); + + return SECSuccess; +} + +static void +tls13_SetKeyExchangeType(sslSocket *ss, const sslNamedGroupDef *group) +{ + ss->sec.keaGroup = group; + switch (group->keaType) { + /* Note: These overwrite on resumption.... so if you start with ECDH + * and resume with DH, we report DH. That's fine, since no answer + * is really right. */ + case ssl_kea_ecdh: + ss->ssl3.hs.kea_def_mutable.exchKeyType = + ss->statelessResume ? ssl_kea_ecdh_psk : ssl_kea_ecdh; + ss->sec.keaType = ssl_kea_ecdh; + break; + case ssl_kea_ecdh_hybrid: + ss->ssl3.hs.kea_def_mutable.exchKeyType = + ss->statelessResume ? ssl_kea_ecdh_hybrid_psk : ssl_kea_ecdh_hybrid; + ss->sec.keaType = ssl_kea_ecdh_hybrid; + break; + case ssl_kea_dh: + ss->ssl3.hs.kea_def_mutable.exchKeyType = + ss->statelessResume ? ssl_kea_dh_psk : ssl_kea_dh; + ss->sec.keaType = ssl_kea_dh; + break; + default: + PORT_Assert(0); + } +} + +/* + * Called from ssl3_HandleServerHello. + * + * Caller must hold Handshake and RecvBuf locks. + */ +static SECStatus +tls13_HandleServerKeyShare(sslSocket *ss) +{ + SECStatus rv; + TLS13KeyShareEntry *entry; + sslEphemeralKeyPair *keyPair; + PK11SymKey *dheSecret = NULL; + PK11SymKey *kemSecret = NULL; + + SSL_TRC(3, ("%d: TLS13[%d]: handle server_key_share handshake", + SSL_GETPID(), ss->fd)); + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* This list should have one entry. */ + if (PR_CLIST_IS_EMPTY(&ss->xtnData.remoteKeyShares)) { + FATAL_ERROR(ss, SSL_ERROR_MISSING_KEY_SHARE, missing_extension); + return SECFailure; + } + + entry = (TLS13KeyShareEntry *)PR_NEXT_LINK(&ss->xtnData.remoteKeyShares); + PORT_Assert(PR_NEXT_LINK(&entry->link) == &ss->xtnData.remoteKeyShares); + + /* Now get our matching key. */ + keyPair = ssl_LookupEphemeralKeyPair(ss, entry->group); + if (!keyPair) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_SHARE, illegal_parameter); + return SECFailure; + } + + PORT_Assert(ssl_NamedGroupEnabled(ss, entry->group)); + + rv = tls13_HandleKeyShare(ss, entry, keyPair->keys, + tls13_GetHash(ss), + &dheSecret); + if (rv != SECSuccess) { + goto loser; /* Error code already set. */ + } + + if (entry->group->keaType == ssl_kea_ecdh_hybrid) { + rv = tls13_HandleKEMCiphertext(ss, entry, keyPair->kemKeys, &kemSecret); + if (rv != SECSuccess) { + goto loser; /* Error set by tls13_HandleKEMCiphertext */ + } + // We may need to handle different "combiners" here in the future. For + // now this is specific to xyber768d00. + PORT_Assert(entry->group->name == ssl_grp_kem_xyber768d00); + ss->ssl3.hs.dheSecret = PK11_ConcatSymKeys(dheSecret, kemSecret, CKM_HKDF_DERIVE, CKA_DERIVE); + if (!ss->ssl3.hs.dheSecret) { + goto loser; /* Error set by PK11_ConcatSymKeys */ + } + PK11_FreeSymKey(dheSecret); + PK11_FreeSymKey(kemSecret); + } else { + ss->ssl3.hs.dheSecret = dheSecret; + } + + tls13_SetKeyExchangeType(ss, entry->group); + ss->sec.keaKeyBits = SECKEY_PublicKeyStrengthInBits(keyPair->keys->pubKey); + + return SECSuccess; + +loser: + PK11_FreeSymKey(dheSecret); + PK11_FreeSymKey(kemSecret); + FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); + return SECFailure; +} + +static PRBool +tls13_FindCompressionAlgAndCheckIfSupportsEncoding(sslSocket *ss) +{ + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + for (int j = 0; j < ss->ssl3.supportedCertCompressionAlgorithmsCount; j++) { + if (ss->ssl3.supportedCertCompressionAlgorithms[j].id == ss->xtnData.compressionAlg) { + if (ss->ssl3.supportedCertCompressionAlgorithms[j].encode != NULL) { + return PR_TRUE; + } + return PR_FALSE; + } + } + + return PR_FALSE; +} + +static SECStatus +tls13_FindCompressionAlgAndEncodeCertificate( + sslSocket *ss, SECItem *certificateToEncode, SECItem *encodedCertificate) +{ + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SECStatus rv = SECFailure; + for (int j = 0; j < ss->ssl3.supportedCertCompressionAlgorithmsCount; j++) { + if (ss->ssl3.supportedCertCompressionAlgorithms[j].id == ss->xtnData.compressionAlg && + ss->ssl3.supportedCertCompressionAlgorithms[j].encode != NULL) { + rv = ss->ssl3.supportedCertCompressionAlgorithms[j].encode( + certificateToEncode, encodedCertificate); + return rv; + } + } + + PORT_SetError(SEC_ERROR_CERTIFICATE_COMPRESSION_ALGORITHM_NOT_SUPPORTED); + return SECFailure; +} + +static SECStatus +tls13_SendCompressedCertificate(sslSocket *ss, sslBuffer *bufferCertificate) +{ + /* TLS Certificate Compression. RFC 8879 */ + /* As the encoding function takes as input a SECItem, + * we convert bufferCertificate to certificateToEncode. + * + * encodedCertificate is used to store the certificate + * after encoding. + */ + SECItem encodedCertificate = { siBuffer, NULL, 0 }; + SECItem certificateToEncode = { siBuffer, NULL, 0 }; + SECStatus rv = SECFailure; + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SSL_TRC(30, ("%d: TLS13[%d]: %s is encoding the certificate using the %s compression algorithm", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + ssl3_mapCertificateCompressionAlgorithmToName(ss, ss->xtnData.compressionAlg))); + + PRINT_BUF(50, (NULL, "The certificate before encoding:", + bufferCertificate->buf, bufferCertificate->len)); + + PRUint32 lengthUnencodedMessage = bufferCertificate->len; + rv = ssl3_CopyToSECItem(bufferCertificate, &certificateToEncode); + if (rv != SECSuccess) { + SSL_TRC(50, ("%d: TLS13[%d]: %s has failed encoding the certificate.", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + goto loser; /* Code already set. */ + } + + rv = tls13_FindCompressionAlgAndEncodeCertificate(ss, &certificateToEncode, + &encodedCertificate); + if (rv != SECSuccess) { + SSL_TRC(50, ("%d: TLS13[%d]: %s has failed encoding the certificate.", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; /* Code already set. */ + } + + /* The CompressedCertificate message is formed as follows: + * struct { + * CertificateCompressionAlgorithm algorithm; + * uint24 uncompressed_length; + * opaque compressed_certificate_message<1..2^24-1>; + * } CompressedCertificate; + */ + + if (encodedCertificate.len < 1) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + goto loser; + } + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_compressed_certificate, + encodedCertificate.len + 2 + 3 + 3); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + rv = ssl3_AppendHandshakeNumber(ss, ss->xtnData.compressionAlg, 2); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + rv = ssl3_AppendHandshakeNumber(ss, lengthUnencodedMessage, 3); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + PRINT_BUF(30, (NULL, "The encoded certificate: ", + encodedCertificate.data, encodedCertificate.len)); + + rv = ssl3_AppendHandshakeVariable(ss, encodedCertificate.data, encodedCertificate.len, 3); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + + SECITEM_FreeItem(&certificateToEncode, PR_FALSE); + SECITEM_FreeItem(&encodedCertificate, PR_FALSE); + return SECSuccess; + +loser: + SECITEM_FreeItem(&certificateToEncode, PR_FALSE); + SECITEM_FreeItem(&encodedCertificate, PR_FALSE); + return SECFailure; +} + +/* + * opaque ASN1Cert<1..2^24-1>; + * + * struct { + * ASN1Cert cert_data; + * Extension extensions<0..2^16-1>; + * } CertificateEntry; + * + * struct { + * opaque certificate_request_context<0..2^8-1>; + * CertificateEntry certificate_list<0..2^24-1>; + * } Certificate; + */ +static SECStatus +tls13_SendCertificate(sslSocket *ss) +{ + SECStatus rv; + CERTCertificateList *certChain; + int certChainLen = 0; + int i; + SECItem context = { siBuffer, NULL, 0 }; + sslBuffer extensionBuf = SSL_BUFFER_EMPTY; + sslBuffer bufferCertificate = SSL_BUFFER_EMPTY; + + SSL_TRC(3, ("%d: TLS1.3[%d]: send certificate handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (ss->sec.isServer) { + PORT_Assert(!ss->sec.localCert); + /* A server certificate is selected in tls13_SelectServerCert(). */ + PORT_Assert(ss->sec.serverCert); + + certChain = ss->sec.serverCert->serverCertChain; + ss->sec.localCert = CERT_DupCertificate(ss->sec.serverCert->serverCert); + } else { + if (ss->sec.localCert) + CERT_DestroyCertificate(ss->sec.localCert); + + certChain = ss->ssl3.clientCertChain; + ss->sec.localCert = CERT_DupCertificate(ss->ssl3.clientCertificate); + } + + if (!ss->sec.isServer) { + PORT_Assert(ss->ssl3.hs.clientCertRequested); + context = ss->xtnData.certReqContext; + } + + if (certChain) { + for (i = 0; i < certChain->len; i++) { + /* Each cert is 3 octet length, cert, and extensions */ + certChainLen += 3 + certChain->certs[i].len + 2; + } + + /* Build the extensions. This only applies to the leaf cert, because we + * don't yet send extensions for non-leaf certs. */ + rv = ssl_ConstructExtensions(ss, &extensionBuf, ssl_hs_certificate); + if (rv != SECSuccess) { + return SECFailure; /* code already set */ + } + /* extensionBuf.len is only added once, for the leaf cert. */ + certChainLen += SSL_BUFFER_LEN(&extensionBuf); + } + + rv = sslBuffer_AppendVariable(&bufferCertificate, context.data, context.len, 1); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + rv = sslBuffer_AppendNumber(&bufferCertificate, certChainLen, 3); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + if (certChain) { + for (i = 0; i < certChain->len; i++) { + rv = sslBuffer_AppendVariable(&bufferCertificate, certChain->certs[i].data, + certChain->certs[i].len, 3); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + + if (i) { + /* Not end-entity. */ + rv = sslBuffer_AppendNumber(&bufferCertificate, 0, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + continue; + } + + rv = sslBuffer_AppendBufferVariable(&bufferCertificate, &extensionBuf, 2); + if (rv != SECSuccess) { + goto loser; /* Code already set. */ + } + } + } + + /* If no compression mechanism was established or + * the compression mechanism supports only decoding, + * we continue as before. */ + if (ss->xtnData.compressionAlg == 0 || !tls13_FindCompressionAlgAndCheckIfSupportsEncoding(ss)) { + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate, + 1 + context.len + 3 + certChainLen); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + rv = ssl3_AppendBufferToHandshake(ss, &bufferCertificate); + if (rv != SECSuccess) { + goto loser; /* err set by AppendHandshake. */ + } + } else { + rv = tls13_SendCompressedCertificate(ss, &bufferCertificate); + if (rv != SECSuccess) { + goto loser; /* err set by tls13_SendCompressedCertificate. */ + } + } + + sslBuffer_Clear(&bufferCertificate); + sslBuffer_Clear(&extensionBuf); + return SECSuccess; + +loser: + sslBuffer_Clear(&bufferCertificate); + sslBuffer_Clear(&extensionBuf); + return SECFailure; +} + +static SECStatus +tls13_HandleCertificateEntry(sslSocket *ss, SECItem *data, PRBool first, + CERTCertificate **certp) +{ + SECStatus rv; + SECItem certData; + SECItem extensionsData; + CERTCertificate *cert = NULL; + + rv = ssl3_ConsumeHandshakeVariable(ss, &certData, + 3, &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeVariable(ss, &extensionsData, + 2, &data->data, &data->len); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Parse all the extensions. */ + if (first && !ss->sec.isServer) { + rv = ssl3_HandleExtensions(ss, &extensionsData.data, + &extensionsData.len, + ssl_hs_certificate); + if (rv != SECSuccess) { + return SECFailure; + } + /* TODO(ekr@rtfm.com): Copy out SCTs. Bug 1315727. */ + } + + cert = CERT_NewTempCertificate(ss->dbHandle, &certData, NULL, + PR_FALSE, PR_TRUE); + + if (!cert) { + PRErrorCode errCode = PORT_GetError(); + switch (errCode) { + case PR_OUT_OF_MEMORY_ERROR: + case SEC_ERROR_BAD_DATABASE: + case SEC_ERROR_NO_MEMORY: + FATAL_ERROR(ss, errCode, internal_error); + return SECFailure; + default: + ssl3_SendAlertForCertError(ss, errCode); + return SECFailure; + } + } + + *certp = cert; + + return SECSuccess; +} + +static SECStatus +tls13_EnsureCerticateExpected(sslSocket *ss) +{ + SECStatus rv = SECFailure; + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + if (ss->sec.isServer) { + /* Receiving this message might be the first sign we have that + * early data is over, so pretend we received EOED. */ + rv = tls13_MaybeHandleSuppressedEndOfEarlyData(ss); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + if (ss->ssl3.clientCertRequested) { + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE, + idle_handshake); + } else { + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE, + wait_client_cert); + } + } else { + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERTIFICATE, + wait_cert_request, wait_server_cert); + } + return rv; +} + +/* RFC 8879 TLS Certificate Compression + * struct { + * CertificateCompressionAlgorithm algorithm; + * uint24 uncompressed_length; + * opaque compressed_certificate_message<1..2^24-1>; + * } CompressedCertificate; + */ +static SECStatus +tls13_HandleCertificateDecode(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SECStatus rv = SECFailure; + + if (!ss->xtnData.certificateCompressionAdvertised) { + FATAL_ERROR(ss, SEC_ERROR_UNEXPECTED_COMPRESSED_CERTIFICATE, decode_error); + return SECFailure; + } + + rv = tls13_EnsureCerticateExpected(ss); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + if (ss->firstHsDone) { + rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_compressed_certificate, b, length); + if (rv != SECSuccess) { + return rv; + } + } + + SSL_TRC(30, ("%d: TLS1.3[%d]: %s handles certificate compression handshake", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + + PRINT_BUF(50, (NULL, "The certificate before decoding:", b, length)); + /* Reading CertificateCompressionAlgorithm. */ + PRUint32 compressionAlg = 0; + rv = ssl3_ConsumeHandshakeNumber(ss, &compressionAlg, 2, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Alert already sent. */ + } + + PRBool compressionAlgorithmIsSupported = PR_FALSE; + SECStatus (*certificateDecodingFunc)(const SECItem *, SECItem *, size_t) = NULL; + for (int i = 0; i < ss->ssl3.supportedCertCompressionAlgorithmsCount; i++) { + if (ss->ssl3.supportedCertCompressionAlgorithms[i].id == compressionAlg) { + compressionAlgorithmIsSupported = PR_TRUE; + certificateDecodingFunc = ss->ssl3.supportedCertCompressionAlgorithms[i].decode; + } + } + + /* Peer selected a compression algorithm we do not support (and did not advertise). */ + if (!compressionAlgorithmIsSupported) { + PORT_SetError(SEC_ERROR_CERTIFICATE_COMPRESSION_ALGORITHM_NOT_SUPPORTED); + FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); + return SECFailure; + } + + /* The algorithm does not support decoding. */ + if (certificateDecodingFunc == NULL) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); + return SECFailure; + } + + SSL_TRC(30, ("%d: TLS13[%d]: %s is decoding the certificate using the %s compression algorithm", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + ssl3_mapCertificateCompressionAlgorithmToName(ss, compressionAlg))); + PRUint32 decodedCertificateLen = 0; + rv = ssl3_ConsumeHandshakeNumber(ss, &decodedCertificateLen, 3, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* alert has been sent */ + } + + /* If the received CompressedCertificate message cannot be decompressed, + * he connection MUST be terminated with the "bad_certificate" alert. + */ + if (decodedCertificateLen == 0) { + SSL_TRC(50, ("%d: TLS13[%d]: %s decoded certificate length is incorrect", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + ssl3_mapCertificateCompressionAlgorithmToName(ss, compressionAlg))); + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, bad_certificate); + return SECFailure; + } + + /* opaque compressed_certificate_message<1..2^24-1>; */ + PRUint32 compressedCertificateMessageLen = 0; + rv = ssl3_ConsumeHandshakeNumber(ss, &compressedCertificateMessageLen, 3, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* alert has been sent */ + } + + if (compressedCertificateMessageLen == 0 || compressedCertificateMessageLen != length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, bad_certificate); + return SECFailure; + } + + /* Decoding received certificate. */ + SECItem decodedCertificate = { siBuffer, NULL, 0 }; + + SECItem encodedCertAsSecItem; + SECITEM_MakeItem(NULL, &encodedCertAsSecItem, b, compressedCertificateMessageLen); + + rv = certificateDecodingFunc(&encodedCertAsSecItem, &decodedCertificate, decodedCertificateLen); + SECITEM_FreeItem(&encodedCertAsSecItem, PR_FALSE); + + if (rv != SECSuccess) { + SSL_TRC(50, ("%d: TLS13[%d]: %s decoding of the certificate has failed", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + ssl3_mapCertificateCompressionAlgorithmToName(ss, compressionAlg))); + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, bad_certificate); + goto loser; + } + PRINT_BUF(60, (ss, "consume bytes:", b, compressedCertificateMessageLen)); + *b += compressedCertificateMessageLen; + length -= compressedCertificateMessageLen; + + /* If, after decompression, the specified length does not match the actual length, + * the party receiving the invalid message MUST abort the connection + * with the "bad_certificate" alert. + */ + if (decodedCertificateLen != decodedCertificate.len) { + SSL_TRC(50, ("%d: TLS13[%d]: %s certificate length does not correspond to extension length", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), + ssl3_mapCertificateCompressionAlgorithmToName(ss, compressionAlg))); + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, bad_certificate); + goto loser; + } + + PRINT_BUF(50, (NULL, "Decoded certificate", + decodedCertificate.data, decodedCertificate.len)); + + /* compressed_certificate_message: The result of applying the indicated + * compression algorithm to the encoded Certificate message that + * would have been sent if certificate compression was not in use. + * + * After decompression, the Certificate message MUST be processed as if + * it were encoded without being compressed. This way, the parsing and + * the verification have the same security properties as they would have + * in TLS normally. + */ + rv = tls13_HandleCertificate(ss, decodedCertificate.data, decodedCertificate.len, PR_TRUE); + if (rv != SECSuccess) { + goto loser; + } + /* We allow only one compressed certificate to be handled after each + certificate compression advertisement. + See test CertificateCompression_TwoEncodedCertificateRequests. */ + ss->xtnData.certificateCompressionAdvertised = PR_FALSE; + SECITEM_FreeItem(&decodedCertificate, PR_FALSE); + return SECSuccess; + +loser: + SECITEM_FreeItem(&decodedCertificate, PR_FALSE); + return SECFailure; +} + +/* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete + * tls13 Certificate message. + * Caller must hold Handshake and RecvBuf locks. + */ +static SECStatus +tls13_HandleCertificate(sslSocket *ss, PRUint8 *b, PRUint32 length, PRBool alreadyHashed) +{ + SECStatus rv; + SECItem context = { siBuffer, NULL, 0 }; + SECItem certList; + PRBool first = PR_TRUE; + ssl3CertNode *lastCert = NULL; + + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate handshake", + SSL_GETPID(), ss->fd)); + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + rv = tls13_EnsureCerticateExpected(ss); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + /* We can ignore any other cleartext from the client. */ + if (ss->sec.isServer && IS_DTLS(ss)) { + ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText); + dtls_ReceivedFirstMessageInFlight(ss); + } + + /* AlreadyHashed is true only when Certificate Compression is used. */ + if (ss->firstHsDone && !alreadyHashed) { + rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate, b, length); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + } + + if (!ss->firstHsDone && ss->sec.isServer) { + /* Our first shot an getting an RTT estimate. If the client took extra + * time to fetch a certificate, this will be bad, but we can't do much + * about that. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss) - ss->ssl3.hs.rttEstimate; + } + + /* Process the context string */ + rv = ssl3_ConsumeHandshakeVariable(ss, &context, 1, &b, &length); + if (rv != SECSuccess) + return SECFailure; + + if (ss->ssl3.clientCertRequested) { + PORT_Assert(ss->sec.isServer); + if (SECITEM_CompareItem(&context, &ss->xtnData.certReqContext) != 0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter); + return SECFailure; + } + } + rv = ssl3_ConsumeHandshakeVariable(ss, &certList, 3, &b, &length); + if (rv != SECSuccess) { + return SECFailure; + } + if (length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, illegal_parameter); + return SECFailure; + } + + if (!certList.len) { + if (!ss->sec.isServer) { + /* Servers always need to send some cert. */ + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERTIFICATE, bad_certificate); + return SECFailure; + } else { + /* This is TLS's version of a no_certificate alert. */ + /* I'm a server. I've requested a client cert. He hasn't got one. */ + rv = ssl3_HandleNoCertificate(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + TLS13_SET_HS_STATE(ss, wait_finished); + return SECSuccess; + } + } + + /* Now clean up. */ + ssl3_CleanupPeerCerts(ss); + ss->ssl3.peerCertArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); + if (ss->ssl3.peerCertArena == NULL) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + + while (certList.len) { + CERTCertificate *cert; + + rv = tls13_HandleCertificateEntry(ss, &certList, first, + &cert); + if (rv != SECSuccess) { + ss->xtnData.signedCertTimestamps.len = 0; + return SECFailure; + } + + if (first) { + ss->sec.peerCert = cert; + + if (ss->xtnData.signedCertTimestamps.len) { + sslSessionID *sid = ss->sec.ci.sid; + rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.signedCertTimestamps, + &ss->xtnData.signedCertTimestamps); + ss->xtnData.signedCertTimestamps.len = 0; + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + } + } else { + ssl3CertNode *c = PORT_ArenaNew(ss->ssl3.peerCertArena, + ssl3CertNode); + if (!c) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + c->cert = cert; + c->next = NULL; + + if (lastCert) { + lastCert->next = c; + } else { + ss->ssl3.peerCertChain = c; + } + lastCert = c; + } + + first = PR_FALSE; + } + SECKEY_UpdateCertPQG(ss->sec.peerCert); + + return ssl3_AuthCertificate(ss); /* sets ss->ssl3.hs.ws */ +} + +/* Add context to the hash functions as described in + [draft-ietf-tls-tls13; Section 4.9.1] */ +SECStatus +tls13_AddContextToHashes(sslSocket *ss, const SSL3Hashes *hashes, + SSLHashType algorithm, PRBool sending, + SSL3Hashes *tbsHash) +{ + SECStatus rv = SECSuccess; + PK11Context *ctx; + const unsigned char context_padding[] = { + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, + 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20, 0x20 + }; + + const char *client_cert_verify_string = "TLS 1.3, client CertificateVerify"; + const char *server_cert_verify_string = "TLS 1.3, server CertificateVerify"; + const char *context_string = (sending ^ ss->sec.isServer) ? client_cert_verify_string + : server_cert_verify_string; + unsigned int hashlength; + + /* Double check that we are doing the same hash.*/ + PORT_Assert(hashes->len == tls13_GetHashSize(ss)); + + ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(algorithm)); + if (!ctx) { + PORT_SetError(SEC_ERROR_NO_MEMORY); + goto loser; + } + + PORT_Assert(SECFailure); + PORT_Assert(!SECSuccess); + + PRINT_BUF(50, (ss, "TLS 1.3 hash without context", hashes->u.raw, hashes->len)); + PRINT_BUF(50, (ss, "Context string", context_string, strlen(context_string))); + rv |= PK11_DigestBegin(ctx); + rv |= PK11_DigestOp(ctx, context_padding, sizeof(context_padding)); + rv |= PK11_DigestOp(ctx, (unsigned char *)context_string, + strlen(context_string) + 1); /* +1 includes the terminating 0 */ + rv |= PK11_DigestOp(ctx, hashes->u.raw, hashes->len); + /* Update the hash in-place */ + rv |= PK11_DigestFinal(ctx, tbsHash->u.raw, &hashlength, sizeof(tbsHash->u.raw)); + PK11_DestroyContext(ctx, PR_TRUE); + PRINT_BUF(50, (ss, "TLS 1.3 hash with context", tbsHash->u.raw, hashlength)); + + tbsHash->len = hashlength; + tbsHash->hashAlg = algorithm; + + if (rv) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + return SECSuccess; + +loser: + return SECFailure; +} + +/* + * Derive-Secret(Secret, Label, Messages) = + * HKDF-Expand-Label(Secret, Label, + * Hash(Messages) + Hash(resumption_context), L)) + */ +SECStatus +tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key, + const char *label, + unsigned int labelLen, + const SSL3Hashes *hashes, + PK11SymKey **dest, + SSLHashType hash) +{ + SECStatus rv; + + rv = tls13_HkdfExpandLabel(key, hash, hashes->u.raw, hashes->len, + label, labelLen, CKM_HKDF_DERIVE, + tls13_GetHashSizeForHash(hash), + ss->protocolVariant, dest); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + return SECSuccess; +} + +/* Convenience wrapper for the empty hash. */ +SECStatus +tls13_DeriveSecretNullHash(sslSocket *ss, PK11SymKey *key, + const char *label, + unsigned int labelLen, + PK11SymKey **dest, + SSLHashType hash) +{ + SSL3Hashes hashes; + SECStatus rv; + PRUint8 buf[] = { 0 }; + + rv = tls13_ComputeHash(ss, &hashes, buf, 0, hash); + if (rv != SECSuccess) { + return SECFailure; + } + + return tls13_DeriveSecret(ss, key, label, labelLen, &hashes, dest, hash); +} + +/* Convenience wrapper that lets us supply a separate prefix and suffix. */ +static SECStatus +tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key, + const char *prefix, + const char *suffix, + const char *keylogLabel, + PK11SymKey **dest) +{ + SECStatus rv; + SSL3Hashes hashes; + char buf[100]; + const char *label; + + if (prefix) { + if ((strlen(prefix) + strlen(suffix) + 2) > sizeof(buf)) { + PORT_Assert(0); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + (void)PR_snprintf(buf, sizeof(buf), "%s %s", + prefix, suffix); + label = buf; + } else { + label = suffix; + } + + SSL_TRC(3, ("%d: TLS13[%d]: deriving secret '%s'", + SSL_GETPID(), ss->fd, label)); + rv = tls13_ComputeHandshakeHashes(ss, &hashes); + if (rv != SECSuccess) { + PORT_Assert(0); /* Should never fail */ + ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = tls13_DeriveSecret(ss, key, label, strlen(label), + &hashes, dest, tls13_GetHash(ss)); + if (rv != SECSuccess) { + return SECFailure; + } + + if (keylogLabel) { + ssl3_RecordKeyLog(ss, keylogLabel, *dest); + } + return SECSuccess; +} + +SECStatus +SSLExp_SecretCallback(PRFileDesc *fd, SSLSecretCallback cb, void *arg) +{ + sslSocket *ss = ssl_FindSocket(fd); + if (!ss) { + SSL_DBG(("%d: SSL[%d]: bad socket in SSL_SecretCallback", + SSL_GETPID(), fd)); + return SECFailure; + } + + ssl_Get1stHandshakeLock(ss); + ssl_GetSSL3HandshakeLock(ss); + ss->secretCallback = cb; + ss->secretCallbackArg = arg; + ssl_ReleaseSSL3HandshakeLock(ss); + ssl_Release1stHandshakeLock(ss); + return SECSuccess; +} + +/* Derive traffic keys for the next cipher spec in the queue. */ +static SECStatus +tls13_DeriveTrafficKeys(sslSocket *ss, ssl3CipherSpec *spec, + TrafficKeyType type, + PRBool deleteSecret) +{ + size_t keySize = spec->cipherDef->key_size; + size_t ivSize = spec->cipherDef->iv_size + + spec->cipherDef->explicit_nonce_size; /* This isn't always going to + * work, but it does for + * AES-GCM */ + CK_MECHANISM_TYPE bulkAlgorithm = ssl3_Alg2Mech(spec->cipherDef->calg); + PK11SymKey **prkp = NULL; + PK11SymKey *prk = NULL; + PRBool clientSecret; + SECStatus rv; + /* These labels are just used for debugging. */ + static const char kHkdfPhaseEarlyApplicationDataKeys[] = "early application data"; + static const char kHkdfPhaseHandshakeKeys[] = "handshake data"; + static const char kHkdfPhaseApplicationDataKeys[] = "application data"; + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + clientSecret = !tls13_UseServerSecret(ss, spec->direction); + switch (type) { + case TrafficKeyEarlyApplicationData: + PORT_Assert(clientSecret); + prkp = &ss->ssl3.hs.clientEarlyTrafficSecret; + spec->phase = kHkdfPhaseEarlyApplicationDataKeys; + break; + case TrafficKeyHandshake: + prkp = clientSecret ? &ss->ssl3.hs.clientHsTrafficSecret + : &ss->ssl3.hs.serverHsTrafficSecret; + spec->phase = kHkdfPhaseHandshakeKeys; + break; + case TrafficKeyApplicationData: + prkp = clientSecret ? &ss->ssl3.hs.clientTrafficSecret + : &ss->ssl3.hs.serverTrafficSecret; + spec->phase = kHkdfPhaseApplicationDataKeys; + break; + default: + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + return SECFailure; + } + PORT_Assert(prkp != NULL); + prk = *prkp; + + SSL_TRC(3, ("%d: TLS13[%d]: deriving %s traffic keys epoch=%d (%s)", + SSL_GETPID(), ss->fd, SPEC_DIR(spec), + spec->epoch, spec->phase)); + + rv = tls13_HkdfExpandLabel(prk, tls13_GetHash(ss), + NULL, 0, + kHkdfPurposeKey, strlen(kHkdfPurposeKey), + bulkAlgorithm, keySize, + ss->protocolVariant, + &spec->keyMaterial.key); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + goto loser; + } + + if (IS_DTLS(ss) && spec->epoch > 0) { + rv = ssl_CreateMaskingContextInner(spec->version, ss->ssl3.hs.cipher_suite, + ss->protocolVariant, prk, kHkdfPurposeSn, + strlen(kHkdfPurposeSn), &spec->maskContext); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + goto loser; + } + } + + rv = tls13_HkdfExpandLabelRaw(prk, tls13_GetHash(ss), + NULL, 0, + kHkdfPurposeIv, strlen(kHkdfPurposeIv), + ss->protocolVariant, + spec->keyMaterial.iv, ivSize); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + PORT_Assert(0); + goto loser; + } + + if (deleteSecret) { + PK11_FreeSymKey(prk); + *prkp = NULL; + } + return SECSuccess; + +loser: + return SECFailure; +} + +void +tls13_SetSpecRecordVersion(sslSocket *ss, ssl3CipherSpec *spec) +{ + /* Set the record version to pretend to be (D)TLS 1.2. */ + if (IS_DTLS(ss)) { + spec->recordVersion = SSL_LIBRARY_VERSION_DTLS_1_2_WIRE; + } else { + spec->recordVersion = SSL_LIBRARY_VERSION_TLS_1_2; + } + SSL_TRC(10, ("%d: TLS13[%d]: set spec=%d record version to 0x%04x", + SSL_GETPID(), ss->fd, spec, spec->recordVersion)); +} + +static SECStatus +tls13_SetupPendingCipherSpec(sslSocket *ss, ssl3CipherSpec *spec) +{ + ssl3CipherSuite suite = ss->ssl3.hs.cipher_suite; + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(spec->epoch); + + /* Version isn't set when we send 0-RTT data. */ + spec->version = PR_MAX(SSL_LIBRARY_VERSION_TLS_1_3, ss->version); + + ssl_SaveCipherSpec(ss, spec); + /* We want to keep read cipher specs around longer because + * there are cases where we might get either epoch N or + * epoch N+1. */ + if (IS_DTLS(ss) && spec->direction == ssl_secret_read) { + ssl_CipherSpecAddRef(spec); + } + + SSL_TRC(3, ("%d: TLS13[%d]: Set Pending Cipher Suite to 0x%04x", + SSL_GETPID(), ss->fd, suite)); + + spec->cipherDef = ssl_GetBulkCipherDef(ssl_LookupCipherSuiteDef(suite)); + + if (spec->epoch == TrafficKeyEarlyApplicationData) { + if (ss->xtnData.selectedPsk && + ss->xtnData.selectedPsk->zeroRttSuite != TLS_NULL_WITH_NULL_NULL) { + spec->earlyDataRemaining = ss->xtnData.selectedPsk->maxEarlyData; + } + } + + tls13_SetSpecRecordVersion(ss, spec); + + /* The record size limit is reduced by one so that the remainder of the + * record handling code can use the same checks for all versions. */ + if (ssl3_ExtensionNegotiated(ss, ssl_record_size_limit_xtn)) { + spec->recordSizeLimit = ((spec->direction == ssl_secret_read) + ? ss->opt.recordSizeLimit + : ss->xtnData.recordSizeLimit) - + 1; + } else { + spec->recordSizeLimit = MAX_FRAGMENT_LENGTH; + } + return SECSuccess; +} + +/* + * Initialize the cipher context. All TLS 1.3 operations are AEAD, + * so they are all message contexts. + */ +static SECStatus +tls13_InitPendingContext(sslSocket *ss, ssl3CipherSpec *spec) +{ + CK_MECHANISM_TYPE encMechanism; + CK_ATTRIBUTE_TYPE encMode; + SECItem iv; + SSLCipherAlgorithm calg; + + calg = spec->cipherDef->calg; + + encMechanism = ssl3_Alg2Mech(calg); + encMode = CKA_NSS_MESSAGE | ((spec->direction == ssl_secret_write) ? CKA_ENCRYPT : CKA_DECRYPT); + iv.data = NULL; + iv.len = 0; + + /* + * build the context + */ + spec->cipherContext = PK11_CreateContextBySymKey(encMechanism, encMode, + spec->keyMaterial.key, + &iv); + if (!spec->cipherContext) { + ssl_MapLowLevelError(SSL_ERROR_SYM_KEY_CONTEXT_FAILURE); + return SECFailure; + } + return SECSuccess; +} + +/* + * Called before sending alerts to set up the right key on the client. + * We might encounter errors during the handshake where the current + * key is ClearText or EarlyApplicationData. This + * function switches to the Handshake key if possible. + */ +SECStatus +tls13_SetAlertCipherSpec(sslSocket *ss) +{ + SECStatus rv; + + if (ss->sec.isServer) { + return SECSuccess; + } + if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return SECSuccess; + } + if (TLS13_IN_HS_STATE(ss, wait_server_hello)) { + return SECSuccess; + } + if ((ss->ssl3.cwSpec->epoch != TrafficKeyClearText) && + (ss->ssl3.cwSpec->epoch != TrafficKeyEarlyApplicationData)) { + return SECSuccess; + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake, + ssl_secret_write, PR_FALSE); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + return SECSuccess; +} + +/* Install a new cipher spec for this direction. + * + * During the handshake, the values for |epoch| take values from the + * TrafficKeyType enum. Afterwards, key update increments them. + */ +static SECStatus +tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch, + SSLSecretDirection direction, PRBool deleteSecret) +{ + TrafficKeyType type; + SECStatus rv; + ssl3CipherSpec *spec = NULL; + ssl3CipherSpec **specp; + + /* Flush out old handshake data. */ + ssl_GetXmitBufLock(ss); + rv = ssl3_FlushHandshake(ss, ssl_SEND_FLAG_FORCE_INTO_BUFFER); + ssl_ReleaseXmitBufLock(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Create the new spec. */ + spec = ssl_CreateCipherSpec(ss, direction); + if (!spec) { + return SECFailure; + } + spec->epoch = epoch; + spec->nextSeqNum = 0; + if (IS_DTLS(ss)) { + dtls_InitRecvdRecords(&spec->recvdRecords); + } + + /* This depends on spec having a valid direction and epoch. */ + rv = tls13_SetupPendingCipherSpec(ss, spec); + if (rv != SECSuccess) { + goto loser; + } + + type = (TrafficKeyType)PR_MIN(TrafficKeyApplicationData, epoch); + rv = tls13_DeriveTrafficKeys(ss, spec, type, deleteSecret); + if (rv != SECSuccess) { + goto loser; + } + + rv = tls13_InitPendingContext(ss, spec); + if (rv != SECSuccess) { + goto loser; + } + + /* Now that we've set almost everything up, finally cut over. */ + specp = (direction == ssl_secret_read) ? &ss->ssl3.crSpec : &ss->ssl3.cwSpec; + ssl_GetSpecWriteLock(ss); + ssl_CipherSpecRelease(*specp); /* May delete old cipher. */ + *specp = spec; /* Overwrite. */ + ssl_ReleaseSpecWriteLock(ss); + + SSL_TRC(3, ("%d: TLS13[%d]: %s installed key for epoch=%d (%s) dir=%s", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), spec->epoch, + spec->phase, SPEC_DIR(spec))); + return SECSuccess; + +loser: + ssl_CipherSpecRelease(spec); + return SECFailure; +} + +SECStatus +tls13_ComputeHandshakeHashes(sslSocket *ss, SSL3Hashes *hashes) +{ + SECStatus rv; + PK11Context *ctx = NULL; + PRBool useEchInner; + sslBuffer *transcript; + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + if (ss->ssl3.hs.hashType == handshake_hash_unknown) { + /* Backup: if we haven't done any hashing, then hash now. + * This happens when we are doing 0-RTT on the client. */ + ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(tls13_GetHash(ss))); + if (!ctx) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + + if (PK11_DigestBegin(ctx) != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + + /* One might expect this to use ss->ssl3.hs.echAccepted, + * but with 0-RTT we don't know that yet. */ + useEchInner = ss->sec.isServer ? PR_FALSE : !!ss->ssl3.hs.echHpkeCtx; + transcript = useEchInner ? &ss->ssl3.hs.echInnerMessages : &ss->ssl3.hs.messages; + + PRINT_BUF(10, (ss, "Handshake hash computed over saved messages", + transcript->buf, + transcript->len)); + + if (PK11_DigestOp(ctx, + transcript->buf, + transcript->len) != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + } else { + if (ss->firstHsDone) { + ctx = PK11_CloneContext(ss->ssl3.hs.shaPostHandshake); + } else { + ctx = PK11_CloneContext(ss->ssl3.hs.sha); + } + if (!ctx) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + return SECFailure; + } + } + + rv = PK11_DigestFinal(ctx, hashes->u.raw, + &hashes->len, + sizeof(hashes->u.raw)); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_DIGEST_FAILURE); + goto loser; + } + + PRINT_BUF(10, (ss, "Handshake hash", hashes->u.raw, hashes->len)); + PORT_Assert(hashes->len == tls13_GetHashSize(ss)); + PK11_DestroyContext(ctx, PR_TRUE); + + return SECSuccess; + +loser: + PK11_DestroyContext(ctx, PR_TRUE); + return SECFailure; +} + +TLS13KeyShareEntry * +tls13_CopyKeyShareEntry(TLS13KeyShareEntry *o) +{ + TLS13KeyShareEntry *n; + + PORT_Assert(o); + n = PORT_ZNew(TLS13KeyShareEntry); + if (!n) { + return NULL; + } + + if (SECSuccess != SECITEM_CopyItem(NULL, &n->key_exchange, &o->key_exchange)) { + PORT_Free(n); + return NULL; + } + n->group = o->group; + return n; +} + +void +tls13_DestroyKeyShareEntry(TLS13KeyShareEntry *offer) +{ + if (!offer) { + return; + } + SECITEM_ZfreeItem(&offer->key_exchange, PR_FALSE); + PORT_ZFree(offer, sizeof(*offer)); +} + +void +tls13_DestroyKeyShares(PRCList *list) +{ + PRCList *cur_p; + + /* The list must be initialized. */ + PORT_Assert(PR_LIST_HEAD(list)); + + while (!PR_CLIST_IS_EMPTY(list)) { + cur_p = PR_LIST_TAIL(list); + PR_REMOVE_LINK(cur_p); + tls13_DestroyKeyShareEntry((TLS13KeyShareEntry *)cur_p); + } +} + +void +tls13_DestroyEarlyData(PRCList *list) +{ + PRCList *cur_p; + + while (!PR_CLIST_IS_EMPTY(list)) { + TLS13EarlyData *msg; + + cur_p = PR_LIST_TAIL(list); + msg = (TLS13EarlyData *)cur_p; + + PR_REMOVE_LINK(cur_p); + SECITEM_ZfreeItem(&msg->data, PR_FALSE); + PORT_ZFree(msg, sizeof(*msg)); + } +} + +/* draft-ietf-tls-tls13 Section 5.2.2 specifies the following + * nonce algorithm: + * + * The length of the per-record nonce (iv_length) is set to max(8 bytes, + * N_MIN) for the AEAD algorithm (see [RFC5116] Section 4). An AEAD + * algorithm where N_MAX is less than 8 bytes MUST NOT be used with TLS. + * The per-record nonce for the AEAD construction is formed as follows: + * + * 1. The 64-bit record sequence number is padded to the left with + * zeroes to iv_length. + * + * 2. The padded sequence number is XORed with the static + * client_write_iv or server_write_iv, depending on the role. + * + * The resulting quantity (of length iv_length) is used as the per- + * record nonce. + * + * Existing suites have the same nonce size: N_MIN = N_MAX = 12 bytes + * + * See RFC 5288 and https://tools.ietf.org/html/draft-ietf-tls-chacha20-poly1305-04#section-2 + */ +static void +tls13_WriteNonce(const unsigned char *ivIn, unsigned int ivInLen, + const unsigned char *nonce, unsigned int nonceLen, + unsigned char *ivOut, unsigned int ivOutLen) +{ + size_t i; + unsigned int offset = ivOutLen - nonceLen; + + PORT_Assert(ivInLen <= ivOutLen); + PORT_Assert(nonceLen <= ivOutLen); + PORT_Memset(ivOut, 0, ivOutLen); + PORT_Memcpy(ivOut, ivIn, ivInLen); + + /* XOR the last n bytes of the IV with the nonce (should be a counter). */ + for (i = 0; i < nonceLen; ++i) { + ivOut[offset + i] ^= nonce[i]; + } + PRINT_BUF(50, (NULL, "Nonce", ivOut, ivOutLen)); +} + +/* Setup the IV for AEAD encrypt. The PKCS #11 module will add the + * counter, but it doesn't know about the DTLS epic, so we add it here. + */ +unsigned int +tls13_SetupAeadIv(PRBool isDTLS, SSL3ProtocolVersion v, unsigned char *ivOut, unsigned char *ivIn, + unsigned int offset, unsigned int ivLen, DTLSEpoch epoch) +{ + PORT_Memcpy(ivOut, ivIn, ivLen); + if (isDTLS && v < SSL_LIBRARY_VERSION_TLS_1_3) { + /* handle the tls 1.2 counter mode case, the epoc is copied + * instead of xored. We accomplish this by clearing ivOut + * before running xor. */ + if (offset >= ivLen) { + ivOut[offset] = ivOut[offset + 1] = 0; + } + ivOut[offset] ^= (unsigned char)(epoch >> BPB) & 0xff; + ivOut[offset + 1] ^= (unsigned char)(epoch)&0xff; + offset += 2; + } + + return offset; +} + +/* + * Do a single AEAD for TLS. This differs from PK11_AEADOp in the following + * ways. + * 1) If context is not supplied, it treats the operation as a single shot + * and creates a context from symKey and mech. + * 2) It always assumes the tag will be at the end of the buffer + * (in on decrypt, out on encrypt) just like the old single shot. + * 3) If we aren't generating an IV, it uses tls13_WriteNonce to create the + * nonce. + * NOTE is context is supplied, symKey and mech are ignored + */ +SECStatus +tls13_AEAD(PK11Context *context, PRBool decrypt, + CK_GENERATOR_FUNCTION ivGen, unsigned int fixedbits, + const unsigned char *ivIn, unsigned char *ivOut, unsigned int ivLen, + const unsigned char *nonceIn, unsigned int nonceLen, + const unsigned char *aad, unsigned int aadLen, + unsigned char *out, unsigned int *outLen, unsigned int maxout, + unsigned int tagLen, const unsigned char *in, unsigned int inLen) +{ + unsigned char *tag; + unsigned char iv[MAX_IV_LENGTH]; + unsigned char tagbuf[HASH_LENGTH_MAX]; + SECStatus rv; + + /* must have either context or the symKey set */ + if (!context) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + PORT_Assert(ivLen <= MAX_IV_LENGTH); + PORT_Assert(tagLen <= HASH_LENGTH_MAX); + if (!ivOut) { + ivOut = iv; /* caller doesn't need a returned, iv */ + } + + if (ivGen == CKG_NO_GENERATE) { + tls13_WriteNonce(ivIn, ivLen, nonceIn, nonceLen, ivOut, ivLen); + } else if (ivIn != ivOut) { + PORT_Memcpy(ivOut, ivIn, ivLen); + } + if (decrypt) { + inLen = inLen - tagLen; + tag = (unsigned char *)in + inLen; + /* tag is const on decrypt, but returned on encrypt */ + } else { + /* tag is written to a separate buffer, then added to the end + * of the actual output buffer. This allows output buffer to be larger + * than the input buffer and everything still work */ + tag = tagbuf; + } + rv = PK11_AEADOp(context, ivGen, fixedbits, ivOut, ivLen, aad, aadLen, + out, (int *)outLen, maxout, tag, tagLen, in, inLen); + /* on encrypt SSL always puts the tag at the end of the buffer */ + if ((rv == SECSuccess) && !(decrypt)) { + unsigned int len = *outLen; + /* make sure there is still space */ + if (len + tagLen > maxout) { + PORT_SetError(SEC_ERROR_OUTPUT_LEN); + return SECFailure; + } + PORT_Memcpy(out + len, tag, tagLen); + *outLen += tagLen; + } + return rv; +} + +static SECStatus +tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + PRUint32 innerLength; + SECItem oldAlpn = { siBuffer, NULL, 0 }; + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SSL_TRC(3, ("%d: TLS13[%d]: handle encrypted extensions", + SSL_GETPID(), ss->fd)); + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_ENCRYPTED_EXTENSIONS, + wait_encrypted_extensions); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeNumber(ss, &innerLength, 2, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Alert already sent. */ + } + if (innerLength != length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS, + illegal_parameter); + return SECFailure; + } + + /* If we are doing 0-RTT, then we already have an ALPN value. Stash + * it for comparison. */ + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent && + ss->xtnData.nextProtoState == SSL_NEXT_PROTO_EARLY_VALUE) { + oldAlpn = ss->xtnData.nextProto; + ss->xtnData.nextProto.data = NULL; + ss->xtnData.nextProtoState = SSL_NEXT_PROTO_NO_SUPPORT; + } + + rv = ssl3_ParseExtensions(ss, &b, &length); + if (rv != SECSuccess) { + return SECFailure; /* Error code set below */ + } + + /* Handle the rest of the extensions. */ + rv = ssl3_HandleParsedExtensions(ss, ssl_hs_encrypted_extensions); + if (rv != SECSuccess) { + return SECFailure; /* Error code set below */ + } + + /* We can only get here if we offered 0-RTT. */ + if (ssl3_ExtensionNegotiated(ss, ssl_tls13_early_data_xtn)) { + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_sent); + if (!ss->xtnData.selectedPsk) { + /* Illegal to accept 0-RTT without also accepting PSK. */ + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS, + illegal_parameter); + } + ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted; + + /* Check that the server negotiated the same ALPN (if any). */ + if (SECITEM_CompareItem(&oldAlpn, &ss->xtnData.nextProto)) { + SECITEM_FreeItem(&oldAlpn, PR_FALSE); + FATAL_ERROR(ss, SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID, + illegal_parameter); + return SECFailure; + } + /* Check that the server negotiated the same cipher suite. */ + if (ss->ssl3.hs.cipher_suite != ss->ssl3.hs.zeroRttSuite) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_ENCRYPTED_EXTENSIONS, + illegal_parameter); + return SECFailure; + } + } else if (ss->ssl3.hs.zeroRttState == ssl_0rtt_sent) { + /* Though we sent 0-RTT, the early_data extension wasn't present so the + * state is unmodified; the server must have rejected 0-RTT. */ + ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored; + ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial; + } else { + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_none || + (ss->ssl3.hs.helloRetry && + ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored)); + } + + SECITEM_FreeItem(&oldAlpn, PR_FALSE); + if (ss->ssl3.hs.kea_def->authKeyType == ssl_auth_psk) { + TLS13_SET_HS_STATE(ss, wait_finished); + } else { + TLS13_SET_HS_STATE(ss, wait_cert_request); + } + + /* Client is done with any PSKs */ + tls13_DestroyPskList(&ss->ssl3.hs.psks); + ss->xtnData.selectedPsk = NULL; + + return SECSuccess; +} + +static SECStatus +tls13_SendEncryptedExtensions(sslSocket *ss) +{ + sslBuffer extensions = SSL_BUFFER_EMPTY; + SECStatus rv; + + SSL_TRC(3, ("%d: TLS13[%d]: send encrypted extensions handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + rv = ssl_ConstructExtensions(ss, &extensions, ssl_hs_encrypted_extensions); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_encrypted_extensions, + SSL_BUFFER_LEN(&extensions) + 2); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + goto loser; + } + rv = ssl3_AppendBufferToHandshakeVariable(ss, &extensions, 2); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + goto loser; + } + sslBuffer_Clear(&extensions); + return SECSuccess; + +loser: + sslBuffer_Clear(&extensions); + return SECFailure; +} + +SECStatus +tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey) +{ + SECStatus rv = SECFailure; + SECItem buf = { siBuffer, NULL, 0 }; + unsigned int len; + SSLHashType hashAlg; + SSL3Hashes hash; + SSL3Hashes tbsHash; /* The hash "to be signed". */ + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SSL_TRC(3, ("%d: TLS13[%d]: send certificate_verify handshake", + SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_single); + rv = tls13_ComputeHandshakeHashes(ss, &hash); + if (rv != SECSuccess) { + return SECFailure; + } + + /* We should have picked a signature scheme when we received a + * CertificateRequest, or when we picked a server certificate. */ + PORT_Assert(ss->ssl3.hs.signatureScheme != ssl_sig_none); + if (ss->ssl3.hs.signatureScheme == ssl_sig_none) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + hashAlg = ssl_SignatureSchemeToHashType(ss->ssl3.hs.signatureScheme); + rv = tls13_AddContextToHashes(ss, &hash, hashAlg, + PR_TRUE, &tbsHash); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl3_SignHashes(ss, &tbsHash, privKey, &buf); + if (rv == SECSuccess && !ss->sec.isServer) { + /* Remember the info about the slot that did the signing. + * Later, when doing an SSL restart handshake, verify this. + * These calls are mere accessors, and can't fail. + */ + PK11SlotInfo *slot; + sslSessionID *sid = ss->sec.ci.sid; + + slot = PK11_GetSlotFromPrivateKey(privKey); + sid->u.ssl3.clAuthSeries = PK11_GetSlotSeries(slot); + sid->u.ssl3.clAuthSlotID = PK11_GetSlotID(slot); + sid->u.ssl3.clAuthModuleID = PK11_GetModuleID(slot); + sid->u.ssl3.clAuthValid = PR_TRUE; + PK11_FreeSlot(slot); + } + if (rv != SECSuccess) { + goto done; /* err code was set by ssl3_SignHashes */ + } + + len = buf.len + 2 + 2; + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_certificate_verify, len); + if (rv != SECSuccess) { + goto done; /* error code set by AppendHandshake */ + } + + rv = ssl3_AppendHandshakeNumber(ss, ss->ssl3.hs.signatureScheme, 2); + if (rv != SECSuccess) { + goto done; /* err set by AppendHandshakeNumber */ + } + + rv = ssl3_AppendHandshakeVariable(ss, buf.data, buf.len, 2); + if (rv != SECSuccess) { + goto done; /* error code set by AppendHandshake */ + } + +done: + /* For parity with the allocation functions, which don't use + * SECITEM_AllocItem(). */ + if (buf.data) + PORT_Free(buf.data); + return rv; +} + +/* Called from tls13_CompleteHandleHandshakeMessage() when it has deciphered a complete + * tls13 CertificateVerify message + * Caller must hold Handshake and RecvBuf locks. + */ +SECStatus +tls13_HandleCertificateVerify(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + sslDelegatedCredential *dc = ss->xtnData.peerDelegCred; + CERTSubjectPublicKeyInfo *spki; + SECKEYPublicKey *pubKey = NULL; + SECItem signed_hash = { siBuffer, NULL, 0 }; + SECStatus rv; + SSLSignatureScheme sigScheme; + SSLHashType hashAlg; + SSL3Hashes tbsHash; + SSL3Hashes hashes; + + SSL_TRC(3, ("%d: TLS13[%d]: handle certificate_verify handshake", + SSL_GETPID(), ss->fd)); + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_CERT_VERIFY, + wait_cert_verify); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = tls13_ComputeHandshakeHashes(ss, &hashes); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->firstHsDone) { + rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_certificate_verify, b, length); + } else { + rv = ssl_HashHandshakeMessage(ss, ssl_hs_certificate_verify, b, length); + } + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = ssl_ConsumeSignatureScheme(ss, &b, &length, &sigScheme); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, illegal_parameter); + return SECFailure; + } + + /* Set the |spki| used to verify the handshake. When verifying with a + * delegated credential (DC), this corresponds to the DC public key; + * otherwise it correspond to the public key of the peer's end-entity + * certificate. + */ + if (tls13_IsVerifyingWithDelegatedCredential(ss)) { + /* DelegatedCredential.cred.expected_cert_verify_algorithm is expected + * to match CertificateVerify.scheme. + * DelegatedCredential.cred.expected_cert_verify_algorithm must also be + * the same as was reported in ssl3_AuthCertificate. + */ + if (sigScheme != dc->expectedCertVerifyAlg || sigScheme != ss->sec.signatureScheme) { + FATAL_ERROR(ss, SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH, illegal_parameter); + return SECFailure; + } + + /* Verify the DC has three steps: (1) use the peer's end-entity + * certificate to verify DelegatedCredential.signature, (2) check that + * the certificate has the correct key usage, and (3) check that the DC + * hasn't expired. + */ + rv = tls13_VerifyDelegatedCredential(ss, dc); + if (rv != SECSuccess) { /* Calls FATAL_ERROR() */ + return SECFailure; + } + + SSL_TRC(3, ("%d: TLS13[%d]: Verifying with delegated credential", + SSL_GETPID(), ss->fd)); + spki = dc->spki; + } else { + spki = &ss->sec.peerCert->subjectPublicKeyInfo; + } + + rv = ssl_CheckSignatureSchemeConsistency(ss, sigScheme, spki); + if (rv != SECSuccess) { + /* Error set already */ + FATAL_ERROR(ss, PORT_GetError(), illegal_parameter); + return SECFailure; + } + hashAlg = ssl_SignatureSchemeToHashType(sigScheme); + + rv = tls13_AddContextToHashes(ss, &hashes, hashAlg, PR_FALSE, &tbsHash); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_DIGEST_FAILURE, internal_error); + return SECFailure; + } + + rv = ssl3_ConsumeHandshakeVariable(ss, &signed_hash, 2, &b, &length); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_VERIFY); + return SECFailure; + } + + if (length != 0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CERT_VERIFY, decode_error); + return SECFailure; + } + + pubKey = SECKEY_ExtractPublicKey(spki); + if (pubKey == NULL) { + ssl_MapLowLevelError(SSL_ERROR_EXTRACT_PUBLIC_KEY_FAILURE); + return SECFailure; + } + + rv = ssl_VerifySignedHashesWithPubKey(ss, pubKey, sigScheme, + &tbsHash, &signed_hash); + if (rv != SECSuccess) { + FATAL_ERROR(ss, PORT_GetError(), decrypt_error); + goto loser; + } + + /* Set the auth type and verify it is what we captured in ssl3_AuthCertificate */ + if (!ss->sec.isServer) { + ss->sec.authType = ssl_SignatureSchemeToAuthType(sigScheme); + + uint32_t prelimAuthKeyBits = ss->sec.authKeyBits; + rv = ssl_SetAuthKeyBits(ss, pubKey); + if (rv != SECSuccess) { + goto loser; /* Alert sent and code set. */ + } + + if (prelimAuthKeyBits != ss->sec.authKeyBits) { + FATAL_ERROR(ss, SSL_ERROR_DC_CERT_VERIFY_ALG_MISMATCH, illegal_parameter); + goto loser; + } + } + + /* Request a client certificate now if one was requested. */ + if (ss->ssl3.hs.clientCertRequested) { + PORT_Assert(!ss->sec.isServer); + rv = ssl3_BeginHandleCertificateRequest( + ss, ss->xtnData.sigSchemes, ss->xtnData.numSigSchemes, + &ss->xtnData.certReqAuthorities); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + goto loser; + } + } + + SECKEY_DestroyPublicKey(pubKey); + TLS13_SET_HS_STATE(ss, wait_finished); + return SECSuccess; + +loser: + SECKEY_DestroyPublicKey(pubKey); + return SECFailure; +} + +/* Compute the PSK binder hash over: + * Client HRR prefix, if present in ss->ssl3.hs.messages or ss->ssl3.hs.echInnerMessages, + * |len| bytes of |buf| */ +static SECStatus +tls13_ComputePskBinderHash(sslSocket *ss, PRUint8 *b, size_t length, + SSL3Hashes *hashes, SSLHashType hashType) +{ + SECStatus rv; + PK11Context *ctx = NULL; + sslBuffer *clientResidual = NULL; + if (!ss->sec.isServer) { + /* On the server, HRR residual is already buffered. */ + clientResidual = ss->ssl3.hs.echHpkeCtx ? &ss->ssl3.hs.echInnerMessages : &ss->ssl3.hs.messages; + } + PORT_Assert(ss->ssl3.hs.hashType == handshake_hash_unknown); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + PRINT_BUF(10, (NULL, "Binder computed over ClientHello", + b, length)); + + ctx = PK11_CreateDigestContext(ssl3_HashTypeToOID(hashType)); + if (!ctx) { + goto loser; + } + rv = PK11_DigestBegin(ctx); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + + if (clientResidual && clientResidual->len) { + PRINT_BUF(10, (NULL, " with HRR prefix", clientResidual->buf, + clientResidual->len)); + rv = PK11_DigestOp(ctx, clientResidual->buf, clientResidual->len); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + } + + if (IS_DTLS(ss) && !ss->sec.isServer) { + /* Removing the unnecessary header fields. + * See ssl3_AppendHandshakeHeader.*/ + PORT_Assert(length >= 12); + rv = PK11_DigestOp(ctx, b, 4); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + rv = PK11_DigestOp(ctx, b + 12, length - 12); + } else { + rv = PK11_DigestOp(ctx, b, length); + } + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + rv = PK11_DigestFinal(ctx, hashes->u.raw, &hashes->len, sizeof(hashes->u.raw)); + if (rv != SECSuccess) { + ssl_MapLowLevelError(SSL_ERROR_SHA_DIGEST_FAILURE); + goto loser; + } + + PK11_DestroyContext(ctx, PR_TRUE); + PRINT_BUF(10, (NULL, "PSK Binder hash", hashes->u.raw, hashes->len)); + return SECSuccess; + +loser: + if (ctx) { + PK11_DestroyContext(ctx, PR_TRUE); + } + return SECFailure; +} + +/* Compute and inject the PSK Binder for sending. + * + * When sending a ClientHello, we construct all the extensions with a dummy + * value for the binder. To construct the binder, we commit the entire message + * up to the point where the binders start. Then we calculate the hash using + * the saved message (in ss->ssl3.hs.messages). This is written over the dummy + * binder, after which we write the remainder of the binder extension. */ +SECStatus +tls13_WriteExtensionsWithBinder(sslSocket *ss, sslBuffer *extensions, sslBuffer *chBuf) +{ + SSL3Hashes hashes; + SECStatus rv; + + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)); + sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks); + unsigned int size = tls13_GetHashSizeForHash(psk->hash); + unsigned int prefixLen = extensions->len - size - 3; + unsigned int finishedLen; + + PORT_Assert(extensions->len >= size + 3); + + rv = sslBuffer_AppendNumber(chBuf, extensions->len, 2); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Only write the extension up to the point before the binders. Assume that + * the pre_shared_key extension is at the end of the buffer. Don't write + * the binder, or the lengths that precede it (a 2 octet length for the list + * of all binders, plus a 1 octet length for the binder length). */ + rv = sslBuffer_Append(chBuf, extensions->buf, prefixLen); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Calculate the binder based on what has been written out. */ + rv = tls13_ComputePskBinderHash(ss, chBuf->buf, chBuf->len, &hashes, psk->hash); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Write the binder into the extensions buffer, over the zeros we reserved + * previously. This avoids an allocation and means that we don't need a + * separate write for the extra bits that precede the binder. */ + PORT_Assert(psk->binderKey); + rv = tls13_ComputeFinished(ss, psk->binderKey, + psk->hash, &hashes, PR_TRUE, + extensions->buf + extensions->len - size, + &finishedLen, size); + if (rv != SECSuccess) { + return SECFailure; + } + PORT_Assert(finishedLen == size); + + /* Write out the remainder of the extension. */ + rv = sslBuffer_Append(chBuf, extensions->buf + prefixLen, + extensions->len - prefixLen); + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +static SECStatus +tls13_ComputeFinished(sslSocket *ss, PK11SymKey *baseKey, + SSLHashType hashType, const SSL3Hashes *hashes, + PRBool sending, PRUint8 *output, unsigned int *outputLen, + unsigned int maxOutputLen) +{ + SECStatus rv; + PK11Context *hmacCtx = NULL; + CK_MECHANISM_TYPE macAlg = tls13_GetHmacMechanismFromHash(hashType); + SECItem param = { siBuffer, NULL, 0 }; + unsigned int outputLenUint; + const char *label = kHkdfLabelFinishedSecret; + PK11SymKey *secret = NULL; + + PORT_Assert(baseKey); + SSL_TRC(3, ("%d: TLS13[%d]: %s calculate finished", + SSL_GETPID(), ss->fd, SSL_ROLE(ss))); + PRINT_BUF(50, (ss, "Handshake hash", hashes->u.raw, hashes->len)); + + /* Now derive the appropriate finished secret from the base secret. */ + rv = tls13_HkdfExpandLabel(baseKey, hashType, + NULL, 0, label, strlen(label), + tls13_GetHmacMechanismFromHash(hashType), + tls13_GetHashSizeForHash(hashType), + ss->protocolVariant, &secret); + if (rv != SECSuccess) { + goto abort; + } + + PORT_Assert(hashes->len == tls13_GetHashSizeForHash(hashType)); + hmacCtx = PK11_CreateContextBySymKey(macAlg, CKA_SIGN, + secret, ¶m); + if (!hmacCtx) { + goto abort; + } + + rv = PK11_DigestBegin(hmacCtx); + if (rv != SECSuccess) + goto abort; + + rv = PK11_DigestOp(hmacCtx, hashes->u.raw, hashes->len); + if (rv != SECSuccess) + goto abort; + + PORT_Assert(maxOutputLen >= tls13_GetHashSizeForHash(hashType)); + rv = PK11_DigestFinal(hmacCtx, output, &outputLenUint, maxOutputLen); + if (rv != SECSuccess) + goto abort; + *outputLen = outputLenUint; + + PK11_FreeSymKey(secret); + PK11_DestroyContext(hmacCtx, PR_TRUE); + PRINT_BUF(50, (ss, "finished value", output, outputLenUint)); + return SECSuccess; + +abort: + if (secret) { + PK11_FreeSymKey(secret); + } + + if (hmacCtx) { + PK11_DestroyContext(hmacCtx, PR_TRUE); + } + + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; +} + +static SECStatus +tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey) +{ + SECStatus rv; + PRUint8 finishedBuf[TLS13_MAX_FINISHED_SIZE]; + unsigned int finishedLen; + SSL3Hashes hashes; + + SSL_TRC(3, ("%d: TLS13[%d]: send finished handshake", SSL_GETPID(), ss->fd)); + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + rv = tls13_ComputeHandshakeHashes(ss, &hashes); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + ssl_GetSpecReadLock(ss); + rv = tls13_ComputeFinished(ss, baseKey, tls13_GetHash(ss), &hashes, PR_TRUE, + finishedBuf, &finishedLen, sizeof(finishedBuf)); + ssl_ReleaseSpecReadLock(ss); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_finished, finishedLen); + if (rv != SECSuccess) { + return SECFailure; /* Error code already set. */ + } + + rv = ssl3_AppendHandshake(ss, finishedBuf, finishedLen); + if (rv != SECSuccess) { + return SECFailure; /* Error code already set. */ + } + + /* TODO(ekr@rtfm.com): Record key log */ + return SECSuccess; +} + +static SECStatus +tls13_VerifyFinished(sslSocket *ss, SSLHandshakeType message, + PK11SymKey *secret, + PRUint8 *b, PRUint32 length, + const SSL3Hashes *hashes) +{ + SECStatus rv; + PRUint8 finishedBuf[TLS13_MAX_FINISHED_SIZE]; + unsigned int finishedLen; + + if (!hashes) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + rv = tls13_ComputeFinished(ss, secret, tls13_GetHash(ss), hashes, PR_FALSE, + finishedBuf, &finishedLen, sizeof(finishedBuf)); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + if (length != finishedLen) { +#ifndef UNSAFE_FUZZER_MODE + FATAL_ERROR(ss, message == ssl_hs_finished ? SSL_ERROR_RX_MALFORMED_FINISHED : SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; +#endif + } + + if (NSS_SecureMemcmp(b, finishedBuf, finishedLen) != 0) { +#ifndef UNSAFE_FUZZER_MODE + FATAL_ERROR(ss, SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE, + decrypt_error); + return SECFailure; +#endif + } + + return SECSuccess; +} + +static SECStatus +tls13_CommonHandleFinished(sslSocket *ss, PK11SymKey *key, + PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + SSL3Hashes hashes; + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_FINISHED, + wait_finished); + if (rv != SECSuccess) { + return SECFailure; + } + ss->ssl3.hs.endOfFlight = PR_TRUE; + + rv = tls13_ComputeHandshakeHashes(ss, &hashes); + if (rv != SECSuccess) { + LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + if (ss->firstHsDone) { + rv = ssl_HashPostHandshakeMessage(ss, ssl_hs_finished, b, length); + } else { + rv = ssl_HashHandshakeMessage(ss, ssl_hs_finished, b, length); + } + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + return tls13_VerifyFinished(ss, ssl_hs_finished, + key, b, length, &hashes); +} + +static SECStatus +tls13_ClientHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SSL_TRC(3, ("%d: TLS13[%d]: client handle finished handshake", + SSL_GETPID(), ss->fd)); + + rv = tls13_CommonHandleFinished(ss, ss->ssl3.hs.serverHsTrafficSecret, + b, length); + if (rv != SECSuccess) { + return SECFailure; + } + + return tls13_SendClientSecondRound(ss); +} + +static SECStatus +tls13_ServerHandleFinished(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + SSL_TRC(3, ("%d: TLS13[%d]: server handle finished handshake", + SSL_GETPID(), ss->fd)); + + if (!tls13_ShouldRequestClientAuth(ss)) { + /* Receiving this message might be the first sign we have that + * early data is over, so pretend we received EOED. */ + rv = tls13_MaybeHandleSuppressedEndOfEarlyData(ss); + if (rv != SECSuccess) { + return SECFailure; /* Code already set. */ + } + + if (!tls13_IsPostHandshake(ss)) { + /* Finalize the RTT estimate. */ + ss->ssl3.hs.rttEstimate = ssl_Time(ss) - ss->ssl3.hs.rttEstimate; + } + } + + rv = tls13_CommonHandleFinished(ss, + ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret, + b, length); + if (rv != SECSuccess) { + return SECFailure; + } + + if (ss->firstHsDone) { + TLS13_SET_HS_STATE(ss, idle_handshake); + + PORT_Assert(ss->ssl3.hs.shaPostHandshake != NULL); + PK11_DestroyContext(ss->ssl3.hs.shaPostHandshake, PR_TRUE); + ss->ssl3.hs.shaPostHandshake = NULL; + + ss->ssl3.clientCertRequested = PR_FALSE; + + if (ss->ssl3.hs.keyUpdateDeferred) { + rv = tls13_SendKeyUpdate(ss, ss->ssl3.hs.deferredKeyUpdateRequest, + PR_FALSE); + if (rv != SECSuccess) { + return SECFailure; /* error is set. */ + } + ss->ssl3.hs.keyUpdateDeferred = PR_FALSE; + } + + return SECSuccess; + } + + if (!tls13_ShouldRequestClientAuth(ss) && + (ss->ssl3.hs.zeroRttState != ssl_0rtt_done)) { + dtls_ReceivedFirstMessageInFlight(ss); + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, + ssl_secret_read, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + if (IS_DTLS(ss)) { + ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, TrafficKeyClearText); + /* We need to keep the handshake cipher spec so we can + * read re-transmitted client Finished. */ + rv = dtls_StartTimer(ss, ss->ssl3.hs.hdTimer, + DTLS_RETRANSMIT_FINISHED_MS, + dtls13_HolddownTimerCb); + if (rv != SECSuccess) { + return SECFailure; + } + } + + rv = tls13_ComputeFinalSecrets(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = tls13_FinishHandshake(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + ssl_GetXmitBufLock(ss); + /* If resumption, authType is the original value and not ssl_auth_psk. */ + if (ss->opt.enableSessionTickets && ss->sec.authType != ssl_auth_psk) { + rv = tls13_SendNewSessionTicket(ss, NULL, 0); + if (rv != SECSuccess) { + goto loser; + } + rv = ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + goto loser; + } + } + ssl_ReleaseXmitBufLock(ss); + return SECSuccess; + +loser: + ssl_ReleaseXmitBufLock(ss); + return SECFailure; +} + +static SECStatus +tls13_FinishHandshake(sslSocket *ss) +{ + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + PORT_Assert(ss->ssl3.hs.restartTarget == NULL); + + /* The first handshake is now completed. */ + ss->handshake = NULL; + + /* Don't need this. */ + PK11_FreeSymKey(ss->ssl3.hs.clientHsTrafficSecret); + ss->ssl3.hs.clientHsTrafficSecret = NULL; + PK11_FreeSymKey(ss->ssl3.hs.serverHsTrafficSecret); + ss->ssl3.hs.serverHsTrafficSecret = NULL; + + TLS13_SET_HS_STATE(ss, idle_handshake); + + return ssl_FinishHandshake(ss); +} + +/* Do the parts of sending the client's second round that require + * the XmitBuf lock. */ +static SECStatus +tls13_SendClientSecondFlight(sslSocket *ss) +{ + SECStatus rv; + unsigned int offset = 0; + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + PORT_Assert(!ss->ssl3.hs.clientCertificatePending); + + PRBool sendClientCert = !ss->ssl3.sendEmptyCert && + ss->ssl3.clientCertChain != NULL && + ss->ssl3.clientPrivateKey != NULL; + + if (ss->firstHsDone) { + offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf); + } + + if (ss->ssl3.sendEmptyCert) { + ss->ssl3.sendEmptyCert = PR_FALSE; + rv = ssl3_SendEmptyCertificate(ss); + /* Don't send verify */ + if (rv != SECSuccess) { + goto alert_error; /* error code is set. */ + } + } else if (sendClientCert) { + rv = tls13_SendCertificate(ss); + if (rv != SECSuccess) { + goto alert_error; /* err code was set. */ + } + } + + if (ss->firstHsDone) { + rv = ssl3_UpdatePostHandshakeHashes(ss, + SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset, + SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset); + if (rv != SECSuccess) { + goto alert_error; /* err code was set. */ + } + } + + if (ss->ssl3.hs.clientCertRequested) { + SECITEM_FreeItem(&ss->xtnData.certReqContext, PR_FALSE); + if (ss->xtnData.certReqAuthorities.arena) { + PORT_FreeArena(ss->xtnData.certReqAuthorities.arena, PR_FALSE); + ss->xtnData.certReqAuthorities.arena = NULL; + } + PORT_Memset(&ss->xtnData.certReqAuthorities, 0, + sizeof(ss->xtnData.certReqAuthorities)); + ss->ssl3.hs.clientCertRequested = PR_FALSE; + } + + if (sendClientCert) { + if (ss->firstHsDone) { + offset = SSL_BUFFER_LEN(&ss->sec.ci.sendBuf); + } + + rv = tls13_SendCertificateVerify(ss, ss->ssl3.clientPrivateKey); + SECKEY_DestroyPrivateKey(ss->ssl3.clientPrivateKey); + ss->ssl3.clientPrivateKey = NULL; + if (rv != SECSuccess) { + goto alert_error; /* err code was set. */ + } + + if (ss->firstHsDone) { + rv = ssl3_UpdatePostHandshakeHashes(ss, + SSL_BUFFER_BASE(&ss->sec.ci.sendBuf) + offset, + SSL_BUFFER_LEN(&ss->sec.ci.sendBuf) - offset); + if (rv != SECSuccess) { + goto alert_error; /* err code was set. */ + } + } + } + + rv = tls13_SendFinished(ss, ss->firstHsDone ? ss->ssl3.hs.clientTrafficSecret : ss->ssl3.hs.clientHsTrafficSecret); + if (rv != SECSuccess) { + goto alert_error; /* err code was set. */ + } + rv = ssl3_FlushHandshake(ss, 0); + if (rv != SECSuccess) { + /* No point in sending an alert here because we're not going to + * be able to send it if we couldn't flush the handshake. */ + goto error; + } + + return SECSuccess; + +alert_error: + FATAL_ERROR(ss, PORT_GetError(), internal_error); + return SECFailure; +error: + LOG_ERROR(ss, PORT_GetError()); + return SECFailure; +} + +static SECStatus +tls13_SendClientSecondRound(sslSocket *ss) +{ + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss)); + PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss)); + + /* Defer client authentication sending if we are still waiting for server + * authentication. This avoids unnecessary disclosure of client credentials + * to an unauthenticated server. + */ + if (ss->ssl3.hs.restartTarget) { + PR_NOT_REACHED("unexpected ss->ssl3.hs.restartTarget"); + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + if (ss->ssl3.hs.authCertificatePending || ss->ssl3.hs.clientCertificatePending) { + SSL_TRC(3, ("%d: TLS13[%d]: deferring tls13_SendClientSecondRound because" + " certificate authentication is still pending.", + SSL_GETPID(), ss->fd)); + ss->ssl3.hs.restartTarget = tls13_SendClientSecondRound; + PORT_SetError(PR_WOULD_BLOCK_ERROR); + return SECFailure; + } + + rv = tls13_ComputeApplicationSecrets(ss); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + if (ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted) { + ssl_GetXmitBufLock(ss); /*******************************/ + rv = tls13_SendEndOfEarlyData(ss); + ssl_ReleaseXmitBufLock(ss); /*******************************/ + if (rv != SECSuccess) { + return SECFailure; /* Error code already set. */ + } + } else if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss) && + ss->ssl3.hs.zeroRttState == ssl_0rtt_none && + !ss->ssl3.hs.helloRetry) { + ssl_GetXmitBufLock(ss); /*******************************/ + rv = ssl3_SendChangeCipherSpecsInt(ss); + ssl_ReleaseXmitBufLock(ss); /*******************************/ + if (rv != SECSuccess) { + return rv; + } + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake, + ssl_secret_write, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_INIT_CIPHER_SUITE_FAILURE, internal_error); + return SECFailure; + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, + ssl_secret_read, PR_FALSE); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + ssl_GetXmitBufLock(ss); /*******************************/ + /* This call can't block, as clientAuthCertificatePending is checked above */ + rv = tls13_SendClientSecondFlight(ss); + ssl_ReleaseXmitBufLock(ss); /*******************************/ + if (rv != SECSuccess) { + return SECFailure; + } + rv = tls13_SetCipherSpec(ss, TrafficKeyApplicationData, + ssl_secret_write, PR_FALSE); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + rv = tls13_ComputeFinalSecrets(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + /* The handshake is now finished */ + return tls13_FinishHandshake(ss); +} + +/* + * enum { (65535) } TicketExtensionType; + * + * struct { + * TicketExtensionType extension_type; + * opaque extension_data<0..2^16-1>; + * } TicketExtension; + * + * struct { + * uint32 ticket_lifetime; + * uint32 ticket_age_add; + * opaque ticket_nonce<1..255>; + * opaque ticket<1..2^16-1>; + * TicketExtension extensions<0..2^16-2>; + * } NewSessionTicket; + */ + +static SECStatus +tls13_SendNewSessionTicket(sslSocket *ss, const PRUint8 *appToken, + unsigned int appTokenLen) +{ + PRUint16 message_length; + PK11SymKey *secret; + SECItem ticket_data = { 0, NULL, 0 }; + SECStatus rv; + NewSessionTicket ticket = { 0 }; + PRUint32 max_early_data_size_len = 0; + PRUint32 greaseLen = 0; + PRUint8 ticketNonce[sizeof(ss->ssl3.hs.ticketNonce)]; + sslBuffer ticketNonceBuf = SSL_BUFFER(ticketNonce); + + SSL_TRC(3, ("%d: TLS13[%d]: send new session ticket message %d", + SSL_GETPID(), ss->fd, ss->ssl3.hs.ticketNonce)); + + ticket.flags = 0; + if (ss->opt.enable0RttData) { + ticket.flags |= ticket_allow_early_data; + max_early_data_size_len = 8; /* type + len + value. */ + } + ticket.ticket_lifetime_hint = ssl_ticket_lifetime; + + if (ss->opt.enableGrease) { + greaseLen = 4; /* type + len + 0 (empty) */ + } + + /* The ticket age obfuscator. */ + rv = PK11_GenerateRandom((PRUint8 *)&ticket.ticket_age_add, + sizeof(ticket.ticket_age_add)); + if (rv != SECSuccess) + goto loser; + + rv = sslBuffer_AppendNumber(&ticketNonceBuf, ss->ssl3.hs.ticketNonce, + sizeof(ticketNonce)); + if (rv != SECSuccess) { + goto loser; + } + ++ss->ssl3.hs.ticketNonce; + rv = tls13_HkdfExpandLabel(ss->ssl3.hs.resumptionMasterSecret, + tls13_GetHash(ss), + ticketNonce, sizeof(ticketNonce), + kHkdfLabelResumption, + strlen(kHkdfLabelResumption), + CKM_HKDF_DERIVE, + tls13_GetHashSize(ss), + ss->protocolVariant, &secret); + if (rv != SECSuccess) { + goto loser; + } + + rv = ssl3_EncodeSessionTicket(ss, &ticket, appToken, appTokenLen, + secret, &ticket_data); + PK11_FreeSymKey(secret); + if (rv != SECSuccess) + goto loser; + + message_length = + 4 + /* lifetime */ + 4 + /* ticket_age_add */ + 1 + sizeof(ticketNonce) + /* ticket_nonce */ + 2 + /* extensions lentgh */ + max_early_data_size_len + /* max_early_data_size extension length */ + greaseLen + /* GREASE extension length */ + 2 + /* ticket length */ + ticket_data.len; + + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_new_session_ticket, + message_length); + if (rv != SECSuccess) + goto loser; + + /* This is a fixed value. */ + rv = ssl3_AppendHandshakeNumber(ss, ssl_ticket_lifetime, 4); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendHandshakeNumber(ss, ticket.ticket_age_add, 4); + if (rv != SECSuccess) + goto loser; + + /* The ticket nonce. */ + rv = ssl3_AppendHandshakeVariable(ss, ticketNonce, sizeof(ticketNonce), 1); + if (rv != SECSuccess) + goto loser; + + /* Encode the ticket. */ + rv = ssl3_AppendHandshakeVariable( + ss, ticket_data.data, ticket_data.len, 2); + if (rv != SECSuccess) + goto loser; + + /* Extensions */ + rv = ssl3_AppendHandshakeNumber(ss, max_early_data_size_len + greaseLen, 2); + if (rv != SECSuccess) + goto loser; + + /* GREASE NewSessionTicket: + * When sending a NewSessionTicket message in TLS 1.3, a server MAY select + * one or more GREASE extension values and advertise them as extensions + * with varying length and contents [RFC8701, SEction 4.1]. */ + if (ss->opt.enableGrease) { + PR_ASSERT(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + + PRUint16 grease; + rv = tls13_RandomGreaseValue(&grease); + if (rv != SECSuccess) + goto loser; + /* Extension type */ + rv = ssl3_AppendHandshakeNumber(ss, grease, 2); + if (rv != SECSuccess) + goto loser; + /* Extension length */ + rv = ssl3_AppendHandshakeNumber(ss, 0, 2); + if (rv != SECSuccess) + goto loser; + } + + /* Max early data size extension. */ + if (max_early_data_size_len) { + rv = ssl3_AppendHandshakeNumber( + ss, ssl_tls13_early_data_xtn, 2); + if (rv != SECSuccess) + goto loser; + + /* Length */ + rv = ssl3_AppendHandshakeNumber(ss, 4, 2); + if (rv != SECSuccess) + goto loser; + + rv = ssl3_AppendHandshakeNumber(ss, ss->opt.maxEarlyDataSize, 4); + if (rv != SECSuccess) + goto loser; + } + + SECITEM_FreeItem(&ticket_data, PR_FALSE); + return SECSuccess; + +loser: + if (ticket_data.data) { + SECITEM_FreeItem(&ticket_data, PR_FALSE); + } + return SECFailure; +} + +SECStatus +SSLExp_SendSessionTicket(PRFileDesc *fd, const PRUint8 *token, + unsigned int tokenLen) +{ + sslSocket *ss; + SECStatus rv; + + ss = ssl_FindSocket(fd); + if (!ss) { + return SECFailure; + } + + if (IS_DTLS(ss)) { + PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION); + return SECFailure; + } + + if (!ss->sec.isServer || !tls13_IsPostHandshake(ss) || + tokenLen > 0xffff) { + PORT_SetError(SEC_ERROR_INVALID_ARGS); + return SECFailure; + } + + /* Disable tickets if we can trace this connection back to a PSK. + * We aren't able to issue tickets (currently) without a certificate. + * As PSK =~ resumption, there is no reason to do this. */ + if (ss->sec.authType == ssl_auth_psk) { + PORT_SetError(SSL_ERROR_FEATURE_DISABLED); + return SECFailure; + } + + ssl_GetSSL3HandshakeLock(ss); + ssl_GetXmitBufLock(ss); + rv = tls13_SendNewSessionTicket(ss, token, tokenLen); + if (rv == SECSuccess) { + rv = ssl3_FlushHandshake(ss, 0); + } + ssl_ReleaseXmitBufLock(ss); + ssl_ReleaseSSL3HandshakeLock(ss); + + return rv; +} + +static SECStatus +tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + PRUint32 utmp; + NewSessionTicket ticket = { 0 }; + SECItem data; + SECItem ticket_nonce; + SECItem ticket_data; + + SSL_TRC(3, ("%d: TLS13[%d]: handle new session ticket message", + SSL_GETPID(), ss->fd)); + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET, + idle_handshake); + if (rv != SECSuccess) { + return SECFailure; + } + if (!tls13_IsPostHandshake(ss) || ss->sec.isServer) { + FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_NEW_SESSION_TICKET, + unexpected_message); + return SECFailure; + } + + ticket.received_timestamp = ssl_Time(ss); + rv = ssl3_ConsumeHandshakeNumber(ss, &ticket.ticket_lifetime_hint, 4, &b, + &length); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, + decode_error); + return SECFailure; + } + ticket.ticket.type = siBuffer; + + rv = ssl3_ConsumeHandshake(ss, &utmp, sizeof(utmp), + &b, &length); + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET); + return SECFailure; + } + ticket.ticket_age_add = PR_ntohl(utmp); + + /* The nonce. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ticket_nonce, 1, &b, &length); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, + decode_error); + return SECFailure; + } + + /* Get the ticket value. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &ticket_data, 2, &b, &length); + if (rv != SECSuccess || !ticket_data.len) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, + decode_error); + return SECFailure; + } + + /* Parse extensions. */ + rv = ssl3_ConsumeHandshakeVariable(ss, &data, 2, &b, &length); + if (rv != SECSuccess || length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, + decode_error); + return SECFailure; + } + + rv = ssl3_HandleExtensions(ss, &data.data, + &data.len, ssl_hs_new_session_ticket); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET, + decode_error); + return SECFailure; + } + if (ss->xtnData.max_early_data_size) { + ticket.flags |= ticket_allow_early_data; + ticket.max_early_data_size = ss->xtnData.max_early_data_size; + } + + if (!ss->opt.noCache) { + PK11SymKey *secret; + + PORT_Assert(ss->sec.ci.sid); + rv = SECITEM_CopyItem(NULL, &ticket.ticket, &ticket_data); + if (rv != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + PRINT_BUF(50, (ss, "Caching session ticket", + ticket.ticket.data, + ticket.ticket.len)); + + /* Replace a previous session ticket when + * we receive a second NewSessionTicket message. */ + if (ss->sec.ci.sid->cached == in_client_cache || + ss->sec.ci.sid->cached == in_external_cache) { + /* Create a new session ID. */ + sslSessionID *sid = ssl3_NewSessionID(ss, PR_FALSE); + if (!sid) { + return SECFailure; + } + + /* Copy over the peerCert. */ + PORT_Assert(ss->sec.ci.sid->peerCert); + sid->peerCert = CERT_DupCertificate(ss->sec.ci.sid->peerCert); + if (!sid->peerCert) { + ssl_FreeSID(sid); + return SECFailure; + } + + /* Destroy the old SID. */ + ssl_UncacheSessionID(ss); + ssl_FreeSID(ss->sec.ci.sid); + ss->sec.ci.sid = sid; + } + + ssl3_SetSIDSessionTicket(ss->sec.ci.sid, &ticket); + PORT_Assert(!ticket.ticket.data); + + rv = tls13_HkdfExpandLabel(ss->ssl3.hs.resumptionMasterSecret, + tls13_GetHash(ss), + ticket_nonce.data, ticket_nonce.len, + kHkdfLabelResumption, + strlen(kHkdfLabelResumption), + CKM_HKDF_DERIVE, + tls13_GetHashSize(ss), + ss->protocolVariant, &secret); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = ssl3_FillInCachedSID(ss, ss->sec.ci.sid, secret); + PK11_FreeSymKey(secret); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Cache the session. */ + ssl_CacheSessionID(ss); + } + + return SECSuccess; +} + +#define _M_NONE 0 +#define _M(a) (1 << PR_MIN(a, 31)) +#define _M1(a) (_M(ssl_hs_##a)) +#define _M2(a, b) (_M1(a) | _M1(b)) +#define _M3(a, b, c) (_M1(a) | _M2(b, c)) + +static const struct { + PRUint16 ex_value; + PRUint32 messages; +} KnownExtensions[] = { + { ssl_server_name_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_supported_groups_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_signature_algorithms_xtn, _M2(client_hello, certificate_request) }, + { ssl_signature_algorithms_cert_xtn, _M2(client_hello, + certificate_request) }, + { ssl_use_srtp_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_app_layer_protocol_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_padding_xtn, _M1(client_hello) }, + { ssl_tls13_key_share_xtn, _M3(client_hello, server_hello, + hello_retry_request) }, + { ssl_tls13_pre_shared_key_xtn, _M2(client_hello, server_hello) }, + { ssl_tls13_psk_key_exchange_modes_xtn, _M1(client_hello) }, + { ssl_tls13_early_data_xtn, _M3(client_hello, encrypted_extensions, + new_session_ticket) }, + { ssl_signed_cert_timestamp_xtn, _M3(client_hello, certificate_request, + certificate) }, + { ssl_cert_status_xtn, _M3(client_hello, certificate_request, + certificate) }, + { ssl_delegated_credentials_xtn, _M2(client_hello, certificate) }, + { ssl_tls13_cookie_xtn, _M2(client_hello, hello_retry_request) }, + { ssl_tls13_certificate_authorities_xtn, _M2(client_hello, certificate_request) }, + { ssl_tls13_supported_versions_xtn, _M3(client_hello, server_hello, + hello_retry_request) }, + { ssl_record_size_limit_xtn, _M2(client_hello, encrypted_extensions) }, + { ssl_tls13_encrypted_client_hello_xtn, _M3(client_hello, encrypted_extensions, hello_retry_request) }, + { ssl_tls13_outer_extensions_xtn, _M_NONE /* Encoding/decoding only */ }, + { ssl_tls13_post_handshake_auth_xtn, _M1(client_hello) }, + { ssl_certificate_compression_xtn, _M2(client_hello, certificate_request) } +}; + +tls13ExtensionStatus +tls13_ExtensionStatus(PRUint16 extension, SSLHandshakeType message) +{ + unsigned int i; + + PORT_Assert((message == ssl_hs_client_hello) || + (message == ssl_hs_server_hello) || + (message == ssl_hs_hello_retry_request) || + (message == ssl_hs_encrypted_extensions) || + (message == ssl_hs_new_session_ticket) || + (message == ssl_hs_certificate) || + (message == ssl_hs_certificate_request)); + + for (i = 0; i < PR_ARRAY_SIZE(KnownExtensions); i++) { + /* Hacky check for message numbers > 30. */ + PORT_Assert(!(KnownExtensions[i].messages & (1U << 31))); + if (KnownExtensions[i].ex_value == extension) { + break; + } + } + if (i >= PR_ARRAY_SIZE(KnownExtensions)) { + return tls13_extension_unknown; + } + + /* Return "disallowed" if the message mask bit isn't set. */ + if (!(_M(message) & KnownExtensions[i].messages)) { + return tls13_extension_disallowed; + } + + return tls13_extension_allowed; +} + +#undef _M +#undef _M1 +#undef _M2 +#undef _M3 + +/* We cheat a bit on additional data because the AEAD interface + * which doesn't have room for the record number. The AAD we + * format is serialized record number followed by the true AD + * (i.e., the record header) plus the serialized record number. */ +static SECStatus +tls13_FormatAdditionalData( + sslSocket *ss, + const PRUint8 *header, unsigned int headerLen, + DTLSEpoch epoch, sslSequenceNumber seqNum, + PRUint8 *aad, unsigned int *aadLength, unsigned int maxLength) +{ + SECStatus rv; + sslBuffer buf = SSL_BUFFER_FIXED(aad, maxLength); + + if (IS_DTLS_1_OR_12(ss)) { + rv = sslBuffer_AppendNumber(&buf, epoch, 2); + if (rv != SECSuccess) { + return SECFailure; + } + } + rv = sslBuffer_AppendNumber(&buf, seqNum, IS_DTLS_1_OR_12(ss) ? 6 : 8); + if (rv != SECSuccess) { + return SECFailure; + } + + rv = sslBuffer_Append(&buf, header, headerLen); + if (rv != SECSuccess) { + return SECFailure; + } + + *aadLength = buf.len; + + return SECSuccess; +} + +PRInt32 +tls13_LimitEarlyData(sslSocket *ss, SSLContentType type, PRInt32 toSend) +{ + PRInt32 reduced; + + PORT_Assert(type == ssl_ct_application_data); + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + PORT_Assert(!ss->firstHsDone); + if (ss->ssl3.cwSpec->epoch != TrafficKeyEarlyApplicationData) { + return toSend; + } + + if (IS_DTLS(ss) && toSend > ss->ssl3.cwSpec->earlyDataRemaining) { + /* Don't split application data records in DTLS. */ + return 0; + } + + reduced = PR_MIN(toSend, ss->ssl3.cwSpec->earlyDataRemaining); + ss->ssl3.cwSpec->earlyDataRemaining -= reduced; + return reduced; +} + +SECStatus +tls13_ProtectRecord(sslSocket *ss, + ssl3CipherSpec *cwSpec, + SSLContentType type, + const PRUint8 *pIn, + PRUint32 contentLen, + sslBuffer *wrBuf) +{ + const ssl3BulkCipherDef *cipher_def = cwSpec->cipherDef; + const int tagLen = cipher_def->tag_size; + SECStatus rv; + + PORT_Assert(cwSpec->direction == ssl_secret_write); + SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) protect 0x%0llx len=%u", + SSL_GETPID(), ss->fd, cwSpec, cwSpec->epoch, cwSpec->phase, + cwSpec->nextSeqNum, contentLen)); + + if (contentLen + 1 + tagLen > SSL_BUFFER_SPACE(wrBuf)) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + /* Copy the data into the wrBuf. We're going to encrypt in-place + * in the AEAD branch anyway */ + PORT_Memcpy(SSL_BUFFER_NEXT(wrBuf), pIn, contentLen); + + if (cipher_def->calg == ssl_calg_null) { + /* Shortcut for plaintext */ + rv = sslBuffer_Skip(wrBuf, contentLen, NULL); + PORT_Assert(rv == SECSuccess); + } else { + PRUint8 hdr[13]; + sslBuffer buf = SSL_BUFFER_FIXED(hdr, sizeof(hdr)); + PRBool needsLength; + PRUint8 aad[21]; + const int ivLen = cipher_def->iv_size + cipher_def->explicit_nonce_size; + unsigned int ivOffset = ivLen - sizeof(sslSequenceNumber); + unsigned char ivOut[MAX_IV_LENGTH]; + + unsigned int aadLen; + unsigned int len; + + PORT_Assert(cipher_def->type == type_aead); + + /* If the following condition holds, we can skip the padding logic for + * DTLS 1.3 (4.2.3). This will be the case until we support a cipher + * with tag length < 15B. */ + PORT_Assert(tagLen + 1 /* cType */ >= 16); + + /* Add the content type at the end. */ + *(SSL_BUFFER_NEXT(wrBuf) + contentLen) = type; + + /* Create the header (ugly that we have to do it twice). */ + rv = ssl_InsertRecordHeader(ss, cwSpec, ssl_ct_application_data, + &buf, &needsLength); + if (rv != SECSuccess) { + return SECFailure; + } + if (needsLength) { + rv = sslBuffer_AppendNumber(&buf, contentLen + 1 + tagLen, 2); + if (rv != SECSuccess) { + return SECFailure; + } + } + rv = tls13_FormatAdditionalData(ss, SSL_BUFFER_BASE(&buf), SSL_BUFFER_LEN(&buf), + cwSpec->epoch, cwSpec->nextSeqNum, + aad, &aadLen, sizeof(aad)); + if (rv != SECSuccess) { + return SECFailure; + } + /* set up initial IV value */ + ivOffset = tls13_SetupAeadIv(IS_DTLS(ss), cwSpec->version, ivOut, cwSpec->keyMaterial.iv, + ivOffset, ivLen, cwSpec->epoch); + rv = tls13_AEAD(cwSpec->cipherContext, PR_FALSE, + CKG_GENERATE_COUNTER_XOR, ivOffset * BPB, + ivOut, ivOut, ivLen, /* iv */ + NULL, 0, /* nonce */ + aad + sizeof(sslSequenceNumber), /* aad */ + aadLen - sizeof(sslSequenceNumber), + SSL_BUFFER_NEXT(wrBuf), /* output */ + &len, /* out len */ + SSL_BUFFER_SPACE(wrBuf), /* max out */ + tagLen, + SSL_BUFFER_NEXT(wrBuf), /* input */ + contentLen + 1); /* input len */ + if (rv != SECSuccess) { + PORT_SetError(SSL_ERROR_ENCRYPTION_FAILURE); + return SECFailure; + } + rv = sslBuffer_Skip(wrBuf, len, NULL); + PORT_Assert(rv == SECSuccess); + } + + return SECSuccess; +} + +/* Unprotect a TLS 1.3 record and leave the result in plaintext. + * + * Called by ssl3_HandleRecord. Caller must hold the spec read lock. + * Therefore, we MUST not call SSL3_SendAlert(). + * + * If SECFailure is returned, we: + * 1. Set |*alert| to the alert to be sent. + * 2. Call PORT_SetError() with an appropriate code. + */ +SECStatus +tls13_UnprotectRecord(sslSocket *ss, + ssl3CipherSpec *spec, + SSL3Ciphertext *cText, + sslBuffer *plaintext, + SSLContentType *innerType, + SSL3AlertDescription *alert) +{ + const ssl3BulkCipherDef *cipher_def = spec->cipherDef; + const int ivLen = cipher_def->iv_size + cipher_def->explicit_nonce_size; + const int tagLen = cipher_def->tag_size; + const int innerTypeLen = 1; + + PRUint8 aad[21]; + unsigned int aadLen; + SECStatus rv; + + *alert = bad_record_mac; /* Default alert for most issues. */ + + PORT_Assert(spec->direction == ssl_secret_read); + SSL_TRC(3, ("%d: TLS13[%d]: spec=%d epoch=%d (%s) unprotect 0x%0llx len=%u", + SSL_GETPID(), ss->fd, spec, spec->epoch, spec->phase, + cText->seqNum, cText->buf->len)); + + /* Verify that the outer content type is right. + * + * For the inner content type as well as lower TLS versions this is checked + * in ssl3con.c/ssl3_HandleNonApllicationData(). + * + * For DTLS 1.3 this is checked in ssl3gthr.c/dtls_GatherData(). DTLS drops + * invalid records silently [RFC6347, Section 4.1.2.7]. + * + * Also allow the DTLS short header in TLS 1.3. */ + if (!(cText->hdr[0] == ssl_ct_application_data || + (IS_DTLS(ss) && + ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && + (cText->hdr[0] & 0xe0) == 0x20))) { + SSL_TRC(3, + ("%d: TLS13[%d]: record has invalid exterior type=%2.2x", + SSL_GETPID(), ss->fd, cText->hdr[0])); + PORT_SetError(SSL_ERROR_RX_UNEXPECTED_RECORD_TYPE); + *alert = unexpected_message; + return SECFailure; + } + + /* We can perform this test in variable time because the record's total + * length and the ciphersuite are both public knowledge. */ + if (cText->buf->len < tagLen) { + SSL_TRC(3, + ("%d: TLS13[%d]: record too short to contain valid AEAD data", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_BAD_MAC_READ); + return SECFailure; + } + + /* Check if the ciphertext can be valid if we assume maximum plaintext and + * add the specific ciphersuite expansion. + * This way we detect overlong plaintexts/padding before decryption. + * This check enforces size limitations more strict than the RFC. + * (see RFC8446, Section 5.2) */ + if (cText->buf->len > (spec->recordSizeLimit + innerTypeLen + tagLen)) { + *alert = record_overflow; + PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG); + return SECFailure; + } + + /* Check the version number in the record. Stream only. */ + if (!IS_DTLS(ss)) { + SSL3ProtocolVersion version = + ((SSL3ProtocolVersion)cText->hdr[1] << 8) | + (SSL3ProtocolVersion)cText->hdr[2]; + if (version != spec->recordVersion) { + /* Do we need a better error here? */ + SSL_TRC(3, ("%d: TLS13[%d]: record has bogus version", + SSL_GETPID(), ss->fd)); + return SECFailure; + } + } + + /* Decrypt */ + PORT_Assert(cipher_def->type == type_aead); + rv = tls13_FormatAdditionalData(ss, cText->hdr, cText->hdrLen, + spec->epoch, cText->seqNum, + aad, &aadLen, sizeof(aad)); + if (rv != SECSuccess) { + + return SECFailure; + } + rv = tls13_AEAD(spec->cipherContext, PR_TRUE, + CKG_NO_GENERATE, 0, /* ignored for decrypt */ + spec->keyMaterial.iv, NULL, ivLen, /* iv */ + aad, sizeof(sslSequenceNumber), /* nonce */ + aad + sizeof(sslSequenceNumber), /* aad */ + aadLen - sizeof(sslSequenceNumber), + plaintext->buf, /* output */ + &plaintext->len, /* outlen */ + plaintext->space, /* maxout */ + tagLen, + cText->buf->buf, /* in */ + cText->buf->len); /* inlen */ + if (rv != SECSuccess) { + if (IS_DTLS(ss)) { + spec->deprotectionFailures++; + } + + SSL_TRC(3, + ("%d: TLS13[%d]: record has bogus MAC", + SSL_GETPID(), ss->fd)); + PORT_SetError(SSL_ERROR_BAD_MAC_READ); + return SECFailure; + } + + /* There is a similar test in ssl3_HandleRecord, but this test is needed to + * account for padding. */ + if (plaintext->len > spec->recordSizeLimit + innerTypeLen) { + *alert = record_overflow; + PORT_SetError(SSL_ERROR_RX_RECORD_TOO_LONG); + return SECFailure; + } + + /* The record is right-padded with 0s, followed by the true + * content type, so read from the right until we receive a + * nonzero byte. */ + while (plaintext->len > 0 && !(plaintext->buf[plaintext->len - 1])) { + --plaintext->len; + } + + /* Bogus padding. */ + if (plaintext->len < 1) { + SSL_TRC(3, ("%d: TLS13[%d]: empty record", SSL_GETPID(), ss->fd)); + /* It's safe to report this specifically because it happened + * after the MAC has been verified. */ + *alert = unexpected_message; + PORT_SetError(SSL_ERROR_BAD_BLOCK_PADDING); + return SECFailure; + } + + /* Record the type. */ + *innerType = (SSLContentType)plaintext->buf[plaintext->len - 1]; + --plaintext->len; + + /* Check for zero-length encrypted Alert and Handshake fragments + * (zero-length + inner content type byte). + * + * Implementations MUST NOT send Handshake and Alert records that have a + * zero-length TLSInnerPlaintext.content; if such a message is received, + * the receiving implementation MUST terminate the connection with an + * "unexpected_message" alert [RFC8446, Section 5.4]. */ + if (!plaintext->len && ((!IS_DTLS(ss) && cText->hdr[0] == ssl_ct_application_data) || + (IS_DTLS(ss) && dtls_IsDtls13Ciphertext(spec->version, cText->hdr[0])))) { + switch (*innerType) { + case ssl_ct_alert: + *alert = unexpected_message; + PORT_SetError(SSL_ERROR_RX_MALFORMED_ALERT); + return SECFailure; + case ssl_ct_handshake: + *alert = unexpected_message; + PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE); + return SECFailure; + default: + break; + } + } + + /* Check that we haven't received too much 0-RTT data. */ + if (spec->epoch == TrafficKeyEarlyApplicationData && + *innerType == ssl_ct_application_data) { + if (plaintext->len > spec->earlyDataRemaining) { + *alert = unexpected_message; + PORT_SetError(SSL_ERROR_TOO_MUCH_EARLY_DATA); + return SECFailure; + } + spec->earlyDataRemaining -= plaintext->len; + } + + SSL_TRC(10, + ("%d: TLS13[%d]: %s received record of length=%d, type=%d", + SSL_GETPID(), ss->fd, SSL_ROLE(ss), plaintext->len, *innerType)); + + return SECSuccess; +} + +/* 0-RTT is only permitted if: + * + * 1. We are doing TLS 1.3 + * 2. This isn't a second ClientHello (in response to HelloRetryRequest) + * 3. The 0-RTT option is set. + * 4. We have a valid ticket or an External PSK. + * 5. If resuming: + * 5a. The server is willing to accept 0-RTT. + * 5b. We have not changed our ALPN settings to disallow the ALPN tag + * in the ticket. + * + * Called from tls13_ClientSendEarlyDataXtn(). + */ +PRBool +tls13_ClientAllow0Rtt(const sslSocket *ss, const sslSessionID *sid) +{ + /* We checked that the cipher suite was still allowed back in + * ssl3_SendClientHello. */ + if (sid->version < SSL_LIBRARY_VERSION_TLS_1_3) { + return PR_FALSE; + } + if (ss->ssl3.hs.helloRetry) { + return PR_FALSE; + } + if (!ss->opt.enable0RttData) { + return PR_FALSE; + } + if (PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)) { + return PR_FALSE; + } + sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks); + + if (psk->zeroRttSuite == TLS_NULL_WITH_NULL_NULL) { + return PR_FALSE; + } + if (!psk->maxEarlyData) { + return PR_FALSE; + } + + if (psk->type == ssl_psk_external) { + return psk->hash == tls13_GetHashForCipherSuite(psk->zeroRttSuite); + } + if (psk->type == ssl_psk_resume) { + if (!ss->statelessResume) + return PR_FALSE; + if ((sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) == 0) + return PR_FALSE; + return ssl_AlpnTagAllowed(ss, &sid->u.ssl3.alpnSelection); + } + PORT_Assert(0); + return PR_FALSE; +} + +SECStatus +tls13_MaybeDo0RTTHandshake(sslSocket *ss) +{ + SECStatus rv; + + /* Don't do anything if there is no early_data xtn, which means we're + * not doing early data. */ + if (!ssl3_ExtensionAdvertised(ss, ssl_tls13_early_data_xtn)) { + return SECSuccess; + } + + ss->ssl3.hs.zeroRttState = ssl_0rtt_sent; + ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite; + /* Note: Reset the preliminary info here rather than just add 0-RTT. We are + * only guessing what might happen at this point.*/ + ss->ssl3.hs.preliminaryInfo = ssl_preinfo_0rtt_cipher_suite; + + SSL_TRC(3, ("%d: TLS13[%d]: in 0-RTT mode", SSL_GETPID(), ss->fd)); + + /* Set the ALPN data as if it was negotiated. We check in the ServerHello + * handler that the server negotiates the same value. */ + if (ss->sec.ci.sid->u.ssl3.alpnSelection.len) { + ss->xtnData.nextProtoState = SSL_NEXT_PROTO_EARLY_VALUE; + rv = SECITEM_CopyItem(NULL, &ss->xtnData.nextProto, + &ss->sec.ci.sid->u.ssl3.alpnSelection); + if (rv != SECSuccess) { + return SECFailure; + } + } + + if (ss->opt.enableTls13CompatMode && !IS_DTLS(ss)) { + /* Pretend that this is a proper ChangeCipherSpec even though it is sent + * before receiving the ServerHello. */ + ssl_GetSpecWriteLock(ss); + tls13_SetSpecRecordVersion(ss, ss->ssl3.cwSpec); + ssl_ReleaseSpecWriteLock(ss); + ssl_GetXmitBufLock(ss); + rv = ssl3_SendChangeCipherSpecsInt(ss); + ssl_ReleaseXmitBufLock(ss); + if (rv != SECSuccess) { + return SECFailure; + } + } + + /* If we have any message that was saved for later hashing. + * The updated hash is then used in tls13_DeriveEarlySecrets. */ + rv = ssl3_MaybeUpdateHashWithSavedRecord(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + /* If we're trying 0-RTT, derive from the first PSK */ + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks) && !ss->xtnData.selectedPsk); + ss->xtnData.selectedPsk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks); + rv = tls13_DeriveEarlySecrets(ss); + if (rv != SECSuccess) { + return SECFailure; + } + + /* Save cwSpec in case we get a HelloRetryRequest and have to send another + * ClientHello. */ + ssl_CipherSpecAddRef(ss->ssl3.cwSpec); + + rv = tls13_SetCipherSpec(ss, TrafficKeyEarlyApplicationData, + ssl_secret_write, PR_TRUE); + ss->xtnData.selectedPsk = NULL; + if (rv != SECSuccess) { + return SECFailure; + } + + return SECSuccess; +} + +PRInt32 +tls13_Read0RttData(sslSocket *ss, PRUint8 *buf, PRInt32 len) +{ + PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.bufferedEarlyData)); + PRInt32 offset = 0; + while (!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.bufferedEarlyData)) { + TLS13EarlyData *msg = + (TLS13EarlyData *)PR_NEXT_LINK(&ss->ssl3.hs.bufferedEarlyData); + unsigned int tocpy = msg->data.len - msg->consumed; + + if (tocpy > (len - offset)) { + if (IS_DTLS(ss)) { + /* In DTLS, we only return entire records. + * So offset and consumed are always zero. */ + PORT_Assert(offset == 0); + PORT_Assert(msg->consumed == 0); + PORT_SetError(SSL_ERROR_RX_SHORT_DTLS_READ); + return -1; + } + + tocpy = len - offset; + } + + PORT_Memcpy(buf + offset, msg->data.data + msg->consumed, tocpy); + offset += tocpy; + msg->consumed += tocpy; + + if (msg->consumed == msg->data.len) { + PR_REMOVE_LINK(&msg->link); + SECITEM_ZfreeItem(&msg->data, PR_FALSE); + PORT_ZFree(msg, sizeof(*msg)); + } + + /* We are done after one record for DTLS; otherwise, when the buffer fills up. */ + if (IS_DTLS(ss) || offset == len) { + break; + } + } + + return offset; +} + +static SECStatus +tls13_SendEndOfEarlyData(sslSocket *ss) +{ + SECStatus rv; + + PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss)); + + if (!ss->opt.suppressEndOfEarlyData) { + SSL_TRC(3, ("%d: TLS13[%d]: send EndOfEarlyData", SSL_GETPID(), ss->fd)); + rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_end_of_early_data, 0); + if (rv != SECSuccess) { + return rv; /* err set by AppendHandshake. */ + } + } + + ss->ssl3.hs.zeroRttState = ssl_0rtt_done; + return SECSuccess; +} + +static SECStatus +tls13_HandleEndOfEarlyData(sslSocket *ss, const PRUint8 *b, PRUint32 length) +{ + SECStatus rv; + + PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + + rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_END_OF_EARLY_DATA, + wait_end_of_early_data); + if (rv != SECSuccess) { + return SECFailure; + } + + /* We shouldn't be getting any more early data, and if we do, + * it is because of reordering and we drop it. */ + if (IS_DTLS(ss)) { + ssl_CipherSpecReleaseByEpoch(ss, ssl_secret_read, + TrafficKeyEarlyApplicationData); + dtls_ReceivedFirstMessageInFlight(ss); + } + + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted); + + if (length) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_END_OF_EARLY_DATA, decode_error); + return SECFailure; + } + + rv = tls13_SetCipherSpec(ss, TrafficKeyHandshake, + ssl_secret_read, PR_FALSE); + if (rv != SECSuccess) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + return SECFailure; + } + + ss->ssl3.hs.zeroRttState = ssl_0rtt_done; + if (tls13_ShouldRequestClientAuth(ss)) { + TLS13_SET_HS_STATE(ss, wait_client_cert); + } else { + TLS13_SET_HS_STATE(ss, wait_finished); + } + return SECSuccess; +} + +static SECStatus +tls13_MaybeHandleSuppressedEndOfEarlyData(sslSocket *ss) +{ + PORT_Assert(ss->sec.isServer); + if (!ss->opt.suppressEndOfEarlyData || + ss->ssl3.hs.zeroRttState != ssl_0rtt_accepted) { + return SECSuccess; + } + + return tls13_HandleEndOfEarlyData(ss, NULL, 0); +} + +SECStatus +tls13_HandleEarlyApplicationData(sslSocket *ss, sslBuffer *origBuf) +{ + TLS13EarlyData *ed; + SECItem it = { siBuffer, NULL, 0 }; + + PORT_Assert(ss->sec.isServer); + PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_accepted); + if (ss->ssl3.hs.zeroRttState != ssl_0rtt_accepted) { + /* Belt and suspenders. */ + FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error); + return SECFailure; + } + + PRINT_BUF(3, (NULL, "Received early application data", + origBuf->buf, origBuf->len)); + ed = PORT_ZNew(TLS13EarlyData); + if (!ed) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + it.data = origBuf->buf; + it.len = origBuf->len; + if (SECITEM_CopyItem(NULL, &ed->data, &it) != SECSuccess) { + FATAL_ERROR(ss, SEC_ERROR_NO_MEMORY, internal_error); + return SECFailure; + } + PR_APPEND_LINK(&ed->link, &ss->ssl3.hs.bufferedEarlyData); + + origBuf->len = 0; /* So ssl3_GatherAppDataRecord will keep looping. */ + + return SECSuccess; +} + +PRUint16 +tls13_EncodeVersion(SSL3ProtocolVersion version, SSLProtocolVariant variant) +{ + if (variant == ssl_variant_datagram) { + return dtls_TLSVersionToDTLSVersion(version); + } + /* Stream-variant encodings do not change. */ + return (PRUint16)version; +} + +SECStatus +tls13_ClientReadSupportedVersion(sslSocket *ss) +{ + PRUint32 temp; + TLSExtension *versionExtension; + SECItem it; + SECStatus rv; + + /* Update the version based on the extension, as necessary. */ + versionExtension = ssl3_FindExtension(ss, ssl_tls13_supported_versions_xtn); + if (!versionExtension) { + return SECSuccess; + } + + /* Struct copy so we don't damage the extension. */ + it = versionExtension->data; + + rv = ssl3_ConsumeHandshakeNumber(ss, &temp, 2, &it.data, &it.len); + if (rv != SECSuccess) { + return SECFailure; + } + if (it.len) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter); + return SECFailure; + } + + if (temp != tls13_EncodeVersion(SSL_LIBRARY_VERSION_TLS_1_3, + ss->protocolVariant)) { + /* You cannot negotiate < TLS 1.3 with supported_versions. */ + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, illegal_parameter); + return SECFailure; + } + + /* Any endpoint receiving a Hello message with...ServerHello.legacy_version + * set to 0x0300 (SSL3) MUST abort the handshake with a "protocol_version" + * alert. [RFC8446, Section D.5] + * + * The ServerHello.legacy_version is read into the ss->version field by + * ssl_ClientReadVersion(). */ + if (ss->version == SSL_LIBRARY_VERSION_3_0) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_SERVER_HELLO, protocol_version); + return SECFailure; + } + + ss->version = SSL_LIBRARY_VERSION_TLS_1_3; + return SECSuccess; +} + +/* Pick the highest version we support that is also advertised. */ +SECStatus +tls13_NegotiateVersion(sslSocket *ss, const TLSExtension *supportedVersions) +{ + PRUint16 version; + /* Make a copy so we're nondestructive. */ + SECItem data = supportedVersions->data; + SECItem versions; + SECStatus rv; + + rv = ssl3_ConsumeHandshakeVariable(ss, &versions, 1, + &data.data, &data.len); + if (rv != SECSuccess) { + return SECFailure; + } + if (data.len || !versions.len || (versions.len & 1)) { + FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_CLIENT_HELLO, illegal_parameter); + return SECFailure; + } + for (version = ss->vrange.max; version >= ss->vrange.min; --version) { + if (version < SSL_LIBRARY_VERSION_TLS_1_3 && + (ss->ssl3.hs.helloRetry || ss->ssl3.hs.echAccepted)) { + /* Prevent negotiating to a lower version after 1.3 HRR or ECH + * When accepting ECH, a different alert is generated. + */ + SSL3AlertDescription alert = ss->ssl3.hs.echAccepted ? illegal_parameter : protocol_version; + PORT_SetError(SSL_ERROR_UNSUPPORTED_VERSION); + FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, alert); + return SECFailure; + } + + PRUint16 wire = tls13_EncodeVersion(version, ss->protocolVariant); + unsigned long offset; + + for (offset = 0; offset < versions.len; offset += 2) { + PRUint16 supported = + (versions.data[offset] << 8) | versions.data[offset + 1]; + if (supported == wire) { + ss->version = version; + return SECSuccess; + } + } + } + + FATAL_ERROR(ss, SSL_ERROR_UNSUPPORTED_VERSION, protocol_version); + return SECFailure; +} + +/* This is TLS 1.3 or might negotiate to it. */ +PRBool +tls13_MaybeTls13(sslSocket *ss) +{ + if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) { + return PR_TRUE; + } + + if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) { + return PR_FALSE; + } + + if (!(ss->ssl3.hs.preliminaryInfo & ssl_preinfo_version)) { + return PR_TRUE; + } + + return PR_FALSE; +} + +/* Setup random client GREASE values according to RFC8701. State must be kept + * so an equal ClientHello might be send on HelloRetryRequest. */ +SECStatus +tls13_ClientGreaseSetup(sslSocket *ss) +{ + if (!ss->opt.enableGrease) { + return SECSuccess; + } + + PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + + if (ss->ssl3.hs.grease) { + return SECFailure; + } + ss->ssl3.hs.grease = PORT_Alloc(sizeof(tls13ClientGrease)); + if (!ss->ssl3.hs.grease) { + return SECFailure; + } + + tls13ClientGrease *grease = ss->ssl3.hs.grease; + /* We require eight GREASE values and randoms. */ + PRUint8 random[8]; + + /* Generate random GREASE values. */ + if (PK11_GenerateRandom(random, sizeof(random)) != SECSuccess) { + return SECFailure; + } + for (size_t i = 0; i < PR_ARRAY_SIZE(grease->idx); i++) { + random[i] = ((random[i] & 0xf0) | 0x0a); + grease->idx[i] = ((random[i] << 8) | random[i]); + } + /* Specific PskKeyExchangeMode GREASE value. */ + grease->pskKem = 0x0b + ((random[8 - 1] >> 5) * 0x1f); + + /* Duplicate extensions are not allowed. */ + if (grease->idx[grease_extension1] == grease->idx[grease_extension2]) { + grease->idx[grease_extension2] ^= 0x1010; + } + + return SECSuccess; +} + +/* Destroy client GREASE state. */ +void +tls13_ClientGreaseDestroy(sslSocket *ss) +{ + if (ss->ssl3.hs.grease) { + PORT_Free(ss->ssl3.hs.grease); + ss->ssl3.hs.grease = NULL; + } +} + +/* Generate a random GREASE value according to RFC8701. + * This function does not provide valid PskKeyExchangeMode GREASE values! */ +SECStatus +tls13_RandomGreaseValue(PRUint16 *out) +{ + PRUint8 random; + + if (PK11_GenerateRandom(&random, sizeof(random)) != SECSuccess) { + return SECFailure; + } + + random = ((random & 0xf0) | 0x0a); + *out = ((random << 8) | random); + + return SECSuccess; +} + +/* Set TLS 1.3 GREASE Extension random GREASE type. */ +SECStatus +tls13_MaybeGreaseExtensionType(const sslSocket *ss, + const SSLHandshakeType message, + PRUint16 *exType) +{ + if (*exType != ssl_tls13_grease_xtn) { + return SECSuccess; + } + + PR_ASSERT(ss->opt.enableGrease); + PR_ASSERT(message == ssl_hs_client_hello || + message == ssl_hs_certificate_request); + + /* GREASE ClientHello: + * A client MAY select one or more GREASE extension values and + * advertise them as extensions with varying length and contents + * [RFC8701, Section 3.1]. */ + if (message == ssl_hs_client_hello) { + PR_ASSERT(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3); + /* Check if the first GREASE extension was already added. */ + if (!ssl3_ExtensionAdvertised(ss, ss->ssl3.hs.grease->idx[grease_extension1])) { + *exType = ss->ssl3.hs.grease->idx[grease_extension1]; + } else { + *exType = ss->ssl3.hs.grease->idx[grease_extension2]; + } + } + /* GREASE CertificateRequest: + * When sending a CertificateRequest in TLS 1.3, a server MAY behave as + * follows: A server MAY select one or more GREASE extension values and + * advertise them as extensions with varying length and contents + * [RFC8701, Section 4.1]. */ + else if (message == ssl_hs_certificate_request) { + PR_ASSERT(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3); + /* Get random grease extension type. */ + SECStatus rv = tls13_RandomGreaseValue(exType); + if (rv != SECSuccess) { + return SECFailure; + } + } + + return SECSuccess; +} |