Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/security/nss/lib/ssl/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 64 kB image not shown  

Quelle  tls13exthandle.c   Sprache: C

 
/* -*- 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;

    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);
        pubKey = keyPair->kemKeys->pubKey;
        size += pubKey->u.kyber.publicValue.len;
    }
    if (keyPair->kemCt) {
        PORT_Assert(!keyPair->kemKeys);
        PORT_Assert(keyPair->group->name == ssl_grp_kem_xyber768d00 || keyPair->group->name == ssl_grp_kem_mlkem768x25519);
        size += keyPair->kemCt->len;
    }

    return size;
}

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

    // Encode the X25519 share first, then the Kyber768 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_WriteMLKEM768X25519KeyExchangeInfo(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_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_WriteMLKEM768X25519KeyExchangeInfo(buf, keyPair);
            break;
        case ssl_grp_kem_xyber768d00:
            rv = tls13_WriteXyber768D00KeyExchangeInfo(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. */
    xtnData->negotiated[xtnData->numNegotiated++] =
        ssl_tls13_key_share_xtn;

    return SECSuccess;

loser:
    tls13_DestroyKeyShares(&xtnData->remoteKeyShares);
    return SECFailure;
}

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

    /* There should be exactly one key share. */
    PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
    PORT_Assert(PR_PREV_LINK(&ss->ephemeralKeyPairs) ==
                PR_NEXT_LINK(&ss->ephemeralKeyPairs));

    keyPair = (sslEphemeralKeyPair *)PR_NEXT_LINK(&ss->ephemeralKeyPairs);

    rv = tls13_EncodeKeyShareEntry(buf, keyPair);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/* Called by clients.
 *
 *      struct {
 *         opaque identity<0..2^16-1>;
 *         uint32 obfuscated_ticket_age;
 *     } PskIdentity;
 *
 *     opaque PskBinderEntry<32..255>;
 *
 *     struct {
 *         select (Handshake.msg_type) {
 *             case client_hello:
 *                 PskIdentity identities<6..2^16-1>;
 *                 PskBinderEntry binders<33..2^16-1>;
 *
 *             case server_hello:
 *                 uint16 selected_identity;
 *         };
 *
 *     } PreSharedKeyExtension;
 */

SECStatus
tls13_ClientSendPreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                sslBuffer *buf, PRBool *added)
{
    const static PRUint8 binder[TLS13_MAX_FINISHED_SIZE] = { 0 };
    unsigned int binderLen;
    unsigned int identityLen = 0;
    const PRUint8 *identity = NULL;
    PRTime age;
    SECStatus rv;

    /* Exit early if no PSKs or max version < 1.3. */
    if (PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks) ||
        ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    /* ...or if PSK type is resumption, but we're not resuming. */
    sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);
    if (psk->type == ssl_psk_resume && !ss->statelessResume) {
        return SECSuccess;
    }

    /* ...or if PSKs are incompatible with negotiated ciphersuites
     * (different hash algorithms) on HRR.
     *
     * In addition, in its updated ClientHello, the client SHOULD NOT offer any
     * pre-shared keys associated with a hash other than that of the selected
     * cipher suite.  This allows the client to avoid having to compute partial
     * hash transcripts for multiple hashes in the second ClientHello
     * [RFC8446, Section 4.1.4]. */

    if (ss->ssl3.hs.helloRetry &&
        (psk->hash != ss->ssl3.hs.suite_def->prf_hash)) {
        return SECSuccess;
    }

    /* Save where this extension starts so that if we have to add padding, it
     * can be inserted before this extension. */

    PORT_Assert(buf->len >= 4);
    xtnData->lastXtnOffset = buf->len - 4;
    PORT_Assert(psk->type == ssl_psk_resume || psk->type == ssl_psk_external);
    binderLen = tls13_GetHashSizeForHash(psk->hash);
    if (psk->type == ssl_psk_resume) {
        /* Send a single ticket identity. */
        NewSessionTicket *session_ticket = &ss->sec.ci.sid->u.ssl3.locked.sessionTicket;
        identityLen = session_ticket->ticket.len;
        identity = session_ticket->ticket.data;

        /* Obfuscated age. */
        age = ssl_Time(ss) - session_ticket->received_timestamp;
        age /= PR_USEC_PER_MSEC;
        age += session_ticket->ticket_age_add;
        PRINT_BUF(50, (ss, "Sending Resumption PSK with identity", identity, identityLen));
    } else if (psk->type == ssl_psk_external) {
        identityLen = psk->label.len;
        identity = psk->label.data;
        age = 0;
        PRINT_BUF(50, (ss, "Sending External PSK with label", identity, identityLen));
    } else {
        PORT_Assert(0);
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* Length is len(identityLen) + identityLen + len(age) */
    rv = sslBuffer_AppendNumber(buf, 2 + identityLen + 4, 2);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = sslBuffer_AppendVariable(buf, identity,
                                  identityLen, 2);
    if (rv != SECSuccess) {
        goto loser;
    }

    rv = sslBuffer_AppendNumber(buf, age, 4);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* Write out the binder list length. */
    rv = sslBuffer_AppendNumber(buf, binderLen + 1, 2);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* Write zeroes for the binder for the moment. These
     * are overwritten in tls13_WriteExtensionsWithBinder. */

    rv = sslBuffer_AppendVariable(buf, binder, binderLen, 1);
    if (rv != SECSuccess) {
        goto loser;
    }

    if (psk->type == ssl_psk_resume) {
        xtnData->sentSessionTicketInClientHello = PR_TRUE;
    }

    *added = PR_TRUE;
    return SECSuccess;

loser:
    xtnData->ticketTimestampVerified = PR_FALSE;
    return SECFailure;
}

