diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-07 19:33:14 +0000 |
commit | 36d22d82aa202bb199967e9512281e9a53db42c9 (patch) | |
tree | 105e8c98ddea1c1e4784a60a5a6410fa416be2de /security/nss/lib/ssl/tls13exthandle.c | |
parent | Initial commit. (diff) | |
download | firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.tar.xz firefox-esr-36d22d82aa202bb199967e9512281e9a53db42c9.zip |
Adding upstream version 115.7.0esr.upstream/115.7.0esrupstream
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.c | 1802 |
1 files changed, 1802 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..4d8c711bd5 --- /dev/null +++ b/security/nss/lib/ssl/tls13exthandle.c @@ -0,0 +1,1802 @@ +/* -*- 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; +} + +/* + * [draft-ietf-tls-tls13-11] Section 6.3.2.3. + * + * struct { + * NamedGroup group; + * opaque key_exchange<1..2^16-1>; + * } KeyShareEntry; + * + * struct { + * select (role) { + * case client: + * KeyShareEntry client_shares<4..2^16-1>; + * + * case server: + * KeyShareEntry server_share; + * } + * } KeyShare; + * + * DH is Section 6.3.2.3.1. + * + * opaque dh_Y<1..2^16-1>; + * + * ECDH is Section 6.3.2.3.2. + * + * opaque point <1..2^8-1>; + */ +PRUint32 +tls13_SizeOfKeyShareEntry(const SECKEYPublicKey *pubKey) +{ + /* Size = NamedGroup(2) + length(2) + opaque<?> share */ + switch (pubKey->keyType) { + case ecKey: + return 2 + 2 + pubKey->u.ec.publicValue.len; + case dhKey: + return 2 + 2 + pubKey->u.dh.prime.len; + default: + PORT_Assert(0); + } + return 0; +} + +SECStatus +tls13_EncodeKeyShareEntry(sslBuffer *buf, SSLNamedGroup group, + SECKEYPublicKey *pubKey) +{ + SECStatus rv; + unsigned int size = tls13_SizeOfKeyShareEntry(pubKey); + + rv = sslBuffer_AppendNumber(buf, group, 2); + if (rv != SECSuccess) + return rv; + rv = sslBuffer_AppendNumber(buf, size - 4, 2); + if (rv != SECSuccess) + return rv; + + 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; + } + + 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->group->name, + keyPair->keys->pubKey); + 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->group->name, + keyPair->keys->pubKey); + 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; +} |