/* -*- 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 "sslt.h"
#include "tls13con.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;

    /* A standalone KEM share has no ECDH `keys`; only the KEM key/ciphertext
     * contributes to the size below. */
    if (keyPair->keys) {
        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 ||
                    keyPair->group->name == ssl_grp_kem_mlkem768x25519 ||
                    keyPair->group->name == ssl_grp_kem_secp256r1mlkem768 ||
                    keyPair->group->name == ssl_grp_kem_secp384r1mlkem1024 ||
                    keyPair->group->name == ssl_grp_kem_mlkem1024);
        size += keyPair->kemKeys->pubKey->u.kyber.publicValue.len;
    }
    if (keyPair->kemCt) {
        PORT_Assert(!keyPair->kemKeys);
        PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00 ||
                    keyPair->group->name == ssl_grp_kem_mlkem768x25519 ||
                    keyPair->group->name == ssl_grp_kem_secp256r1mlkem768 ||
                    keyPair->group->name == ssl_grp_kem_secp384r1mlkem1024 ||
                    keyPair->group->name == ssl_grp_kem_mlkem1024);
        size += keyPair->kemCt->len;
    }

    return size;
}

static SECStatus
tls13_WriteHybridECCKeyFirst(sslBuffer *buf, sslEphemeralKeyPair *keyPair)
{
    PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00 ||
                keyPair->group->name == ssl_grp_kem_secp256r1mlkem768 ||
                keyPair->group->name == ssl_grp_kem_secp384r1mlkem1024);
    PORT_Assert(keyPair->keys->pubKey->keyType == ecKey);

    // Encode the ecc share first, then the MLKEM key or ciphertext.
    SECStatus rv;
    rv = sslBuffer_Append(buf, keyPair->keys->pubKey->u.ec.publicValue.data,
                          keyPair->keys->pubKey->u.ec.publicValue.len);
    if (rv != SECSuccess) {
        return rv;
    }

    if (keyPair->kemKeys) {
        PORT_Assert(!keyPair->kemCt);
        rv = sslBuffer_Append(buf, keyPair->kemKeys->pubKey->u.kyber.publicValue.data, keyPair->kemKeys->pubKey->u.kyber.publicValue.len);
    } else if (keyPair->kemCt) {
        rv = sslBuffer_Append(buf, keyPair->kemCt->data, keyPair->kemCt->len);
    } else {
        PORT_Assert(0);
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        rv = SECFailure;
    }
    return rv;
}

static SECStatus
tls13_WriteHybridHybridKeyFirst(sslBuffer *buf, sslEphemeralKeyPair *keyPair)
{
    PORT_Assert(keyPair->group->name == ssl_grp_kem_mlkem768x25519);
    PORT_Assert(keyPair->keys->pubKey->keyType == ecKey);

    // Encode the ML-KEM-768 key or ciphertext first, then the X25519 share.
    SECStatus rv;
    if (keyPair->kemKeys) {
        PORT_Assert(!keyPair->kemCt);
        rv = sslBuffer_Append(buf, keyPair->kemKeys->pubKey->u.kyber.publicValue.data, keyPair->kemKeys->pubKey->u.kyber.publicValue.len);
    } else if (keyPair->kemCt) {
        rv = sslBuffer_Append(buf, keyPair->kemCt->data, keyPair->kemCt->len);
    } else {
        PORT_Assert(0);
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        rv = SECFailure;
    }
    if (rv != SECSuccess) {
        return rv;
    }

    rv = sslBuffer_Append(buf, keyPair->keys->pubKey->u.ec.publicValue.data,
                          keyPair->keys->pubKey->u.ec.publicValue.len);
    return rv;
}

static SECStatus
tls13_WriteKEMKeyShare(sslBuffer *buf, sslEphemeralKeyPair *keyPair)
{
    PORT_Assert(keyPair->group->name == ssl_grp_kem_mlkem1024);
    PORT_Assert(!keyPair->keys);

    /* Standalone KEM: write only the ML-KEM encapsulation key (client) or the
     * ciphertext (server). There is no ECDH share. */
    if (keyPair->kemKeys) {
        PORT_Assert(!keyPair->kemCt);
        return sslBuffer_Append(buf,
                                keyPair->kemKeys->pubKey->u.kyber.publicValue.data,
                                keyPair->kemKeys->pubKey->u.kyber.publicValue.len);
    }
    if (keyPair->kemCt) {
        return sslBuffer_Append(buf, keyPair->kemCt->data, keyPair->kemCt->len);
    }
    PORT_Assert(0);
    PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
    return SECFailure;
}

static SECStatus
tls13_WriteKeyExchangeInfo(sslBuffer *buf, sslEphemeralKeyPair *keyPair)
{
    SECStatus 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);
            rv = SECFailure;
            break;
    }

    return rv;
}

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

    switch (keyPair->group->name) {
        case ssl_grp_kem_mlkem768x25519:
            rv = tls13_WriteHybridHybridKeyFirst(buf, keyPair);
            break;
        case ssl_grp_kem_secp256r1mlkem768:
        case ssl_grp_kem_secp384r1mlkem1024:
        case ssl_grp_kem_xyber768d00:
            rv = tls13_WriteHybridECCKeyFirst(buf, keyPair);
            break;
        case ssl_grp_kem_mlkem1024:
            rv = tls13_WriteKEMKeyShare(buf, keyPair);
            break;
        default:
            rv = tls13_WriteKeyExchangeInfo(buf, keyPair);
            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);
        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. */
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
    }

    ssl3_RecordExtensionNegotiated(ss, xtnData, 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. */
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
    }

    ssl3_RecordExtensionNegotiated(ss, xtnData, 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. */
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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);

    SECItem cookie;
    rv = ssl3_ExtConsumeHandshakeVariable(
        ss, &cookie, 2,
        &data->data, &data->len);
    if (rv != SECSuccess) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
        return SECFailure;
    }
    if (!cookie.len || data->len) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
        return SECFailure;
    }

    PORT_Assert(!ss->ssl3.hs.cookie.data && !ss->ssl3.hs.cookie.len);
    SECITEM_FreeItem(&CONST_CAST(sslSocket, ss)->ssl3.hs.cookie, PR_FALSE);
    rv = SECITEM_CopyItem(NULL, &CONST_CAST(sslSocket, ss)->ssl3.hs.cookie, &cookie);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    return SECSuccess;
}

SECStatus
tls13_ClientSendHrrCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             sslBuffer *buf, PRBool *added)
{
    SECStatus rv;

    /* Only send the TLS 1.3 Cookie extension in response to a
     * HelloRetryRequest. If we are replying to a DTLS 1.2
     * HelloVerifyRequest, the cookie must be carried in the DTLS
     * ClientHello cookie field, not as a TLS 1.3 extension.
     */
    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
        !ss->ssl3.hs.cookie.len || !ss->ssl3.hs.helloRetry) {
        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. */
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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. */
        ssl3_RecordExtensionNegotiated(ss, xtnData, 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. */
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
                if (ss->sec.isServer) {
                    ssl3_RecordExtensionNegotiated(ss, xtnData, 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;
}