/* Handle a TLS 1.3 PreSharedKey Extension. */
SECStatus
tls13_ServerHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    SECItem inner;
    SECStatus rv;
    unsigned int numIdentities = 0;
    unsigned int numBinders = 0;
    SECItem *appToken;

    SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
                SSL_GETPID(), ss->fd));

    /* If we are doing < TLS 1.3, then ignore this. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    /* The application token is set via the cookie extension if this is the
     * second ClientHello.  Don't set it twice.  The cookie extension handler
     * sets |helloRetry| and that will have been called already because this
     * extension always comes last. */

    if (!ss->ssl3.hs.helloRetry) {
        appToken = &xtnData->applicationToken;
    } else {
        appToken = NULL;
    }

    /* Parse the identities list. */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &inner, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    while (inner.len) {
        SECItem label;
        PRUint32 obfuscatedAge;

        rv = ssl3_ExtConsumeHandshakeVariable(ss, &label, 2,
                                              &inner.data, &inner.len);
        if (rv != SECSuccess)
            return rv;
        if (!label.len) {
            goto alert_loser;
        }

        rv = ssl3_ExtConsumeHandshakeNumber(ss, &obfuscatedAge, 4,
                                            &inner.data, &inner.len);
        if (rv != SECSuccess)
            return rv;

        if (!numIdentities) {
            /* Check any configured external PSK for a matching label.
             * If none exists, try to parse it as a ticket. */

            PORT_Assert(!xtnData->selectedPsk);
            for (PRCList *cur_p = PR_LIST_HEAD(&ss->ssl3.hs.psks);
                 cur_p != &ss->ssl3.hs.psks;
                 cur_p = PR_NEXT_LINK(cur_p)) {
                sslPsk *psk = (sslPsk *)cur_p;
                if (psk->type != ssl_psk_external ||
                    SECITEM_CompareItem(&psk->label, &label) != SECEqual) {
                    continue;
                }
                PRINT_BUF(50, (ss, "Using External PSK with label",
                               psk->label.data, psk->label.len));
                xtnData->selectedPsk = psk;
            }

            if (!xtnData->selectedPsk) {
                PRINT_BUF(50, (ss, "Handling PreSharedKey value",
                               label.data, label.len));
                rv = ssl3_ProcessSessionTicketCommon(
                    CONST_CAST(sslSocket, ss), &label, appToken);
                /* This only happens if we have an internal error, not
                 * a malformed ticket. Bogus tickets just don't resume
                 * and return SECSuccess. */

                if (rv != SECSuccess) {
                    return SECFailure;
                }

                if (ss->sec.ci.sid) {
                    /* xtnData->ticketAge contains the baseline we use for
                     * calculating the ticket age (i.e., our RTT estimate less the
                     * value of ticket_age_add).
                     *
                     * Add that to the obfuscated ticket age to recover the client's
                     * view of the ticket age plus the estimated RTT.
                     *
                     * See ssl3_EncodeSessionTicket() for details. */

                    xtnData->ticketAge += obfuscatedAge;

                    /* We are not committed to resumption until after unwrapping the
                     * RMS in tls13_HandleClientHelloPart2. The RPSK will be stored
                     * in ss->xtnData.selectedPsk at that point, so continue. */

                }
            }
        }

        ++numIdentities;
    }

    xtnData->pskBindersLen = data->len;

    /* Parse the binders list. */
    rv = ssl3_ExtConsumeHandshakeVariable(ss,
                                          &inner, 2, &data->data, &data->len);
    if (rv != SECSuccess)
        return SECFailure;
    if (data->len) {
        goto alert_loser;
    }

    while (inner.len) {
        SECItem binder;
        rv = ssl3_ExtConsumeHandshakeVariable(ss, &binder, 1,
                                              &inner.data, &inner.len);
        if (rv != SECSuccess)
            return rv;
        if (binder.len < 32) {
            goto alert_loser;
        }

        if (!numBinders) {
            xtnData->pskBinder = binder;
        }
        ++numBinders;
    }

    if (numBinders != numIdentities)
        goto alert_loser;

    if (ss->statelessResume) {
        PORT_Assert(!ss->xtnData.selectedPsk);
    } else if (!xtnData->selectedPsk) {
        /* No matching EPSK. */
        return SECSuccess;
    }

    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
    return SECSuccess;

alert_loser:
    ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
    PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
    return SECFailure;
}

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

    /* We only process the first session ticket the client sends,
     * so the index is always 0. */

    rv = sslBuffer_AppendNumber(buf, 0, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/* Handle a TLS 1.3 PreSharedKey Extension. */
SECStatus
tls13_ClientHandlePreSharedKeyXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    PRUint32 index;
    SECStatus rv;

    SSL_TRC(3, ("%d: SSL3[%d]: handle pre_shared_key extension",
                SSL_GETPID(), ss->fd));

    /* The server must not send this extension when negotiating < TLS 1.3. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
        return SECFailure;
    }

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &index, 2, &data->data, &data->len);
    if (rv != SECSuccess)
        return SECFailure;

    /* This should be the end of the extension. */
    if (data->len) {
        PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
        return SECFailure;
    }

    /* We only sent one PSK label so index must be equal to 0 */
    if (index) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_MALFORMED_PRE_SHARED_KEY);
        return SECFailure;
    }

    PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks));
    sslPsk *candidate = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);

    /* Check that the server-selected ciphersuite hash and PSK hash match. */
    if (candidate->hash != tls13_GetHashForCipherSuite(ss->ssl3.hs.cipher_suite)) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_pre_shared_key_xtn;
    xtnData->selectedPsk = candidate;

    return SECSuccess;
}

