summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/tls13con.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-19 00:47:55 +0000
commit26a029d407be480d791972afb5975cf62c9360a6 (patch)
treef435a8308119effd964b339f76abb83a57c29483 /security/nss/lib/ssl/tls13con.c
parentInitial commit. (diff)
downloadfirefox-26a029d407be480d791972afb5975cf62c9360a6.tar.xz
firefox-26a029d407be480d791972afb5975cf62c9360a6.zip
Adding upstream version 124.0.1.upstream/124.0.1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'security/nss/lib/ssl/tls13con.c')
-rw-r--r--security/nss/lib/ssl/tls13con.c7210
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,
+ &paramSet, &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, &param);
+ 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;
+}