summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/ssl/tls13exthandle.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/tls13exthandle.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/tls13exthandle.c')
-rw-r--r--security/nss/lib/ssl/tls13exthandle.c1961
1 files changed, 1961 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/tls13exthandle.c b/security/nss/lib/ssl/tls13exthandle.c
new file mode 100644
index 0000000000..20aa479a16
--- /dev/null
+++ b/security/nss/lib/ssl/tls13exthandle.c
@@ -0,0 +1,1961 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/*
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "nssrenam.h"
+#include "nss.h"
+#include "ssl.h"
+#include "sslproto.h"
+#include "sslimpl.h"
+#include "pk11pub.h"
+#include "ssl3ext.h"
+#include "ssl3exthandle.h"
+#include "tls13ech.h"
+#include "tls13exthandle.h"
+#include "tls13psk.h"
+#include "tls13subcerts.h"
+
+SECStatus
+tls13_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ const sslServerCert *serverCert = ss->sec.serverCert;
+ const SECItem *item;
+ SECStatus rv;
+
+ if (!serverCert->certStatusArray ||
+ !serverCert->certStatusArray->len) {
+ return SECSuccess;
+ }
+
+ item = &serverCert->certStatusArray->items[0];
+
+ /* Only send the first entry. */
+ /* status_type == ocsp */
+ rv = sslBuffer_AppendNumber(buf, 1 /*ocsp*/, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ /* opaque OCSPResponse<1..2^24-1> */
+ rv = sslBuffer_AppendVariable(buf, item->data, item->len, 3);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/*
+ * [RFC 8446] Section 4.2.8.
+ *
+ * struct {
+ * NamedGroup group;
+ * opaque key_exchange<1..2^16-1>;
+ * } KeyShareEntry;
+ *
+ */
+PRUint32
+tls13_SizeOfKeyShareEntry(const sslEphemeralKeyPair *keyPair)
+{
+ /* Size = NamedGroup(2) + length(2) + opaque<?> share */
+ PRUint32 size = 2 + 2;
+
+ const SECKEYPublicKey *pubKey = keyPair->keys->pubKey;
+ switch (pubKey->keyType) {
+ case ecKey:
+ size += pubKey->u.ec.publicValue.len;
+ break;
+ case dhKey:
+ size += pubKey->u.dh.prime.len;
+ break;
+ default:
+ PORT_Assert(0);
+ return 0;
+ }
+
+ if (keyPair->kemKeys) {
+ PORT_Assert(!keyPair->kemCt);
+ PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00);
+ pubKey = keyPair->kemKeys->pubKey;
+ size += pubKey->u.kyber.publicValue.len;
+ }
+ if (keyPair->kemCt) {
+ PORT_Assert(!keyPair->kemKeys);
+ PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00);
+ size += keyPair->kemCt->len;
+ }
+
+ return size;
+}
+
+SECStatus
+tls13_EncodeKeyShareEntry(sslBuffer *buf, sslEphemeralKeyPair *keyPair)
+{
+ SECStatus rv;
+ unsigned int size = tls13_SizeOfKeyShareEntry(keyPair);
+
+ rv = sslBuffer_AppendNumber(buf, keyPair->group->name, 2);
+ if (rv != SECSuccess)
+ return rv;
+ rv = sslBuffer_AppendNumber(buf, size - 4, 2);
+ if (rv != SECSuccess)
+ return rv;
+
+ const SECKEYPublicKey *pubKey = keyPair->keys->pubKey;
+ switch (pubKey->keyType) {
+ case ecKey:
+ rv = sslBuffer_Append(buf, pubKey->u.ec.publicValue.data,
+ pubKey->u.ec.publicValue.len);
+ break;
+ case dhKey:
+ rv = ssl_AppendPaddedDHKeyShare(buf, pubKey, PR_FALSE);
+ break;
+ default:
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ break;
+ }
+
+ if (rv != SECSuccess) {
+ return rv;
+ }
+
+ if (keyPair->kemKeys) {
+ PORT_Assert(!keyPair->kemCt);
+ PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00);
+ pubKey = keyPair->kemKeys->pubKey;
+ rv = sslBuffer_Append(buf, pubKey->u.kyber.publicValue.data, pubKey->u.kyber.publicValue.len);
+ }
+ if (keyPair->kemCt) {
+ PORT_Assert(!keyPair->kemKeys);
+ PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00);
+ rv = sslBuffer_Append(buf, keyPair->kemCt->data, keyPair->kemCt->len);
+ }
+
+ return rv;
+}
+
+SECStatus
+tls13_ClientSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+ PRCList *cursor;
+ unsigned int lengthOffset;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* Optimistically try to send an ECDHE key using the
+ * preexisting key (in future will be keys) */
+ SSL_TRC(3, ("%d: TLS13[%d]: send client key share xtn",
+ SSL_GETPID(), ss->fd));
+
+ /* Save the offset to the length. */
+ rv = sslBuffer_Skip(buf, 2, &lengthOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ for (cursor = PR_NEXT_LINK(&ss->ephemeralKeyPairs);
+ cursor != &ss->ephemeralKeyPairs;
+ cursor = PR_NEXT_LINK(cursor)) {
+ sslEphemeralKeyPair *keyPair = (sslEphemeralKeyPair *)cursor;
+ rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ /* GREASE KeyShareEntry:
+ * [The client] MAY also send KeyShareEntry values for a subset of those
+ * selected in the "key_share" extension. For each of these, the
+ * "key_exchange" field MAY be any value [RFC8701, Section 3.1].
+ *
+ * By default we do not send KeyShares for every NamedGroup so the
+ * ServerKeyShare handshake message / additional round-trip is not
+ * triggered by sending GREASE KeyShareEntries. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_group], 2);
+ if (rv != SECSuccess)
+ return rv;
+ /* Entry length */
+ rv = sslBuffer_AppendNumber(buf, 2, 2);
+ if (rv != SECSuccess)
+ return rv;
+ /* Entry value */
+ rv = sslBuffer_AppendNumber(buf, 0xCD, 2);
+ if (rv != SECSuccess)
+ return rv;
+ }
+
+ rv = sslBuffer_InsertLength(buf, lengthOffset, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_DecodeKeyShareEntry(sslReader *rdr, TLS13KeyShareEntry **ksp)
+{
+ SECStatus rv;
+ PRUint64 group;
+ const sslNamedGroupDef *groupDef;
+ TLS13KeyShareEntry *ks = NULL;
+ sslReadBuffer share;
+
+ rv = sslRead_ReadNumber(rdr, 2, &group);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ groupDef = ssl_LookupNamedGroup(group);
+ rv = sslRead_ReadVariable(rdr, 2, &share);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* This has to happen here because we want to consume
+ * the entire entry even if the group is unknown
+ * or disabled. */
+ /* If the group is disabled, continue. */
+ if (!groupDef) {
+ return SECSuccess;
+ }
+
+ ks = PORT_ZNew(TLS13KeyShareEntry);
+ if (!ks) {
+ goto loser;
+ }
+ ks->group = groupDef;
+
+ rv = SECITEM_MakeItem(NULL, &ks->key_exchange,
+ share.buf, share.len);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ *ksp = ks;
+ return SECSuccess;
+
+loser:
+ tls13_DestroyKeyShareEntry(ks);
+
+ return SECFailure;
+}
+/* Handle an incoming KeyShare extension at the client and copy to
+ * |xtnData->remoteKeyShares| for future use. The key
+ * share is processed in tls13_HandleServerKeyShare(). */
+SECStatus
+tls13_ClientHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
+ TLS13KeyShareEntry *ks = NULL;
+
+ PORT_Assert(!ss->sec.isServer);
+
+ /* The server must not send this extension when negotiating < TLS 1.3. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
+ SSL_GETPID(), ss->fd));
+
+ sslReader rdr = SSL_READER(data->data, data->len);
+ rv = tls13_DecodeKeyShareEntry(&rdr, &ks);
+ if ((rv != SECSuccess) || !ks) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ return SECFailure;
+ }
+
+ if (SSL_READER_REMAINING(&rdr)) {
+ tls13_DestroyKeyShareEntry(ks);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ return SECFailure;
+ }
+ PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleKeyShareXtnHrr(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PRUint32 tmp;
+ const sslNamedGroupDef *group;
+
+ PORT_Assert(!ss->sec.isServer);
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension in HRR",
+ SSL_GETPID(), ss->fd));
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2, &data->data, &data->len);
+ if (rv != SECSuccess) {
+ return SECFailure; /* error code already set */
+ }
+ if (data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ group = ssl_LookupNamedGroup((SSLNamedGroup)tmp);
+ /* If the group is not enabled, or we already have a share for the
+ * requested group, abort. */
+ if (!ssl_NamedGroupEnabled(ss, group) ||
+ ssl_HaveEphemeralKeyPair(ss, group)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ /* Now delete all the key shares per [draft-ietf-tls-tls13 S 4.1.2] */
+ ssl_FreeEphemeralKeyPairs(CONST_CAST(sslSocket, ss));
+
+ /* And replace with our new share. */
+ rv = tls13_AddKeyShare(CONST_CAST(sslSocket, ss), group);
+ if (rv != SECSuccess) {
+ ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
+ PORT_SetError(SEC_ERROR_KEYGEN_FAIL);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+/* Handle an incoming KeyShare extension at the server and copy to
+ * |xtnData->remoteKeyShares| for future use. The key
+ * share is processed in tls13_HandleClientKeyShare(). */
+SECStatus
+tls13_ServerHandleKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PRUint32 length;
+
+ PORT_Assert(ss->sec.isServer);
+ PORT_Assert(PR_CLIST_IS_EMPTY(&xtnData->remoteKeyShares));
+
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle key_share extension",
+ SSL_GETPID(), ss->fd));
+
+ /* Redundant length because of TLS encoding (this vector consumes
+ * the entire extension.) */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &length, 2, &data->data,
+ &data->len);
+ if (rv != SECSuccess)
+ goto loser;
+ if (length != data->len) {
+ /* Check for consistency */
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ goto loser;
+ }
+
+ sslReader rdr = SSL_READER(data->data, data->len);
+ while (SSL_READER_REMAINING(&rdr)) {
+ TLS13KeyShareEntry *ks = NULL;
+ rv = tls13_DecodeKeyShareEntry(&rdr, &ks);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_KEY_SHARE);
+ goto loser;
+ }
+ if (ks) {
+ /* |ks| == NULL if this is an unknown group. */
+ PR_APPEND_LINK(&ks->link, &xtnData->remoteKeyShares);
+ }
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] =
+ ssl_tls13_key_share_xtn;
+
+ return SECSuccess;
+
+loser:
+ tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
+ return SECFailure;
+}
+
+SECStatus
+tls13_ServerSendKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+ sslEphemeralKeyPair *keyPair;
+
+ /* There should be 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);
+
+ rv = tls13_EncodeKeyShareEntry(buf, keyPair);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/* Called by clients.
+ *
+ * struct {
+ * opaque identity<0..2^16-1>;
+ * uint32 obfuscated_ticket_age;
+ * } PskIdentity;
+ *
+ * opaque PskBinderEntry<32..255>;
+ *
+ * struct {
+ * select (Handshake.msg_type) {
+ * case client_hello:
+ * PskIdentity identities<6..2^16-1>;
+ * PskBinderEntry binders<33..2^16-1>;
+ *
+ * case server_hello:
+ * uint16 selected_identity;
+ * };
+ *
+ * } PreSharedKeyExtension;
+ */
+SECStatus
+tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ const static PRUint8 binder[TLS13_MAX_FINISHED_SIZE] = { 0 };
+ unsigned int binderLen;
+ unsigned int identityLen = 0;
+ const PRUint8 *identity = NULL;
+ PRTime age;
+ SECStatus rv;
+
+ /* Exit early if no PSKs or max version < 1.3. */
+ if (PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks) ||
+ ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* ...or if PSK type is resumption, but we're not resuming. */
+ sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);
+ if (psk->type == ssl_psk_resume && !ss->statelessResume) {
+ return SECSuccess;
+ }
+
+ /* ...or if PSKs are incompatible with negotiated ciphersuites
+ * (different hash algorithms) on HRR.
+ *
+ * In addition, in its updated ClientHello, the client SHOULD NOT offer any
+ * pre-shared keys associated with a hash other than that of the selected
+ * cipher suite. This allows the client to avoid having to compute partial
+ * hash transcripts for multiple hashes in the second ClientHello
+ * [RFC8446, Section 4.1.4]. */
+ if (ss->ssl3.hs.helloRetry &&
+ (psk->hash != ss->ssl3.hs.suite_def->prf_hash)) {
+ return SECSuccess;
+ }
+
+ /* Save where this extension starts so that if we have to add padding, it
+ * can be inserted before this extension. */
+ PORT_Assert(buf->len >= 4);
+ xtnData->lastXtnOffset = buf->len - 4;
+ PORT_Assert(psk->type == ssl_psk_resume || psk->type == ssl_psk_external);
+ binderLen = tls13_GetHashSizeForHash(psk->hash);
+ if (psk->type == ssl_psk_resume) {
+ /* Send a single ticket identity. */
+ NewSessionTicket *session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket;
+ identityLen = session_ticket->ticket.len;
+ identity = session_ticket->ticket.data;
+
+ /* Obfuscated age. */
+ age = ssl_Time(ss) - session_ticket->received_timestamp;
+ age /= PR_USEC_PER_MSEC;
+ age += session_ticket->ticket_age_add;
+ PRINT_BUF(50, (ss, "Sending Resumption PSK with identity", identity, identityLen));
+ } else if (psk->type == ssl_psk_external) {
+ identityLen = psk->label.len;
+ identity = psk->label.data;
+ age = 0;
+ PRINT_BUF(50, (ss, "Sending External PSK with label", identity, identityLen));
+ } else {
+ PORT_Assert(0);
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ /* Length is len(identityLen) + identityLen + len(age) */
+ rv = sslBuffer_AppendNumber(buf, 2 + identityLen + 4, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = sslBuffer_AppendVariable(buf, identity,
+ identityLen, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, age, 4);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Write out the binder list length. */
+ rv = sslBuffer_AppendNumber(buf, binderLen + 1, 2);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ /* Write zeroes for the binder for the moment. These
+ * are overwritten in tls13_WriteExtensionsWithBinder. */
+ rv = sslBuffer_AppendVariable(buf, binder, binderLen, 1);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+
+ if (psk->type == ssl_psk_resume) {
+ xtnData->sentSessionTicketInClientHello = PR_TRUE;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+
+loser:
+ xtnData->ticketTimestampVerified = PR_FALSE;
+ return SECFailure;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. */
+SECStatus
+tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECItem inner;
+ SECStatus rv;
+ unsigned int numIdentities = 0;
+ unsigned int numBinders = 0;
+ SECItem *appToken;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ /* The application token is set via the cookie extension if this is the
+ * second ClientHello. Don't set it twice. The cookie extension handler
+ * sets |helloRetry| and that will have been called already because this
+ * extension always comes last. */
+ if (!ss->ssl3.hs.helloRetry) {
+ appToken = &xtnData->applicationToken;
+ } else {
+ appToken = NULL;
+ }
+
+ /* Parse the identities list. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &inner, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ while (inner.len) {
+ SECItem label;
+ PRUint32 obfuscatedAge;
+
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &label, 2,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+ if (!label.len) {
+ goto alert_loser;
+ }
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &obfuscatedAge, 4,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+
+ if (!numIdentities) {
+ /* Check any configured external PSK for a matching label.
+ * If none exists, try to parse it as a ticket. */
+ PORT_Assert(!xtnData->selectedPsk);
+ for (PRCList *cur_p = PR_LIST_HEAD(&ss->ssl3.hs.psks);
+ cur_p != &ss->ssl3.hs.psks;
+ cur_p = PR_NEXT_LINK(cur_p)) {
+ sslPsk *psk = (sslPsk *)cur_p;
+ if (psk->type != ssl_psk_external ||
+ SECITEM_CompareItem(&psk->label, &label) != SECEqual) {
+ continue;
+ }
+ PRINT_BUF(50, (ss, "Using External PSK with label",
+ psk->label.data, psk->label.len));
+ xtnData->selectedPsk = psk;
+ }
+
+ if (!xtnData->selectedPsk) {
+ PRINT_BUF(50, (ss, "Handling PreSharedKey value",
+ label.data, label.len));
+ rv = ssl3_ProcessSessionTicketCommon(
+ CONST_CAST(sslSocket, ss), &label, appToken);
+ /* This only happens if we have an internal error, not
+ * a malformed ticket. Bogus tickets just don't resume
+ * and return SECSuccess. */
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (ss->sec.ci.sid) {
+ /* xtnData->ticketAge contains the baseline we use for
+ * calculating the ticket age (i.e., our RTT estimate less the
+ * value of ticket_age_add).
+ *
+ * Add that to the obfuscated ticket age to recover the client's
+ * view of the ticket age plus the estimated RTT.
+ *
+ * See ssl3_EncodeSessionTicket() for details. */
+ xtnData->ticketAge += obfuscatedAge;
+
+ /* We are not committed to resumption until after unwrapping the
+ * RMS in tls13_HandleClientHelloPart2. The RPSK will be stored
+ * in ss->xtnData.selectedPsk at that point, so continue. */
+ }
+ }
+ }
+
+ ++numIdentities;
+ }
+
+ xtnData->pskBindersLen = data->len;
+
+ /* Parse the binders list. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss,
+ &inner, 2, &data->data, &data->len);
+ if (rv != SECSuccess)
+ return SECFailure;
+ if (data->len) {
+ goto alert_loser;
+ }
+
+ while (inner.len) {
+ SECItem binder;
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &binder, 1,
+ &inner.data, &inner.len);
+ if (rv != SECSuccess)
+ return rv;
+ if (binder.len < 32) {
+ goto alert_loser;
+ }
+
+ if (!numBinders) {
+ xtnData->pskBinder = binder;
+ }
+ ++numBinders;
+ }
+
+ if (numBinders != numIdentities)
+ goto alert_loser;
+
+ if (ss->statelessResume) {
+ PORT_Assert(!ss->xtnData.selectedPsk);
+ } else if (!xtnData->selectedPsk) {
+ /* No matching EPSK. */
+ return SECSuccess;
+ }
+
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
+ return SECSuccess;
+
+alert_loser:
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+}
+
+SECStatus
+tls13_ServerSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ /* We only process the first session ticket the client sends,
+ * so the index is always 0. */
+ rv = sslBuffer_AppendNumber(buf, 0, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/* Handle a TLS 1.3 PreSharedKey Extension. */
+SECStatus
+tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ PRUint32 index;
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
+ SSL_GETPID(), ss->fd));
+
+ /* The server must not send this extension when negotiating < TLS 1.3. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &index, 2, &data->data, &data->len);
+ if (rv != SECSuccess)
+ return SECFailure;
+
+ /* This should be the end of the extension. */
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ /* We only sent one PSK label so index must be equal to 0 */
+ if (index) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
+ return SECFailure;
+ }
+
+ PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks));
+ sslPsk *candidate = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);
+
+ /* Check that the server-selected ciphersuite hash and PSK hash match. */
+ if (candidate->hash != tls13_GetHashForCipherSuite(ss->ssl3.hs.cipher_suite)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
+ xtnData->selectedPsk = candidate;
+
+ return SECSuccess;
+}
+
+/*
+ * struct { } EarlyDataIndication;
+ */
+SECStatus
+tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid)) {
+ return SECSuccess;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ if (ss->ssl3.hs.helloRetry) {
+ ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
+ return SECFailure;
+ }
+
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;
+
+ return SECSuccess;
+}
+
+/* This will only be called if we also offered the extension. */
+SECStatus
+tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* The server must not send this extension when negotiating < TLS 1.3. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleTicketEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ PRUint32 utmp;
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle ticket early_data extension",
+ SSL_GETPID(), ss->fd));
+
+ /* The server must not send this extension when negotiating < TLS 1.3. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
+ return SECFailure;
+ }
+
+ rv = ssl3_ExtConsumeHandshake(ss, &utmp, sizeof(utmp),
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
+ return SECFailure;
+ }
+
+ xtnData->max_early_data_size = PR_ntohl(utmp);
+
+ return SECSuccess;
+}
+
+/*
+ * struct {
+ * select (Handshake.msg_type) {
+ * case client_hello:
+ * ProtocolVersion versions<2..254>;
+ * case server_hello:
+ * ProtocolVersion version;
+ * };
+ * } SupportedVersions;
+ */
+SECStatus
+tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ PRUint16 version;
+ unsigned int lengthOffset;
+ SECStatus rv;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: client send supported_versions extension",
+ SSL_GETPID(), ss->fd));
+
+ rv = sslBuffer_Skip(buf, 1, &lengthOffset);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ PORT_Assert(!ss->ssl3.hs.echHpkeCtx || ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+ for (version = ss->vrange.max; version >= ss->vrange.min; --version) {
+ PRUint16 wire = tls13_EncodeVersion(version,
+ ss->protocolVariant);
+ rv = sslBuffer_AppendNumber(buf, wire, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (ss->opt.enableDtls13VersionCompat &&
+ ss->protocolVariant == ssl_variant_datagram) {
+ switch (version) {
+ case SSL_LIBRARY_VERSION_TLS_1_2:
+ case SSL_LIBRARY_VERSION_TLS_1_1:
+ rv = sslBuffer_AppendNumber(buf, (PRUint16)version, 2);
+ break;
+ default:
+ continue;
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+ }
+
+ /* GREASE SupportedVersions:
+ * A client MAY select one or more GREASE version values and advertise them
+ * in the "supported_versions" extension, if sent [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_version], 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ rv = sslBuffer_InsertLength(buf, lengthOffset, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: server send supported_versions extension",
+ SSL_GETPID(), ss->fd));
+
+ PRUint16 ver = tls13_EncodeVersion(SSL_LIBRARY_VERSION_TLS_1_3,
+ ss->protocolVariant);
+ rv = sslBuffer_AppendNumber(buf, ver, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/*
+ * struct {
+ * opaque cookie<1..2^16-1>;
+ * } Cookie;
+ */
+SECStatus
+tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */
+ rv = ssl3_ExtConsumeHandshakeVariable(
+ ss, &CONST_CAST(sslSocket, ss)->ssl3.hs.cookie, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+ if (!ss->ssl3.hs.cookie.len || data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
+ return SECFailure;
+ }
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ !ss->ssl3.hs.cookie.len) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd));
+ rv = sslBuffer_AppendVariable(buf, ss->ssl3.hs.cookie.data,
+ ss->ssl3.hs.cookie.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandleCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
+ SSL_GETPID(), ss->fd));
+
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &xtnData->cookie, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (xtnData->cookie.len == 0) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_cookie_xtn;
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ /* Only one post-handshake message is supported: a single
+ * NST immediately following the client Finished. */
+ if (!IS_DTLS(ss)) {
+ SSL_TRC(3, ("%d: TLS13[%d]: send post_handshake_auth extension",
+ SSL_GETPID(), ss->fd));
+ *added = ss->opt.enablePostHandshakeAuth;
+ }
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: handle post_handshake_auth extension",
+ SSL_GETPID(), ss->fd));
+
+ if (data->len) {
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ /* Only one post-handshake message is supported: a single
+ * NST immediately following the client Finished. */
+ if (!IS_DTLS(ss)) {
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_post_handshake_auth_xtn;
+ }
+
+ return SECSuccess;
+}
+
+/*
+ * enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
+ *
+ * struct {
+ * PskKeyExchangeMode ke_modes<1..255>;
+ * } PskKeyExchangeModes;
+ */
+SECStatus
+tls13_ClientSendPskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ ss->opt.noCache) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: send psk key exchange modes extension",
+ SSL_GETPID(), ss->fd));
+
+ /* GREASE PskKeyExchangeMode:
+ * A client MAY select one or more GREASE PskKeyExchangeMode values and
+ * advertise them in the "psk_key_exchange_modes" extension, if sent
+ * [RFC8701, Section 3.1]. */
+ if (ss->opt.enableGrease) {
+ rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke, ss->ssl3.hs.grease->pskKem }, 2, 1);
+ } else {
+ rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke }, 1, 1);
+ }
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandlePskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+
+ /* If we are doing < TLS 1.3, then ignore this. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ return SECSuccess;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle PSK key exchange modes extension",
+ SSL_GETPID(), ss->fd));
+
+ /* IMPORTANT: We aren't copying these values, just setting pointers.
+ * They will only be valid as long as the ClientHello is in memory. */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss,
+ &xtnData->psk_ke_modes, 1,
+ &data->data, &data->len);
+ if (rv != SECSuccess)
+ return rv;
+ if (!xtnData->psk_ke_modes.len || data->len) {
+ PORT_SetError(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->negotiated[xtnData->numNegotiated++] =
+ ssl_tls13_psk_key_exchange_modes_xtn;
+
+ return SECSuccess;
+}
+
+SECStatus
+tls13_SendCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ unsigned int calen;
+ const SECItem *name;
+ unsigned int nnames;
+ SECStatus rv;
+
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ rv = ssl_GetCertificateRequestCAs(ss, &calen, &name, &nnames);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ if (!calen) {
+ return SECSuccess;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, calen, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ while (nnames) {
+ rv = sslBuffer_AppendVariable(buf, name->data, name->len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ ++name;
+ --nnames;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PLArenaPool *arena;
+
+ if (!data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
+ return SECFailure;
+ }
+
+ arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
+ if (!arena) {
+ PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
+ return SECFailure;
+ }
+
+ xtnData->certReqAuthorities.arena = arena;
+ rv = ssl3_ParseCertificateRequestCAs((sslSocket *)ss,
+ &data->data, &data->len,
+ &xtnData->certReqAuthorities);
+ if (rv != SECSuccess) {
+ goto loser;
+ }
+ if (data->len) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
+ goto loser;
+ }
+ return SECSuccess;
+
+loser:
+ PORT_FreeArena(arena, PR_FALSE);
+ xtnData->certReqAuthorities.arena = NULL;
+ return SECFailure;
+}
+
+SECStatus
+tls13_ServerHandleCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
+{
+ SSL_TRC(3, ("%d: TLS13[%d]: ignore certificate_authorities extension",
+ SSL_GETPID(), ss->fd));
+ /* NSS ignores certificate_authorities in the ClientHello */
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerSendHrrKeyShareXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+
+ if (!xtnData->selectedGroup) {
+ return SECSuccess;
+ }
+
+ rv = sslBuffer_AppendNumber(buf, xtnData->selectedGroup->name, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ PORT_Assert(xtnData->cookie.len > 0);
+
+ rv = sslBuffer_AppendVariable(buf,
+ xtnData->cookie.data, xtnData->cookie.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleHrrEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ if (data->len != TLS13_ECH_SIGNAL_LEN) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+ return SECFailure;
+ }
+ if (!ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn)) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+ if (!ss->ssl3.hs.echHpkeCtx) {
+ SSL_TRC(50, ("%d: TLS13[%d]: client received GREASEd ECH confirmation",
+ SSL_GETPID(), ss->fd));
+ return SECSuccess;
+ }
+ SSL_TRC(50, ("%d: TLS13[%d]: client received HRR ECH confirmation",
+ SSL_GETPID(), ss->fd));
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
+ return SECFailure;
+ }
+ xtnData->ech->hrrConfirmation = data->data;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ClientHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ PRCList parsedConfigs;
+ PR_INIT_CLIST(&parsedConfigs);
+
+ /* The [retry config] response is valid only when the server used the
+ * ClientHelloOuter. If the server sent this extension in response to the
+ * inner variant [ECH was accepted], then the client MUST abort with an
+ * "unsupported_extension" alert [draft-ietf-tls-esni-14, Section 5]. */
+ if (ss->ssl3.hs.echAccepted) {
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
+ return SECFailure;
+ }
+
+ /* If the server is configured with any ECHConfigs, it MUST include the
+ * "encrypted_client_hello" extension in its EncryptedExtensions with the
+ * "retry_configs" field set to one or more ECHConfig structures with
+ * up-to-date keys [draft-ietf-tls-esni-14, Section 7.1]. */
+ if (ss->ssl3.hs.msg_type != ssl_hs_encrypted_extensions) {
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ /* For TLS < 1.3 the extension is unkown/unsupported. */
+ ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
+ } else {
+ /* For TLS 1.3 the extension is known but prohibited outside EE
+ * (see RFC8446, Section 4.2 for alert rationale). */
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ }
+ return SECFailure;
+ }
+
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
+ return SECFailure;
+ }
+
+ /* Parse the list to determine 1) That the configs are valid
+ * and properly encoded, and 2) If any are compatible. */
+ rv = tls13_DecodeEchConfigs(data, &parsedConfigs);
+ if (rv == SECFailure) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
+ return SECFailure;
+ }
+ /* Don't mark ECH negotiated on rejection with retry_config.
+ * Save the the raw configs so the application can retry. If
+ * we sent GREASE ECH (no echHpkeCtx), don't apply retry_configs. */
+ if (ss->ssl3.hs.echHpkeCtx && !PR_CLIST_IS_EMPTY(&parsedConfigs)) {
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->retryConfigs, data);
+ }
+ tls13_DestroyEchConfigs(&parsedConfigs);
+
+ return rv;
+}
+
+/* Indicates support for the delegated credentials extension. This should be
+ * hooked while processing the ClientHello. */
+SECStatus
+tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ /* Only send the extension if support is enabled and the client can
+ * negotiate TLS 1.3. */
+ if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ !ss->opt.enableDelegatedCredentials) {
+ return SECSuccess;
+ }
+
+ /* Filter the schemes that are enabled and acceptable. Save these in
+ * the "advertised" list, then encode them to be sent. If we receive
+ * a DC in response, validate that it matches one of the advertised
+ * schemes. */
+ SSLSignatureScheme filtered[MAX_SIGNATURE_SCHEMES] = { 0 };
+ unsigned int filteredCount = 0;
+ SECStatus rv = ssl3_FilterSigAlgs(ss, ss->vrange.max,
+ PR_TRUE /* disableRsae */,
+ PR_FALSE /* forCert */,
+ MAX_SIGNATURE_SCHEMES,
+ filtered,
+ &filteredCount);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ /* If no schemes available for the DC extension, don't send it. */
+ if (!filteredCount) {
+ return SECSuccess;
+ }
+
+ rv = ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount,
+ PR_FALSE /* GREASE */, buf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ SSLSignatureScheme *dcSchemesAdvertised = PORT_ZNewArray(SSLSignatureScheme,
+ filteredCount);
+ if (!dcSchemesAdvertised) {
+ return SECFailure;
+ }
+ for (unsigned int i = 0; i < filteredCount; i++) {
+ dcSchemesAdvertised[i] = filtered[i];
+ }
+
+ if (xtnData->delegCredSigSchemesAdvertised) {
+ PORT_Free(xtnData->delegCredSigSchemesAdvertised);
+ }
+ xtnData->delegCredSigSchemesAdvertised = dcSchemesAdvertised;
+ xtnData->numDelegCredSigSchemesAdvertised = filteredCount;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/* Parses the delegated credential (DC) offered by the server. This should be
+ * hooked while processing the server's CertificateVerify.
+ *
+ * Only the DC sent with the end-entity certificate is to be parsed. This is
+ * ensured by |tls13_HandleCertificateEntry|, which only processes extensions
+ * for the first certificate in the chain.
+ */
+SECStatus
+tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ if (!ss->opt.enableDelegatedCredentials ||
+ ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ sslDelegatedCredential *dc = NULL;
+ SECStatus rv = tls13_ReadDelegatedCredential(data->data, data->len, &dc);
+ if (rv != SECSuccess) {
+ goto loser; /* code already set */
+ }
+
+ /* When using RSA, the public key MUST NOT use the rsaEncryption OID. */
+ if (dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha256 ||
+ dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha384 ||
+ dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha512) {
+ goto alert_loser;
+ }
+
+ /* The algorithm and expected_cert_verify_algorithm fields MUST be of a
+ * type advertised by the client in the SignatureSchemeList and are
+ * considered invalid otherwise. Clients that receive invalid delegated
+ * credentials MUST terminate the connection with an "illegal_parameter"
+ * alert. */
+ PRBool found = PR_FALSE;
+ for (unsigned int i = 0; i < ss->xtnData.numDelegCredSigSchemesAdvertised; ++i) {
+ if (dc->expectedCertVerifyAlg == ss->xtnData.delegCredSigSchemesAdvertised[i]) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ if (found == PR_FALSE) {
+ goto alert_loser;
+ }
+
+ // Check the dc->alg, if necessary.
+ if (dc->alg != dc->expectedCertVerifyAlg) {
+ found = PR_FALSE;
+ for (unsigned int i = 0; i < ss->xtnData.numDelegCredSigSchemesAdvertised; ++i) {
+ if (dc->alg == ss->xtnData.delegCredSigSchemesAdvertised[i]) {
+ found = PR_TRUE;
+ break;
+ }
+ }
+ if (found == PR_FALSE) {
+ goto alert_loser;
+ }
+ }
+
+ xtnData->peerDelegCred = dc;
+ xtnData->negotiated[xtnData->numNegotiated++] =
+ ssl_delegated_credentials_xtn;
+ return SECSuccess;
+alert_loser:
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
+loser:
+ tls13_DestroyDelegatedCredential(dc);
+ return SECFailure;
+}
+
+/* Adds the DC extension if we're committed to authenticating with a DC. */
+static SECStatus
+tls13_ServerSendDelegatedCredentialsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (tls13_IsSigningWithDelegatedCredential(ss)) {
+ const SECItem *dc = &ss->sec.serverCert->delegCred;
+ SECStatus rv;
+ rv = sslBuffer_Append(buf, dc->data, dc->len);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ *added = PR_TRUE;
+ }
+ return SECSuccess;
+}
+
+/* The client has indicated support of DCs. We can't act on this information
+ * until we've committed to signing with a DC, so just set a callback for
+ * sending the DC extension later. */
+SECStatus
+tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ if (xtnData->delegCredSigSchemes) {
+ PORT_Free(xtnData->delegCredSigSchemes);
+ xtnData->delegCredSigSchemes = NULL;
+ xtnData->numDelegCredSigSchemes = 0;
+ }
+ SECStatus rv = ssl_ParseSignatureSchemes(ss, NULL,
+ &xtnData->delegCredSigSchemes,
+ &xtnData->numDelegCredSigSchemes,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+ if (xtnData->numDelegCredSigSchemes == 0) {
+ ssl3_ExtSendAlert(ss, alert_fatal, handshake_failure);
+ PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
+ return SECFailure;
+ }
+ /* Check for trailing data. */
+ if (data->len != 0) {
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
+ return SECFailure;
+ }
+
+ /* Keep track of negotiated extensions. */
+ xtnData->peerRequestedDelegCred = PR_TRUE;
+ xtnData->negotiated[xtnData->numNegotiated++] =
+ ssl_delegated_credentials_xtn;
+
+ return ssl3_RegisterExtensionSender(
+ ss, xtnData, ssl_delegated_credentials_xtn,
+ tls13_ServerSendDelegatedCredentialsXtn);
+}
+
+/* Adds the ECH extension containing server retry_configs */
+SECStatus
+tls13_ServerSendEchXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+ PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
+ if (PR_CLIST_IS_EMPTY(&ss->echConfigs)) {
+ return SECSuccess;
+ }
+
+ const sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
+ rv = sslBuffer_AppendVariable(buf, cfg->raw.data, cfg->raw.len, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+/* If an ECH server sends the HRR ECH extension after it accepted ECH, the
+ * extension's payload must be set to 8 zero bytes, these are overwritten with
+ * the accept_confirmation value after the required transcript calculation.
+ * If a client-facing/shared-mode server did not accept ECH when offered in CH
+ * 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].
+ *
+ * Depending on the acceptance of ECH, zero or random bytes are written to
+ * ss->ssl3.hs.greaseEchBuf.buf in tls13con.c/tls13_SendHelloRetryRequest(). */
+SECStatus
+tls13_ServerSendHrrEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ SECStatus rv;
+ /* Do not send HRR ECH extension if TLS < 1.3 was negotiated OR no ECH
+ * extension was received OR the server is NOT in any ECH server mode AND
+ * ECH GREASE is NOT enabled. */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
+ !xtnData->ech ||
+ (!ss->echPubKey && !ss->opt.enableTls13BackendEch && !ss->opt.enableTls13GreaseEch)) {
+ SSL_TRC(100, ("%d: TLS13[%d]: server not sending HRR ECH Xtn",
+ SSL_GETPID(), ss->fd));
+ return SECSuccess;
+ }
+ SSL_TRC(100, ("%d: TLS13[%d]: server sending HRR ECH Xtn",
+ SSL_GETPID(), ss->fd));
+ PR_ASSERT(SSL_BUFFER_LEN(&ss->ssl3.hs.greaseEchBuf) == TLS13_ECH_SIGNAL_LEN);
+ PRINT_BUF(100, (ss, "grease_ech_confirmation", ss->ssl3.hs.greaseEchBuf.buf, TLS13_ECH_SIGNAL_LEN));
+ rv = sslBuffer_AppendBuffer(buf, &ss->ssl3.hs.greaseEchBuf);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_ServerHandleInnerEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ PRUint64 xtn_type;
+ sslReader xtnReader = SSL_READER(data->data, data->len);
+
+ PR_ASSERT(ss->ssl3.hs.echAccepted || ss->opt.enableTls13BackendEch);
+ PR_ASSERT(!xtnData->ech->receivedInnerXtn);
+
+ SECStatus rv = sslRead_ReadNumber(&xtnReader, 1, &xtn_type);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ if (xtn_type != ech_xtn_type_inner) {
+ goto alert_loser;
+ }
+ if (SSL_READER_REMAINING(&xtnReader)) {
+ /* Inner ECH Extension must contain only type enum */
+ goto alert_loser;
+ }
+
+ xtnData->ech->receivedInnerXtn = PR_TRUE;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
+ return SECSuccess;
+
+alert_loser:
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+ return SECFailure;
+}
+
+SECStatus
+tls13_ServerHandleOuterEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ SECStatus rv;
+ HpkeKdfId kdf;
+ HpkeAeadId aead;
+ PRUint32 tmp;
+ PRUint8 configId;
+ SECItem senderPubKey;
+ SECItem encryptedCh;
+
+ PRUint32 xtn_type;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &xtn_type, 1, &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ if (xtn_type != ech_xtn_type_outer && xtn_type != ech_xtn_type_inner) {
+ SSL_TRC(3, ("%d: TLS13[%d]: unexpected ECH extension type in client hello outer, alert",
+ SSL_GETPID(), ss->fd));
+ goto alert_loser;
+ }
+ /* If we are operating in shared mode, we can accept an inner xtn in the ClientHelloOuter */
+ if (xtn_type == ech_xtn_type_inner) {
+ if (!ss->opt.enableTls13BackendEch) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
+ return SECFailure;
+ }
+ /* We have to rewind the buffer advanced by ssl3_ExtConsumeHandshakeNumber */
+ data->data--;
+ data->len++;
+ return tls13_ServerHandleInnerEchXtn(ss, xtnData, data);
+ }
+ if (ss->ssl3.hs.echAccepted) {
+ ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
+ PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
+ return SECFailure;
+ }
+
+ SSL_TRC(3, ("%d: TLS13[%d]: handle outer ECH extension",
+ SSL_GETPID(), ss->fd));
+
+ PORT_Assert(!xtnData->ech);
+ xtnData->ech = PORT_ZNew(sslEchXtnState);
+ if (!xtnData->ech) {
+ return SECFailure;
+ }
+
+ /* Parse the KDF and AEAD. */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ kdf = (HpkeKdfId)tmp;
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ aead = (HpkeAeadId)tmp;
+
+ /* config_id */
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 1,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ configId = tmp;
+
+ /* enc */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &senderPubKey, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+
+ /* payload, which must be final and non-empty. */
+ xtnData->ech->payloadStart = data->data + 2; /* Move past length */
+ rv = ssl3_ExtConsumeHandshakeVariable(ss, &encryptedCh, 2,
+ &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+ if (data->len || !encryptedCh.len) {
+ goto alert_loser;
+ }
+
+ if (!ss->ssl3.hs.helloRetry) {
+ /* In the real ECH HRR case, config_id and enc should be empty. This
+ * is checked after acceptance, because it might be GREASE ECH. */
+ if (!senderPubKey.len) {
+ goto alert_loser;
+ }
+
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->senderPubKey, &senderPubKey);
+ if (rv == SECFailure) {
+ return SECFailure;
+ }
+ }
+
+ rv = SECITEM_CopyItem(NULL, &xtnData->ech->innerCh, &encryptedCh);
+ PRINT_BUF(100, (ss, "CT for ECH Decryption", encryptedCh.data, encryptedCh.len));
+ if (rv == SECFailure) {
+ return SECFailure;
+ }
+ xtnData->ech->configId = configId;
+ xtnData->ech->kdfId = kdf;
+ xtnData->ech->aeadId = aead;
+
+ /* Not negotiated until tls13_MaybeAcceptEch. */
+ return SECSuccess;
+
+alert_loser:
+ ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
+ PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
+ return SECFailure;
+}
+
+SECStatus
+tls13_SendEmptyGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (!ss->opt.enableGrease ||
+ (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
+ (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+ return SECSuccess;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+tls13_SendGreaseXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ if (!ss->opt.enableGrease ||
+ (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
+ (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
+ return SECSuccess;
+ }
+
+ SECStatus rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ 0x00 }, 1, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+SECStatus
+ssl3_SendCertificateCompressionXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ sslBuffer *buf, PRBool *added)
+{
+ /* enum {
+ * zlib(1),
+ * brotli(2),
+ * zstd(3),
+ * (65535)
+ * } CertificateCompressionAlgorithm;
+ *
+ * struct {
+ * CertificateCompressionAlgorithm algorithms<2..2^8-2>;
+ * } CertificateCompressionAlgorithms;
+ */
+
+ SECStatus rv = SECFailure;
+ if (ss->ssl3.cwSpec->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ SSL_TRC(50, ("%d: TLS13[%d]: certificate_compression_algorithm extension requires TLS1.3 and above",
+ SSL_GETPID(), ss->fd));
+ return SECSuccess;
+ }
+
+ size_t certificateCompressionAlgorithmsLen = ss->ssl3.supportedCertCompressionAlgorithmsCount;
+ if (certificateCompressionAlgorithmsLen == 0) {
+ SSL_TRC(30, ("%d: TLS13[%d]: %s does not support any certificate compression algorithm",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
+ return SECSuccess;
+ }
+
+ SSL_TRC(30, ("%d: TLS13[%d]: %s sends certificate_compression_algorithm extension",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
+ PORT_Assert(certificateCompressionAlgorithmsLen < (0x1u << 8) - 1);
+
+ rv = sslBuffer_AppendNumber(buf, certificateCompressionAlgorithmsLen << 1, 1);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+
+ for (size_t i = 0; i < certificateCompressionAlgorithmsLen; i++) {
+ rv = sslBuffer_AppendNumber(buf, ss->ssl3.supportedCertCompressionAlgorithms[i].id, 2);
+ if (rv != SECSuccess) {
+ return SECFailure;
+ }
+ }
+
+ xtnData->certificateCompressionAdvertised = PR_TRUE;
+ *added = PR_TRUE;
+ return SECSuccess;
+}
+
+const char *
+ssl3_mapCertificateCompressionAlgorithmToName(const sslSocket *ss, SSLCertificateCompressionAlgorithmID alg)
+{
+ for (int i = 0; i < ss->ssl3.supportedCertCompressionAlgorithmsCount; i++) {
+ if (ss->ssl3.supportedCertCompressionAlgorithms[i].id == alg) {
+ return ss->ssl3.supportedCertCompressionAlgorithms[i].name;
+ }
+ }
+ return "unknown";
+}
+
+SECStatus
+ssl3_HandleCertificateCompressionXtn(const sslSocket *ss,
+ TLSExtensionData *xtnData,
+ SECItem *data)
+{
+ /* This extension is only supported with TLS 1.3 [RFC8446] and newer;
+ * if TLS 1.2 [RFC5246] or earlier is negotiated, the peers MUST ignore this extension.
+ */
+ if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
+ SSL_TRC(50, ("%d: TLS13[%d]: ignore certificate_compression extension",
+ SSL_GETPID(), ss->fd));
+ return SECSuccess;
+ }
+
+ SECStatus rv = SECFailure;
+ PRUint32 lengthSupportedAlgorithms = 0;
+ PRUint32 certComprAlgId = 0;
+
+ SSL_TRC(30, ("%d: TLS13[%d]: %s handles certificate_compression_algorithm extension",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
+
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &lengthSupportedAlgorithms, 1, &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+
+ /* Each of the algorithm is 2 bytes. */
+ if (lengthSupportedAlgorithms % 2 != 0) {
+ goto alert_loser;
+ }
+
+ if (data->len != lengthSupportedAlgorithms) {
+ goto alert_loser;
+ }
+
+ SECStatus algFound = SECFailure;
+
+ /* We use the first common algorithm we found. */
+ for (int i = 0; i < lengthSupportedAlgorithms / 2; i++) {
+ rv = ssl3_ExtConsumeHandshakeNumber(ss, &certComprAlgId, 2, &data->data, &data->len);
+ if (rv != SECSuccess) {
+ goto alert_loser;
+ }
+
+ SSLCertificateCompressionAlgorithmID alg = (SSLCertificateCompressionAlgorithmID)certComprAlgId;
+ if (alg == 0) {
+ SSL_TRC(50, ("%d: TLS13[%d]: certificate compression ignores reserved algorithm %02x",
+ SSL_GETPID(), ss->fd, alg));
+ continue;
+ }
+
+ for (int j = 0; j < ss->ssl3.supportedCertCompressionAlgorithmsCount; j++) {
+ if (ss->ssl3.supportedCertCompressionAlgorithms[j].id == alg) {
+ xtnData->compressionAlg = alg;
+ xtnData->negotiated[xtnData->numNegotiated++] = ssl_certificate_compression_xtn;
+ algFound = SECSuccess;
+ break;
+ }
+ }
+
+ if (algFound == SECSuccess) {
+ break;
+ }
+ }
+
+ if (algFound == SECSuccess) {
+ SSL_TRC(30, ("%d: TLS13[%d]: %s established certificate compression algorithm %s",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss),
+ ssl3_mapCertificateCompressionAlgorithmToName(ss, xtnData->compressionAlg)));
+ } else {
+ SSL_TRC(30, ("%d: TLS13[%d]: no common certificate compression algorithms found on the %s side",
+ SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
+ }
+
+ return SECSuccess;
+
+alert_loser:
+ ssl3_ExtDecodeError(ss);
+ return SECFailure;
+}