/*
 *  struct { } EarlyDataIndication;
 */

SECStatus
tls13_ClientSendEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             sslBuffer *buf, PRBool *added)
{
    if (!tls13_ClientAllow0Rtt(ss, ss->sec.ci.sid)) {
        return SECSuccess;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ServerHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                               SECItem *data)
{
    SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
                SSL_GETPID(), ss->fd));

    /* If we are doing < TLS 1.3, then ignore this. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    if (ss->ssl3.hs.helloRetry) {
        ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        return SECFailure;
    }

    if (data->len) {
        PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
        return SECFailure;
    }

    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;

    return SECSuccess;
}

/* This will only be called if we also offered the extension. */
SECStatus
tls13_ClientHandleEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                               SECItem *data)
{
    SSL_TRC(3, ("%d: TLS13[%d]: handle early_data extension",
                SSL_GETPID(), ss->fd));

    /* The server must not send this extension when negotiating < TLS 1.3. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
        return SECFailure;
    }

    if (data->len) {
        PORT_SetError(SSL_ERROR_MALFORMED_EARLY_DATA);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_early_data_xtn;

    return SECSuccess;
}

SECStatus
tls13_ClientHandleTicketEarlyDataXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                     SECItem *data)
{
    PRUint32 utmp;
    SECStatus rv;

    SSL_TRC(3, ("%d: TLS13[%d]: handle ticket early_data extension",
                SSL_GETPID(), ss->fd));

    /* The server must not send this extension when negotiating < TLS 1.3. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        PORT_SetError(SSL_ERROR_EXTENSION_DISALLOWED_FOR_VERSION);
        return SECFailure;
    }

    rv = ssl3_ExtConsumeHandshake(ss, &utmp, sizeof(utmp),
                                  &data->data, &data->len);
    if (rv != SECSuccess) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
        return SECFailure;
    }
    if (data->len) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_NEW_SESSION_TICKET);
        return SECFailure;
    }

    xtnData->max_early_data_size = PR_ntohl(utmp);

    return SECSuccess;
}

/*
 *     struct {
 *       select (Handshake.msg_type) {
 *       case client_hello:
 *          ProtocolVersion versions<2..254>;
 *       case server_hello:
 *          ProtocolVersion version;
 *       };
 *     } SupportedVersions;
 */

SECStatus
tls13_ClientSendSupportedVersionsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                     sslBuffer *buf, PRBool *added)
{
    PRUint16 version;
    unsigned int lengthOffset;
    SECStatus rv;

    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: client send supported_versions extension",
                SSL_GETPID(), ss->fd));

    rv = sslBuffer_Skip(buf, 1, &lengthOffset);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    PORT_Assert(!ss->ssl3.hs.echHpkeCtx || ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);
    for (version = ss->vrange.max; version >= ss->vrange.min; --version) {
        PRUint16 wire = tls13_EncodeVersion(version,
                                            ss->protocolVariant);
        rv = sslBuffer_AppendNumber(buf, wire, 2);
        if (rv != SECSuccess) {
            return SECFailure;
        }

        if (ss->opt.enableDtls13VersionCompat &&
            ss->protocolVariant == ssl_variant_datagram) {
            switch (version) {
                case SSL_LIBRARY_VERSION_TLS_1_2:
                case SSL_LIBRARY_VERSION_TLS_1_1:
                    rv = sslBuffer_AppendNumber(buf, (PRUint16)version, 2);
                    break;
                default:
                    continue;
            }
            if (rv != SECSuccess) {
                return SECFailure;
            }
        }
    }

    /* GREASE SupportedVersions:
     * A client MAY select one or more GREASE version values and advertise them
     * in the "supported_versions" extension, if sent [RFC8701, Section 3.1]. */

    if (ss->opt.enableGrease) {
        rv = sslBuffer_AppendNumber(buf, ss->ssl3.hs.grease->idx[grease_version], 2);
        if (rv != SECSuccess) {
            return SECFailure;
        }
    }

    rv = sslBuffer_InsertLength(buf, lengthOffset, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

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

    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: server send supported_versions extension",
                SSL_GETPID(), ss->fd));

    PRUint16 ver = tls13_EncodeVersion(SSL_LIBRARY_VERSION_TLS_1_3,
                                       ss->protocolVariant);
    rv = sslBuffer_AppendNumber(buf, ver, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/*
 *    struct {
 *        opaque cookie<1..2^16-1>;
 *    } Cookie;
 */

SECStatus
tls13_ClientHandleHrrCookie(const sslSocket *ss, TLSExtensionData *xtnData,
                            SECItem *data)
{
    SECStatus rv;

    SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
                SSL_GETPID(), ss->fd));

    PORT_Assert(ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3);

    /* IMPORTANT: this is only valid while the HelloRetryRequest is still valid. */
    rv = ssl3_ExtConsumeHandshakeVariable(
        ss, &CONST_CAST(sslSocket, ss)->ssl3.hs.cookie, 2,
        &data->data, &data->len);
    if (rv != SECSuccess) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
        return SECFailure;
    }
    if (!ss->ssl3.hs.cookie.len || data->len) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HELLO_RETRY_REQUEST);
        return SECFailure;
    }

    return SECSuccess;
}

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

    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
        !ss->ssl3.hs.cookie.len) {
        return SECSuccess;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: send cookie extension", SSL_GETPID(), ss->fd));
    rv = sslBuffer_AppendVariable(buf, ss->ssl3.hs.cookie.data,
                                  ss->ssl3.hs.cookie.len, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ServerHandleCookieXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                            SECItem *data)
{
    SECStatus rv;

    SSL_TRC(3, ("%d: TLS13[%d]: handle cookie extension",
                SSL_GETPID(), ss->fd));

    rv = ssl3_ExtConsumeHandshakeVariable(ss, &xtnData->cookie, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (xtnData->cookie.len == 0) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }

    if (data->len) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_cookie_xtn;

    return SECSuccess;
}

SECStatus
tls13_ClientSendPostHandshakeAuthXtn(const sslSocket *ss,
                                     TLSExtensionData *xtnData,
                                     sslBuffer *buf, PRBool *added)
{
    /* Only one post-handshake message is supported: a single
     * NST immediately following the client Finished. */

    if (!IS_DTLS(ss)) {
        SSL_TRC(3, ("%d: TLS13[%d]: send post_handshake_auth extension",
                    SSL_GETPID(), ss->fd));
        *added = ss->opt.enablePostHandshakeAuth;
    }
    return SECSuccess;
}

SECStatus
tls13_ServerHandlePostHandshakeAuthXtn(const sslSocket *ss,
                                       TLSExtensionData *xtnData,
                                       SECItem *data)
{
    SSL_TRC(3, ("%d: TLS13[%d]: handle post_handshake_auth extension",
                SSL_GETPID(), ss->fd));

    if (data->len) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }

    /* Only one post-handshake message is supported: a single
     * NST immediately following the client Finished. */

    if (!IS_DTLS(ss)) {
        /* Keep track of negotiated extensions. */
        xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_post_handshake_auth_xtn;
    }

    return SECSuccess;
}

/*
 *     enum { psk_ke(0), psk_dhe_ke(1), (255) } PskKeyExchangeMode;
 *
 *     struct {
 *         PskKeyExchangeMode ke_modes<1..255>;
 *     } PskKeyExchangeModes;
 */

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

    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
        ss->opt.noCache) {
        return SECSuccess;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: send psk key exchange modes extension",
                SSL_GETPID(), ss->fd));

    /* GREASE PskKeyExchangeMode:
     * A client MAY select one or more GREASE PskKeyExchangeMode values and
     * advertise them in the "psk_key_exchange_modes" extension, if sent
     * [RFC8701, Section 3.1]. */

    if (ss->opt.enableGrease) {
        rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke, ss->ssl3.hs.grease->pskKem }, 2, 1);
    } else {
        rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ tls13_psk_dh_ke }, 1, 1);
    }
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ServerHandlePskModesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                              SECItem *data)
{
    SECStatus rv;

    /* If we are doing < TLS 1.3, then ignore this. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: handle PSK key exchange modes extension",
                SSL_GETPID(), ss->fd));

    /* IMPORTANT: We aren't copying these values, just setting pointers.
     * They will only be valid as long as the ClientHello is in memory. */

    rv = ssl3_ExtConsumeHandshakeVariable(ss,
                                          &xtnData->psk_ke_modes, 1,
                                          &data->data, &data->len);
    if (rv != SECSuccess)
        return rv;
    if (!xtnData->psk_ke_modes.len || data->len) {
        PORT_SetError(SSL_ERROR_MALFORMED_PSK_KEY_EXCHANGE_MODES);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] =
        ssl_tls13_psk_key_exchange_modes_xtn;

    return SECSuccess;
}

SECStatus
tls13_SendCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             sslBuffer *buf, PRBool *added)
{
    unsigned int calen;
    const SECItem *name;
    unsigned int nnames;
    SECStatus rv;

    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);

    rv = ssl_GetCertificateRequestCAs(ss, &calen, &name, &nnames);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (!calen) {
        return SECSuccess;
    }

    rv = sslBuffer_AppendNumber(buf, calen, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    while (nnames) {
        rv = sslBuffer_AppendVariable(buf, name->data, name->len, 2);
        if (rv != SECSuccess) {
            return SECFailure;
        }
        ++name;
        --nnames;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ClientHandleCertAuthoritiesXtn(const sslSocket *ss,
                                     TLSExtensionData *xtnData,
                                     SECItem *data)
{
    SECStatus rv;
    PLArenaPool *arena;

    if (!data->len) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
        return SECFailure;
    }

    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    if (!arena) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    xtnData->certReqAuthorities.arena = arena;
    rv = ssl3_ParseCertificateRequestCAs((sslSocket *)ss,
                                         &data->data, &data->len,
                                         &xtnData->certReqAuthorities);
    if (rv != SECSuccess) {
        goto loser;
    }
    if (data->len) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CERT_REQUEST);
        goto loser;
    }
    return SECSuccess;

loser:
    PORT_FreeArena(arena, PR_FALSE);
    xtnData->certReqAuthorities.arena = NULL;
    return SECFailure;
}

SECStatus
tls13_ServerHandleCertAuthoritiesXtn(const sslSocket *ss, TLSExtensionData *xtnData, SECItem *data)
{
    SSL_TRC(3, ("%d: TLS13[%d]: ignore certificate_authorities extension",
                SSL_GETPID(), ss->fd));
    /* NSS ignores certificate_authorities in the ClientHello */
    return SECSuccess;
}

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

    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);

    if (!xtnData->selectedGroup) {
        return SECSuccess;
    }

    rv = sslBuffer_AppendNumber(buf, xtnData->selectedGroup->name, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

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

    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
    PORT_Assert(xtnData->cookie.len > 0);

    rv = sslBuffer_AppendVariable(buf,
                                  xtnData->cookie.data, xtnData->cookie.len, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ClientHandleHrrEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                            SECItem *data)
{
    if (data->len != TLS13_ECH_SIGNAL_LEN) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
        return SECFailure;
    }
    if (!ssl3_ExtensionAdvertised(ss, ssl_tls13_encrypted_client_hello_xtn)) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        return SECFailure;
    }
    if (!ss->ssl3.hs.echHpkeCtx) {
        SSL_TRC(50, ("%d: TLS13[%d]: client received GREASEd ECH confirmation",
                     SSL_GETPID(), ss->fd));
        return SECSuccess;
    }
    SSL_TRC(50, ("%d: TLS13[%d]: client received HRR ECH confirmation",
                 SSL_GETPID(), ss->fd));
    PORT_Assert(!xtnData->ech);
    xtnData->ech = PORT_ZNew(sslEchXtnState);
    if (!xtnData->ech) {
        return SECFailure;
    }
    xtnData->ech->hrrConfirmation = data->data;
    return SECSuccess;
}

SECStatus
tls13_ClientHandleEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                         SECItem *data)
{
    SECStatus rv;
    PRCList parsedConfigs;
    PR_INIT_CLIST(&parsedConfigs);

    /* The [retry config] response is valid only when the server used the
     * ClientHelloOuter. If the server sent this extension in response to the
     * inner variant [ECH was accepted], then the client MUST abort with an
     * "unsupported_extension" alert [draft-ietf-tls-esni-14, Section 5]. */

    if (ss->ssl3.hs.echAccepted) {
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
        return SECFailure;
    }

    /* If the server is configured with any ECHConfigs, it MUST include the
     * "encrypted_client_hello" extension in its EncryptedExtensions with the
     * "retry_configs" field set to one or more ECHConfig structures with
     * up-to-date keys [draft-ietf-tls-esni-14, Section 7.1]. */

    if (ss->ssl3.hs.msg_type != ssl_hs_encrypted_extensions) {
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
            /* For TLS < 1.3 the extension is unkown/unsupported. */
            ssl3_ExtSendAlert(ss, alert_fatal, unsupported_extension);
        } else {
            /* For TLS 1.3 the extension is known but prohibited outside EE
             * (see RFC8446, Section 4.2 for alert rationale). */

            ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        }
        return SECFailure;
    }

    PORT_Assert(!xtnData->ech);
    xtnData->ech = PORT_ZNew(sslEchXtnState);
    if (!xtnData->ech) {
        return SECFailure;
    }

    /* Parse the list to determine 1) That the configs are valid
     * and properly encoded, and 2) If any are compatible. */

    rv = tls13_DecodeEchConfigs(data, &parsedConfigs);
    if (rv == SECFailure) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_CONFIG);
        return SECFailure;
    }
    /* Don't mark ECH negotiated on rejection with retry_config.
     * Save the the raw configs so the application can retry. If
     * we sent GREASE ECH (no echHpkeCtx), don't apply retry_configs. */

    if (ss->ssl3.hs.echHpkeCtx && !PR_CLIST_IS_EMPTY(&parsedConfigs)) {
        rv = SECITEM_CopyItem(NULL, &xtnData->ech->retryConfigs, data);
    }
    tls13_DestroyEchConfigs(&parsedConfigs);

    return rv;
}

/* Indicates support for the delegated credentials extension. This should be
 * hooked while processing the ClientHello. */

SECStatus
tls13_ClientSendDelegatedCredentialsXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
                                        sslBuffer *buf, PRBool *added)
{
    /* Only send the extension if support is enabled and the client can
     * negotiate TLS 1.3. */

    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3 ||
        !ss->opt.enableDelegatedCredentials) {
        return SECSuccess;
    }

    /* Filter the schemes that are enabled and acceptable. Save these in
     * the "advertised" list, then encode them to be sent. If we receive
     * a DC in response, validate that it matches one of the advertised
     * schemes. */

    SSLSignatureScheme filtered[MAX_SIGNATURE_SCHEMES] = { 0 };
    unsigned int filteredCount = 0;
    SECStatus rv = ssl3_FilterSigAlgs(ss, ss->vrange.max,
                                      PR_TRUE /* disableRsae */,
                                      PR_FALSE /* forCert */,
                                      MAX_SIGNATURE_SCHEMES,
                                      filtered,
                                      &filteredCount);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    /* If no schemes available for the DC extension, don't send it. */
    if (!filteredCount) {
        return SECSuccess;
    }

    rv = ssl3_EncodeFilteredSigAlgs(ss, filtered, filteredCount,
                                    PR_FALSE /* GREASE */, buf);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    SSLSignatureScheme *dcSchemesAdvertised = PORT_ZNewArray(SSLSignatureScheme,
                                                             filteredCount);
    if (!dcSchemesAdvertised) {
        return SECFailure;
    }
    for (unsigned int i = 0; i < filteredCount; i++) {
        dcSchemesAdvertised[i] = filtered[i];
    }

    if (xtnData->delegCredSigSchemesAdvertised) {
        PORT_Free(xtnData->delegCredSigSchemesAdvertised);
    }
    xtnData->delegCredSigSchemesAdvertised = dcSchemesAdvertised;
    xtnData->numDelegCredSigSchemesAdvertised = filteredCount;
    *added = PR_TRUE;
    return SECSuccess;
}

/* Parses the delegated credential (DC) offered by the server. This should be
 * hooked while processing the server's CertificateVerify.
 *
 * Only the DC sent with the end-entity certificate is to be parsed. This is
 * ensured by |tls13_HandleCertificateEntry|, which only processes extensions
 * for the first certificate in the chain.
 */

SECStatus
tls13_ClientHandleDelegatedCredentialsXtn(const sslSocket *ss,
                                          TLSExtensionData *xtnData,
                                          SECItem *data)
{
    if (!ss->opt.enableDelegatedCredentials ||
        ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        return SECFailure;
    }

    sslDelegatedCredential *dc = NULL;
    SECStatus rv = tls13_ReadDelegatedCredential(data->data, data->len, &dc);
    if (rv != SECSuccess) {
        goto loser; /* code already set */
    }

    /* When using RSA, the public key MUST NOT use the rsaEncryption OID. */
    if (dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha256 ||
        dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha384 ||
        dc->expectedCertVerifyAlg == ssl_sig_rsa_pss_rsae_sha512) {
        goto alert_loser;
    }

    /* The algorithm and expected_cert_verify_algorithm fields MUST be of a
     * type advertised by the client in the SignatureSchemeList and are
     * considered invalid otherwise.  Clients that receive invalid delegated
     * credentials MUST terminate the connection with an "illegal_parameter"
     * alert. */

    PRBool found = PR_FALSE;
    for (unsigned int i = 0; i < ss->xtnData.numDelegCredSigSchemesAdvertised; ++i) {
        if (dc->expectedCertVerifyAlg == ss->xtnData.delegCredSigSchemesAdvertised[i]) {
            found = PR_TRUE;
            break;
        }
    }
    if (found == PR_FALSE) {
        goto alert_loser;
    }

    // Check the dc->alg, if necessary.
    if (dc->alg != dc->expectedCertVerifyAlg) {
        found = PR_FALSE;
        for (unsigned int i = 0; i < ss->xtnData.numDelegCredSigSchemesAdvertised; ++i) {
            if (dc->alg == ss->xtnData.delegCredSigSchemesAdvertised[i]) {
                found = PR_TRUE;
                break;
            }
        }
        if (found == PR_FALSE) {
            goto alert_loser;
        }
    }

    xtnData->peerDelegCred = dc;
    xtnData->negotiated[xtnData->numNegotiated++] =
        ssl_delegated_credentials_xtn;
    return SECSuccess;
alert_loser:
    ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
    PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
loser:
    tls13_DestroyDelegatedCredential(dc);
    return SECFailure;
}

/* Adds the DC extension if we're committed to authenticating with a DC. */
static SECStatus
tls13_ServerSendDelegatedCredentialsXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
                                        sslBuffer *buf, PRBool *added)
{
    if (tls13_IsSigningWithDelegatedCredential(ss)) {
        const SECItem *dc = &ss->sec.serverCert->delegCred;
        SECStatus rv;
        rv = sslBuffer_Append(buf, dc->data, dc->len);
        if (rv != SECSuccess) {
            return SECFailure;
        }
        *added = PR_TRUE;
    }
    return SECSuccess;
}

/* The client has indicated support of DCs. We can't act on this information
 * until we've committed to signing with a DC, so just set a callback for
 * sending the DC extension later. */

SECStatus
tls13_ServerHandleDelegatedCredentialsXtn(const sslSocket *ss,
                                          TLSExtensionData *xtnData,
                                          SECItem *data)
{
    if (xtnData->delegCredSigSchemes) {
        PORT_Free(xtnData->delegCredSigSchemes);
        xtnData->delegCredSigSchemes = NULL;
        xtnData->numDelegCredSigSchemes = 0;
    }
    SECStatus rv = ssl_ParseSignatureSchemes(ss, NULL,
                                             &xtnData->delegCredSigSchemes,
                                             &xtnData->numDelegCredSigSchemes,
                                             &data->data, &data->len);
    if (rv != SECSuccess) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }
    if (xtnData->numDelegCredSigSchemes == 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, handshake_failure);
        PORT_SetError(SSL_ERROR_UNSUPPORTED_SIGNATURE_ALGORITHM);
        return SECFailure;
    }
    /* Check for trailing data. */
    if (data->len != 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->peerRequestedDelegCred = PR_TRUE;
    xtnData->negotiated[xtnData->numNegotiated++] =
        ssl_delegated_credentials_xtn;

    return ssl3_RegisterExtensionSender(
        ss, xtnData, ssl_delegated_credentials_xtn,
        tls13_ServerSendDelegatedCredentialsXtn);
}

/* Adds the ECH extension containing server retry_configs */
SECStatus
tls13_ServerSendEchXtn(const sslSocket *ss,
                       TLSExtensionData *xtnData,
                       sslBuffer *buf, PRBool *added)
{
    SECStatus rv;
    PORT_Assert(ss->version >= SSL_LIBRARY_VERSION_TLS_1_3);
    if (PR_CLIST_IS_EMPTY(&ss->echConfigs)) {
        return SECSuccess;
    }

    const sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
    rv = sslBuffer_AppendVariable(buf, cfg->raw.data, cfg->raw.len, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/* If an ECH server sends the HRR ECH extension after it accepted ECH, the
 * extension's payload must be set to 8 zero bytes, these are overwritten with
 * the accept_confirmation value after the required transcript calculation.
 * If a client-facing/shared-mode server did not accept ECH when offered in CH
 * or if ECH GREASE is enabled on the server and a ECH extension was received,
 * a 8 byte random value is set as the extension's payload
 * [draft-ietf-tls-esni-14, Section 7].
 *
 * Depending on the acceptance of ECH, zero or random bytes are written to
 * ss->ssl3.hs.greaseEchBuf.buf in tls13con.c/tls13_SendHelloRetryRequest(). */

SECStatus
tls13_ServerSendHrrEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                          sslBuffer *buf, PRBool *added)
{
    SECStatus rv;
    /* Do not send HRR ECH extension if TLS < 1.3 was negotiated OR no ECH
     * extension was received OR the server is NOT in any ECH server mode AND
     * ECH GREASE is NOT enabled. */

    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 ||
        !xtnData->ech ||
        (!ss->echPubKey && !ss->opt.enableTls13BackendEch && !ss->opt.enableTls13GreaseEch)) {
        SSL_TRC(100, ("%d: TLS13[%d]: server not sending HRR ECH Xtn",
                      SSL_GETPID(), ss->fd));
        return SECSuccess;
    }
    SSL_TRC(100, ("%d: TLS13[%d]: server sending HRR ECH Xtn",
                  SSL_GETPID(), ss->fd));
    PR_ASSERT(SSL_BUFFER_LEN(&ss->ssl3.hs.greaseEchBuf) == TLS13_ECH_SIGNAL_LEN);
    PRINT_BUF(100, (ss, "grease_ech_confirmation", ss->ssl3.hs.greaseEchBuf.buf, TLS13_ECH_SIGNAL_LEN));
    rv = sslBuffer_AppendBuffer(buf, &ss->ssl3.hs.greaseEchBuf);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_ServerHandleInnerEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                              SECItem *data)
{
    PRUint64 xtn_type;
    sslReader xtnReader = SSL_READER(data->data, data->len);

    PR_ASSERT(ss->ssl3.hs.echAccepted || ss->opt.enableTls13BackendEch);
    PR_ASSERT(!xtnData->ech->receivedInnerXtn);

    SECStatus rv = sslRead_ReadNumber(&xtnReader, 1, &xtn_type);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    if (xtn_type != ech_xtn_type_inner) {
        goto alert_loser;
    }
    if (SSL_READER_REMAINING(&xtnReader)) {
        /* Inner ECH Extension must contain only type enum */
        goto alert_loser;
    }

    xtnData->ech->receivedInnerXtn = PR_TRUE;
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_tls13_encrypted_client_hello_xtn;
    return SECSuccess;

alert_loser:
    ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
    PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
    return SECFailure;
}

SECStatus
tls13_ServerHandleOuterEchXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                              SECItem *data)
{
    SECStatus rv;
    HpkeKdfId kdf;
    HpkeAeadId aead;
    PRUint32 tmp;
    PRUint8 configId;
    SECItem senderPubKey;
    SECItem encryptedCh;

    PRUint32 xtn_type;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &xtn_type, 1, &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    if (xtn_type != ech_xtn_type_outer && xtn_type != ech_xtn_type_inner) {
        SSL_TRC(3, ("%d: TLS13[%d]: unexpected ECH extension type in client hello outer, alert",
                    SSL_GETPID(), ss->fd));
        goto alert_loser;
    }
    /* If we are operating in shared mode, we can accept an inner xtn in the ClientHelloOuter */
    if (xtn_type == ech_xtn_type_inner) {
        if (!ss->opt.enableTls13BackendEch) {
            ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
            PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
            return SECFailure;
        }
        PORT_Assert(!xtnData->ech);
        xtnData->ech = PORT_ZNew(sslEchXtnState);
        if (!xtnData->ech) {
            return SECFailure;
        }
        /* We have to rewind the buffer advanced by ssl3_ExtConsumeHandshakeNumber */
        data->data--;
        data->len++;
        return tls13_ServerHandleInnerEchXtn(ss, xtnData, data);
    }
    if (ss->ssl3.hs.echAccepted) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_UNEXPECTED_EXTENSION);
        return SECFailure;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: handle outer ECH extension",
                SSL_GETPID(), ss->fd));

    PORT_Assert(!xtnData->ech);
    xtnData->ech = PORT_ZNew(sslEchXtnState);
    if (!xtnData->ech) {
        return SECFailure;
    }

    /* Parse the KDF and AEAD. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2,
                                        &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    kdf = (HpkeKdfId)tmp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 2,
                                        &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    aead = (HpkeAeadId)tmp;

    /* config_id */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &tmp, 1,
                                        &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    configId = tmp;

    /* enc */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &senderPubKey, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }

    /* payload, which must be final and non-empty. */
    xtnData->ech->payloadStart = data->data + 2; /* Move past length */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &encryptedCh, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }
    if (data->len || !encryptedCh.len) {
        goto alert_loser;
    }

    if (!ss->ssl3.hs.helloRetry) {
        /* In the real ECH HRR case, config_id and enc should be empty. This
         * is checked after acceptance, because it might be GREASE ECH. */

        if (!senderPubKey.len) {
            goto alert_loser;
        }

        rv = SECITEM_CopyItem(NULL, &xtnData->ech->senderPubKey, &senderPubKey);
        if (rv == SECFailure) {
            return SECFailure;
        }
    }

    rv = SECITEM_CopyItem(NULL, &xtnData->ech->innerCh, &encryptedCh);
    PRINT_BUF(100, (ss, "CT for ECH Decryption", encryptedCh.data, encryptedCh.len));
    if (rv == SECFailure) {
        return SECFailure;
    }
    xtnData->ech->configId = configId;
    xtnData->ech->kdfId = kdf;
    xtnData->ech->aeadId = aead;

    /* Not negotiated until tls13_MaybeAcceptEch. */
    return SECSuccess;

alert_loser:
    ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
    PORT_SetError(SSL_ERROR_RX_MALFORMED_ECH_EXTENSION);
    return SECFailure;
}

SECStatus
tls13_SendEmptyGreaseXtn(const sslSocket *ss,
                         TLSExtensionData *xtnData,
                         sslBuffer *buf, PRBool *added)
{
    if (!ss->opt.enableGrease ||
        (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
        (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
        return SECSuccess;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
tls13_SendGreaseXtn(const sslSocket *ss,
                    TLSExtensionData *xtnData,
                    sslBuffer *buf, PRBool *added)
{
    if (!ss->opt.enableGrease ||
        (!ss->sec.isServer && ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_3) ||
        (ss->sec.isServer && ss->version < SSL_LIBRARY_VERSION_TLS_1_3)) {
        return SECSuccess;
    }

    SECStatus rv = sslBuffer_AppendVariable(buf, (PRUint8[]){ 0x00 }, 1, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_SendCertificateCompressionXtn(const sslSocket *ss,
                                   TLSExtensionData *xtnData,
                                   sslBuffer *buf, PRBool *added)
{
    /* enum {
     *  zlib(1),
     *  brotli(2),
     *  zstd(3),
     *  (65535)
     * } CertificateCompressionAlgorithm;
     *
     * struct {
     *      CertificateCompressionAlgorithm algorithms<2..2^8-2>;
     *  } CertificateCompressionAlgorithms;
     */


    SECStatus rv = SECFailure;
    if (ss->ssl3.cwSpec->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        SSL_TRC(50, ("%d: TLS13[%d]: certificate_compression_algorithm extension requires TLS1.3 and above",
                     SSL_GETPID(), ss->fd));
        return SECSuccess;
    }

    size_t certificateCompressionAlgorithmsLen = ss->ssl3.supportedCertCompressionAlgorithmsCount;
    if (certificateCompressionAlgorithmsLen == 0) {
        SSL_TRC(30, ("%d: TLS13[%d]: %s does not support any certificate compression algorithm",
                     SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
        return SECSuccess;
    }

    SSL_TRC(30, ("%d: TLS13[%d]: %s sends certificate_compression_algorithm extension",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss)));
    PORT_Assert(certificateCompressionAlgorithmsLen < (0x1u << 8) - 1);

    rv = sslBuffer_AppendNumber(buf, certificateCompressionAlgorithmsLen << 1, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    for (size_t i = 0; i < certificateCompressionAlgorithmsLen; i++) {
        rv = sslBuffer_AppendNumber(buf, ss->ssl3.supportedCertCompressionAlgorithms[i].id, 2);
        if (rv != SECSuccess) {
            return SECFailure;
        }
    }

    xtnData->certificateCompressionAdvertised = PR_TRUE;
    *added = PR_TRUE;
    return SECSuccess;
}

const char *
ssl3_mapCertificateCompressionAlgorithmToName(const sslSocket *ss, SSLCertificateCompressionAlgorithmID alg)
{
    for (int i = 0; i < ss->ssl3.supportedCertCompressionAlgorithmsCount; i++) {
        if (ss->ssl3.supportedCertCompressionAlgorithms[i].id == alg) {
            return ss->ssl3.supportedCertCompressionAlgorithms[i].name;
        }
    }
    return "unknown";
}

SECStatus
ssl3_HandleCertificateCompressionXtn(const sslSocket *ss,
                                     TLSExtensionData *xtnData,
                                     SECItem *data)
{
    /* This extension is only supported with TLS 1.3 [RFC8446] and newer;
     * if TLS 1.2 [RFC5246] or earlier is negotiated, the peers MUST ignore this extension.
     */

    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3) {
        SSL_TRC(50, ("%d: TLS13[%d]: ignore certificate_compression extension",
                     SSL_GETPID(), ss->fd));
        return SECSuccess;
    }

    SECStatus rv = SECFailure;
    PRUint32 lengthSupportedAlgorithms = 0;
    PRUint32 certComprAlgId = 0;

    SSL_TRC(30, ("%d: TLS13[%d]: %s handles certificate_compression_algorithm extension",
                 SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &lengthSupportedAlgorithms, 1, &data->data, &data->len);
    if (rv != SECSuccess) {
        goto alert_loser;
    }

    /* Each of the algorithm is 2 bytes. */
    if (lengthSupportedAlgorithms % 2 != 0) {
        goto alert_loser;
    }

    if (data->len != lengthSupportedAlgorithms) {
        goto alert_loser;
    }

    SECStatus algFound = SECFailure;

    /* We use the first common algorithm we found. */
    for (int i = 0; i < lengthSupportedAlgorithms / 2; i++) {
        rv = ssl3_ExtConsumeHandshakeNumber(ss, &certComprAlgId, 2, &data->data, &data->len);
        if (rv != SECSuccess) {
            goto alert_loser;
        }

        SSLCertificateCompressionAlgorithmID alg = (SSLCertificateCompressionAlgorithmID)certComprAlgId;
        if (alg == 0) {
            SSL_TRC(50, ("%d: TLS13[%d]: certificate compression ignores reserved algorithm %02x",
                         SSL_GETPID(), ss->fd, alg));
            continue;
        }

        for (int j = 0; j < ss->ssl3.supportedCertCompressionAlgorithmsCount; j++) {
            if (ss->ssl3.supportedCertCompressionAlgorithms[j].id == alg) {
                xtnData->compressionAlg = alg;
                xtnData->negotiated[xtnData->numNegotiated++] = ssl_certificate_compression_xtn;
                algFound = SECSuccess;
                break;
            }
        }

        if (algFound == SECSuccess) {
            break;
        }
    }

    if (algFound == SECSuccess) {
        SSL_TRC(30, ("%d: TLS13[%d]: %s established certificate compression algorithm %s",
                     SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                     ssl3_mapCertificateCompressionAlgorithmToName(ss, xtnData->compressionAlg)));
    } else {
        SSL_TRC(30, ("%d: TLS13[%d]: no common certificate compression algorithms found on the %s side",
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=93 H=85 G=88

¤ Dauer der Verarbeitung: 0.25 Sekunden  (vorverarbeitet)  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.