Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  tls13con.c   Sprache: C

 
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * TLS 1.3 Protocol
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */


#include "sslt.h"
#include "stdarg.h"
#include "cert.h"
#include "ssl.h"
#include "keyhi.h"
#include "pk11func.h"
#include "prerr.h"
#include "secitem.h"
#include "secmod.h"
#include "sslimpl.h"
#include "sslproto.h"
#include "sslerr.h"
#include "ssl3exthandle.h"
#include "tls13hkdf.h"
#include "tls13con.h"
#include "tls13err.h"
#include "tls13ech.h"
#include "tls13exthandle.h"
#include "tls13hashstate.h"
#include "tls13subcerts.h"
#include "tls13psk.h"

static SECStatus tls13_SetCipherSpec(sslSocket *ss, PRUint16 epoch,
                                     SSLSecretDirection install,
                                     PRBool deleteSecret);
static SECStatus tls13_SendServerHelloSequence(sslSocket *ss);
static SECStatus tls13_SendEncryptedExtensions(sslSocket *ss);
static void tls13_SetKeyExchangeType(sslSocket *ss, const sslNamedGroupDef *group);
static SECStatus tls13_HandleClientKeyShare(sslSocket *ss,
                                            TLS13KeyShareEntry *peerShare);
static SECStatus tls13_SendHelloRetryRequest(
    sslSocket *ss, const sslNamedGroupDef *selectedGroup,
    const PRUint8 *token, unsigned int tokenLen);

static SECStatus tls13_HandleServerKeyShare(sslSocket *ss);
static SECStatus tls13_HandleEncryptedExtensions(sslSocket *ss, PRUint8 *b,
                                                 PRUint32 length);
static SECStatus tls13_SendCertificate(sslSocket *ss);
static SECStatus tls13_HandleCertificateDecode(
    sslSocket *ss, PRUint8 *b, PRUint32 length);
static SECStatus tls13_HandleCertificate(
    sslSocket *ss, PRUint8 *b, PRUint32 length, PRBool alreadyHashed);
static SECStatus tls13_ReinjectHandshakeTranscript(sslSocket *ss);
static SECStatus tls13_SendCertificateRequest(sslSocket *ss);
static SECStatus tls13_HandleCertificateRequest(sslSocket *ss, PRUint8 *b,
                                                PRUint32 length);
static SECStatus
tls13_SendCertificateVerify(sslSocket *ss, SECKEYPrivateKey *privKey);
static SECStatus tls13_HandleCertificateVerify(
    sslSocket *ss, PRUint8 *b, PRUint32 length);
static SECStatus tls13_RecoverWrappedSharedSecret(sslSocket *ss,
                                                  sslSessionID *sid);
static SECStatus
tls13_DeriveSecretWrap(sslSocket *ss, PK11SymKey *key,
                       const char *prefix,
                       const char *suffix,
                       const char *keylogLabel,
                       PK11SymKey **dest);
SECStatus
tls13_DeriveSecret(sslSocket *ss, PK11SymKey *key,
                   const char *label,
                   unsigned int labelLen,
                   const SSL3Hashes *hashes,
                   PK11SymKey **dest,
                   SSLHashType hash);
static SECStatus tls13_SendEndOfEarlyData(sslSocket *ss);
static SECStatus tls13_HandleEndOfEarlyData(sslSocket *ss, const PRUint8 *b,
                                            PRUint32 length);
static SECStatus tls13_MaybeHandleSuppressedEndOfEarlyData(sslSocket *ss);
static SECStatus tls13_SendFinished(sslSocket *ss, PK11SymKey *baseKey);
static SECStatus tls13_ComputePskBinderHash(sslSocket *ss, PRUint8 *b, size_t length,
                                            SSL3Hashes *hashes, SSLHashType type);
static SECStatus tls13_VerifyFinished(sslSocket *ss, SSLHandshakeType message,
                                      PK11SymKey *secret,
                                      PRUint8 *b, PRUint32 length,
                                      const SSL3Hashes *hashes);
static SECStatus tls13_ClientHandleFinished(sslSocket *ss,
                                            PRUint8 *b, PRUint32 length);
static SECStatus tls13_ServerHandleFinished(sslSocket *ss,
                                            PRUint8 *b, PRUint32 length);
static SECStatus tls13_SendNewSessionTicket(sslSocket *ss,
                                            const PRUint8 *appToken,
                                            unsigned int appTokenLen);
static SECStatus tls13_HandleNewSessionTicket(sslSocket *ss, PRUint8 *b,
                                              PRUint32 length);
static SECStatus tls13_ComputeEarlySecretsWithPsk(sslSocket *ss);
static SECStatus tls13_ComputeHandshakeSecrets(sslSocket *ss);
static SECStatus tls13_ComputeApplicationSecrets(sslSocket *ss);
static SECStatus tls13_ComputeFinalSecrets(sslSocket *ss);
static SECStatus tls13_ComputeFinished(
    sslSocket *ss, PK11SymKey *baseKey, SSLHashType hashType,
    const SSL3Hashes *hashes, PRBool sending, PRUint8 *output,
    unsigned int *outputLen, unsigned int maxOutputLen);
static SECStatus tls13_SendClientSecondRound(sslSocket *ss);
static SECStatus tls13_SendClientSecondFlight(sslSocket *ss);
static SECStatus tls13_FinishHandshake(sslSocket *ss);

const char kHkdfLabelClient[] = "c";
const char kHkdfLabelServer[] = "s";
const char kHkdfLabelDerivedSecret[] = "derived";
const char kHkdfLabelResPskBinderKey[] = "res binder";
const char kHkdfLabelExtPskBinderKey[] = "ext binder";
const char kHkdfLabelEarlyTrafficSecret[] = "e traffic";
const char kHkdfLabelEarlyExporterSecret[] = "e exp master";
const char kHkdfLabelHandshakeTrafficSecret[] = "hs traffic";
const char kHkdfLabelApplicationTrafficSecret[] = "ap traffic";
const char kHkdfLabelFinishedSecret[] = "finished";
const char kHkdfLabelResumptionMasterSecret[] = "res master";
const char kHkdfLabelExporterMasterSecret[] = "exp master";
const char kHkdfLabelResumption[] = "resumption";
const char kHkdfLabelTrafficUpdate[] = "traffic upd";
const char kHkdfPurposeKey[] = "key";
const char kHkdfPurposeSn[] = "sn";
const char kHkdfPurposeIv[] = "iv";

const char keylogLabelClientEarlyTrafficSecret[] = "CLIENT_EARLY_TRAFFIC_SECRET";
const char keylogLabelClientHsTrafficSecret[] = "CLIENT_HANDSHAKE_TRAFFIC_SECRET";
const char keylogLabelServerHsTrafficSecret[] = "SERVER_HANDSHAKE_TRAFFIC_SECRET";
const char keylogLabelClientTrafficSecret[] = "CLIENT_TRAFFIC_SECRET_0";
const char keylogLabelServerTrafficSecret[] = "SERVER_TRAFFIC_SECRET_0";
const char keylogLabelEarlyExporterSecret[] = "EARLY_EXPORTER_SECRET";
const char keylogLabelExporterSecret[] = "EXPORTER_SECRET";

/* Belt and suspenders in case we ever add a TLS 1.4. */
PR_STATIC_ASSERT(SSL_LIBRARY_VERSION_MAX_SUPPORTED <=
                 SSL_LIBRARY_VERSION_TLS_1_3);

void
tls13_FatalError(sslSocket *ss, PRErrorCode prError, SSL3AlertDescription desc)
{
    PORT_Assert(desc != internal_error); /* These should never happen */
    (void)SSL3_SendAlert(ss, alert_fatal, desc);
    PORT_SetError(prError);
}

#ifdef TRACE
#define STATE_CASE(a) \
    case a:           \
        return #a
static char *
tls13_HandshakeState(SSL3WaitState st)
{
    switch (st) {
        STATE_CASE(idle_handshake);
        STATE_CASE(wait_client_hello);
        STATE_CASE(wait_end_of_early_data);
        STATE_CASE(wait_client_cert);
        STATE_CASE(wait_client_key);
        STATE_CASE(wait_cert_verify);
        STATE_CASE(wait_change_cipher);
        STATE_CASE(wait_finished);
        STATE_CASE(wait_server_hello);
        STATE_CASE(wait_certificate_status);
        STATE_CASE(wait_server_cert);
        STATE_CASE(wait_server_key);
        STATE_CASE(wait_cert_request);
        STATE_CASE(wait_hello_done);
        STATE_CASE(wait_new_session_ticket);
        STATE_CASE(wait_encrypted_extensions);
        default:
            break;
    }
    PORT_Assert(0);
    return "unknown";
}
#endif

#define TLS13_WAIT_STATE_MASK 0x80

#define TLS13_BASE_WAIT_STATE(ws) (ws & ~TLS13_WAIT_STATE_MASK)
/* We don't mask idle_handshake because other parts of the code use it*/
#define TLS13_WAIT_STATE(ws) (((ws == idle_handshake) || (ws == wait_server_hello)) ? ws : ws | TLS13_WAIT_STATE_MASK)
#define TLS13_CHECK_HS_STATE(ss, err, ...)                          \
    tls13_CheckHsState(ss, err, #err, __func__, __FILE__, __LINE__, \
                       __VA_ARGS__,                                 \
                       wait_invalid)
void
tls13_SetHsState(sslSocket *ss, SSL3WaitState ws,
                 const char *func, const char *file, int line)
{
#ifdef TRACE
    const char *new_state_name =
        tls13_HandshakeState(ws);

    SSL_TRC(3, ("%d: TLS13[%d]: %s state change from %s->%s in %s (%s:%d)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)),
                new_state_name,
                func, file, line));
#endif

    ss->ssl3.hs.ws = TLS13_WAIT_STATE(ws);
}

static PRBool
tls13_InHsStateV(sslSocket *ss, va_list ap)
{
    SSL3WaitState ws;

    while ((ws = va_arg(ap, SSL3WaitState)) != wait_invalid) {
        if (TLS13_WAIT_STATE(ws) == ss->ssl3.hs.ws) {
            return PR_TRUE;
        }
    }
    return PR_FALSE;
}

PRBool
tls13_InHsState(sslSocket *ss, ...)
{
    PRBool found;
    va_list ap;

    va_start(ap, ss);
    found = tls13_InHsStateV(ss, ap);
    va_end(ap);

    return found;
}

static SECStatus
tls13_CheckHsState(sslSocket *ss, int err, const char *error_name,
                   const char *func, const char *file, int line,
                   ...)
{
    va_list ap;
    va_start(ap, line);
    if (tls13_InHsStateV(ss, ap)) {
        va_end(ap);
        return SECSuccess;
    }
    va_end(ap);

    SSL_TRC(3, ("%d: TLS13[%d]: error %s state is (%s) at %s (%s:%d)",
                SSL_GETPID(), ss->fd,
                error_name,
                tls13_HandshakeState(TLS13_BASE_WAIT_STATE(ss->ssl3.hs.ws)),
                func, file, line));
    tls13_FatalError(ss, err, unexpected_message);
    return SECFailure;
}

PRBool
tls13_IsPostHandshake(const sslSocket *ss)
{
    return ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 && ss->firstHsDone;
}

SSLHashType
tls13_GetHashForCipherSuite(ssl3CipherSuite suite)
{
    const ssl3CipherSuiteDef *cipherDef =
        ssl_LookupCipherSuiteDef(suite);
    PORT_Assert(cipherDef);
    if (!cipherDef) {
        return ssl_hash_none;
    }
    return cipherDef->prf_hash;
}

SSLHashType
tls13_GetHash(const sslSocket *ss)
{
    /* suite_def may not be set yet when doing EPSK 0-Rtt. */
    if (!ss->ssl3.hs.suite_def) {
        if (ss->xtnData.selectedPsk) {
            return ss->xtnData.selectedPsk->hash;
        }
        /* This should never happen. */
        PORT_Assert(0);
        return ssl_hash_none;
    }

    /* All TLS 1.3 cipher suites must have an explict PRF hash. */
    PORT_Assert(ss->ssl3.hs.suite_def->prf_hash != ssl_hash_none);
    return ss->ssl3.hs.suite_def->prf_hash;
}

SECStatus
tls13_GetHashAndCipher(PRUint16 version, PRUint16 cipherSuite,
                       SSLHashType *hash, const ssl3BulkCipherDef **cipher)
{
    if (version < SSL_LIBRARY_VERSION_TLS_1_3) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    // Lookup and check the suite.
    SSLVersionRange vrange = { version, version };
    if (!ssl3_CipherSuiteAllowedForVersionRange(cipherSuite, &vrange)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    const ssl3CipherSuiteDef *suiteDef = ssl_LookupCipherSuiteDef(cipherSuite);
    const ssl3BulkCipherDef *cipherDef = ssl_GetBulkCipherDef(suiteDef);
    if (cipherDef->type != type_aead) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }
    *hash = suiteDef->prf_hash;
    if (cipher != NULL) {
        *cipher = cipherDef;
    }
    return SECSuccess;
}

unsigned int
tls13_GetHashSizeForHash(SSLHashType hash)
{
    switch (hash) {
        case ssl_hash_sha256:
            return 32;
        case ssl_hash_sha384:
            return 48;
        default:
            PORT_Assert(0);
    }
    return 32;
}

unsigned int
tls13_GetHashSize(const sslSocket *ss)
{
    return tls13_GetHashSizeForHash(tls13_GetHash(ss));
}

static CK_MECHANISM_TYPE
tls13_GetHmacMechanismFromHash(SSLHashType hashType)
{
    switch (hashType) {
        case ssl_hash_sha256:
            return CKM_SHA256_HMAC;
        case ssl_hash_sha384:
            return CKM_SHA384_HMAC;
        default:
            PORT_Assert(0);
    }
    return CKM_SHA256_HMAC;
}

static CK_MECHANISM_TYPE
tls13_GetHmacMechanism(const sslSocket *ss)
{
    return tls13_GetHmacMechanismFromHash(tls13_GetHash(ss));
}

SECStatus
tls13_ComputeHash(sslSocket *ss, SSL3Hashes *hashes,
                  const PRUint8 *buf, unsigned int len,
                  SSLHashType hash)
{
    SECStatus rv;

    rv = PK11_HashBuf(ssl3_HashTypeToOID(hash), hashes->u.raw, buf, len);
    if (rv != SECSuccess) {
        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
        return SECFailure;
    }
    hashes->len = tls13_GetHashSizeForHash(hash);

    return SECSuccess;
}

static SECStatus
tls13_CreateKEMKeyPair(sslSocket *ss, const sslNamedGroupDef *groupDef,
                       sslKeyPair **outKeyPair)
{
    PORT_Assert(groupDef);

    sslKeyPair *keyPair = NULL;
    SECKEYPrivateKey *privKey = NULL;
    SECKEYPublicKey *pubKey = NULL;
    CK_MECHANISM_TYPE mechanism;
    CK_NSS_KEM_PARAMETER_SET_TYPE paramSet;

    switch (groupDef->name) {
        case ssl_grp_kem_xyber768d00:
            mechanism = CKM_NSS_KYBER_KEY_PAIR_GEN;
            paramSet = CKP_NSS_KYBER_768_ROUND3;
            break;
        case ssl_grp_kem_mlkem768x25519:
            mechanism = CKM_NSS_ML_KEM_KEY_PAIR_GEN;
            paramSet = CKP_NSS_ML_KEM_768;
            break;
        default:
            PORT_Assert(0);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
    }

    PK11SlotInfo *slot = PK11_GetBestSlot(mechanism, ss->pkcs11PinArg);
    if (!slot) {
        goto loser;
    }

    privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism,
                                              ¶mSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC,
                                              CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg);

    if (!privKey) {
        privKey = PK11_GenerateKeyPairWithOpFlags(slot, mechanism,
                                                  ¶mSet, &pubKey, PK11_ATTR_SESSION | PK11_ATTR_SENSITIVE | PK11_ATTR_PRIVATE,
                                                  CKF_DERIVE, CKF_DERIVE, ss->pkcs11PinArg);
    }

    PK11_FreeSlot(slot);
    if (!privKey || !pubKey) {
        goto loser;
    }

    keyPair = ssl_NewKeyPair(privKey, pubKey);
    if (!keyPair) {
        goto loser;
    }

    SSL_TRC(50, ("%d: SSL[%d]: Create Kyber ephemeral key %d",
                 SSL_GETPID(), ss ? ss->fd : NULL, groupDef->name));
    PRINT_BUF(50, (ss, "Public Key", pubKey->u.kyber.publicValue.data,
                   pubKey->u.kyber.publicValue.len));
#ifdef TRACE
    if (ssl_trace >= 50) {
        SECItem d = { siBuffer, NULL, 0 };
        SECStatus rv = PK11_ReadRawAttribute(PK11_TypePrivKey, privKey, CKA_VALUE, &d);
        if (rv == SECSuccess) {
            PRINT_BUF(50, (ss, "Private Key", d.data, d.len));
            SECITEM_FreeItem(&d, PR_FALSE);
        } else {
            SSL_TRC(50, ("Error extracting private key"));
        }
    }
#endif

    *outKeyPair = keyPair;
    return SECSuccess;

loser:
    SECKEY_DestroyPrivateKey(privKey);
    SECKEY_DestroyPublicKey(pubKey);
    ssl_MapLowLevelError(SEC_ERROR_KEYGEN_FAIL);
    return SECFailure;
}

SECStatus
tls13_CreateKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef,
                     sslEphemeralKeyPair **outKeyPair)
{
    SECStatus rv;
    const ssl3DHParams *params;
    sslEphemeralKeyPair *keyPair = NULL;

    PORT_Assert(groupDef);
    switch (groupDef->keaType) {
        case ssl_kea_ecdh_hybrid:
            if (groupDef->name != ssl_grp_kem_xyber768d00 && groupDef->name != ssl_grp_kem_mlkem768x25519) {
                PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                return SECFailure;
            }
            const sslNamedGroupDef *x25519 = ssl_LookupNamedGroup(ssl_grp_ec_curve25519);
            sslEphemeralKeyPair *x25519Pair = ssl_LookupEphemeralKeyPair(ss, x25519);
            if (x25519Pair) {
                keyPair = ssl_CopyEphemeralKeyPair(x25519Pair);
            }
            if (!keyPair) {
                rv = ssl_CreateECDHEphemeralKeyPair(ss, x25519, &keyPair);
                if (rv != SECSuccess) {
                    return SECFailure;
                }
            }
            keyPair->group = groupDef;
            break;
        case ssl_kea_ecdh:
            if (groupDef->name == ssl_grp_ec_curve25519) {
                sslEphemeralKeyPair *hybridPair = ssl_LookupEphemeralKeyPair(ss, ssl_LookupNamedGroup(ssl_grp_kem_mlkem768x25519));
                if (!hybridPair) {
                    hybridPair = ssl_LookupEphemeralKeyPair(ss, ssl_LookupNamedGroup(ssl_grp_kem_xyber768d00));
                }
                if (hybridPair) {
                    // We could use ssl_CopyEphemeralKeyPair here, but we would need to free
                    // the KEM components. We should pull this out into a utility function when
                    // we refactor to support multiple hybrid mechanisms.
                    keyPair = PORT_ZNew(sslEphemeralKeyPair);
                    if (!keyPair) {
                        return SECFailure;
                    }
                    PR_INIT_CLIST(&keyPair->link);
                    keyPair->group = groupDef;
                    keyPair->keys = ssl_GetKeyPairRef(hybridPair->keys);
                }
            }
            if (!keyPair) {
                rv = ssl_CreateECDHEphemeralKeyPair(ss, groupDef, &keyPair);
                if (rv != SECSuccess) {
                    return SECFailure;
                }
            }
            break;
        case ssl_kea_dh:
            params = ssl_GetDHEParams(groupDef);
            PORT_Assert(params->name != ssl_grp_ffdhe_custom);
            rv = ssl_CreateDHEKeyPair(groupDef, params, &keyPair);
            if (rv != SECSuccess) {
                return SECFailure;
            }
            break;
        default:
            PORT_Assert(0);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
    }

    // If we're creating an ECDH + KEM hybrid share and we're the client, then
    // we still need to generate the KEM key pair. Otherwise we're done.
    if (groupDef->keaType == ssl_kea_ecdh_hybrid && !ss->sec.isServer) {
        rv = tls13_CreateKEMKeyPair(ss, groupDef, &keyPair->kemKeys);
        if (rv != SECSuccess) {
            ssl_FreeEphemeralKeyPair(keyPair);
            return SECFailure;
        }
    }

    *outKeyPair = keyPair;
    return SECSuccess;
}

SECStatus
tls13_AddKeyShare(sslSocket *ss, const sslNamedGroupDef *groupDef)
{
    sslEphemeralKeyPair *keyPair = NULL;
    SECStatus rv;

    rv = tls13_CreateKeyShare(ss, groupDef, &keyPair);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    PR_APPEND_LINK(&keyPair->link, &ss->ephemeralKeyPairs);
    return SECSuccess;
}

SECStatus
SSL_SendAdditionalKeyShares(PRFileDesc *fd, unsigned int count)
{
    sslSocket *ss = ssl_FindSocket(fd);
    if (!ss) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    ss->additionalShares = count;
    return SECSuccess;
}

/*
 * Generate shares for ECDHE and FFDHE.  This picks the first enabled group of
 * the requisite type and creates a share for that.
 *
 * Called from ssl3_SendClientHello.
 */

SECStatus
tls13_SetupClientHello(sslSocket *ss, sslClientHelloType chType)
{
    unsigned int i;
    SSL3Statistics *ssl3stats = SSL_GetStatistics();
    NewSessionTicket *session_ticket = NULL;
    sslSessionID *sid = ss->sec.ci.sid;
    unsigned int numShares = 0;
    SECStatus rv;

    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
    PORT_Assert(ss->opt.noLocks || ssl_HaveXmitBufLock(ss));

    rv = tls13_ClientSetupEch(ss, chType);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    /* Everything below here is only run on the first CH. */
    if (chType != client_hello_initial) {
        return SECSuccess;
    }

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

    /* Select the first enabled group.
     * TODO(ekr@rtfm.com): be smarter about offering the group
     * that the other side negotiated if we are resuming. */

    PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs));
    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
        if (!ss->namedGroupPreferences[i]) {
            continue;
        }
        rv = tls13_AddKeyShare(ss, ss->namedGroupPreferences[i]);
        if (rv != SECSuccess) {
            return SECFailure;
        }
        if (++numShares > ss->additionalShares) {
            break;
        }
    }

    if (PR_CLIST_IS_EMPTY(&ss->ephemeralKeyPairs)) {
        PORT_SetError(SSL_ERROR_NO_CIPHERS_SUPPORTED);
        return SECFailure;
    }

    /* Try to do stateless resumption, if we can. */
    if (sid->cached != never_cached &&
        sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
        /* The caller must be holding sid->u.ssl3.lock for reading. */
        session_ticket = &sid->u.ssl3.locked.sessionTicket;
        PORT_Assert(session_ticket && session_ticket->ticket.data);

        if (ssl_TicketTimeValid(ss, session_ticket)) {
            ss->statelessResume = PR_TRUE;
        }

        if (ss->statelessResume) {
            PORT_Assert(ss->sec.ci.sid);
            rv = tls13_RecoverWrappedSharedSecret(ss, ss->sec.ci.sid);
            if (rv != SECSuccess) {
                FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
                SSL_AtomicIncrementLong(&ssl3stats->sch_sid_cache_not_ok);
                ssl_UncacheSessionID(ss);
                ssl_FreeSID(ss->sec.ci.sid);
                ss->sec.ci.sid = NULL;
                return SECFailure;
            }

            ss->ssl3.hs.cipher_suite = ss->sec.ci.sid->u.ssl3.cipherSuite;
            rv = ssl3_SetupCipherSuite(ss, PR_FALSE);
            if (rv != SECSuccess) {
                FATAL_ERROR(ss, PORT_GetError(), internal_error);
                return SECFailure;
            }
            PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks));
        }
    }

    /* Derive the binder keys if any PSKs. */
    if (!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks)) {
        /* If an External PSK specified a suite, use that. */
        sslPsk *psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);
        if (!ss->statelessResume &&
            psk->type == ssl_psk_external &&
            psk->zeroRttSuite != TLS_NULL_WITH_NULL_NULL) {
            ss->ssl3.hs.cipher_suite = psk->zeroRttSuite;
        }

        rv = tls13_ComputeEarlySecretsWithPsk(ss);
        if (rv != SECSuccess) {
            FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
            return SECFailure;
        }
    }

    return SECSuccess;
}

static SECStatus
tls13_ImportDHEKeyShare(SECKEYPublicKey *peerKey,
                        PRUint8 *b, PRUint32 length,
                        SECKEYPublicKey *pubKey)
{
    SECStatus rv;
    SECItem publicValue = { siBuffer, NULL, 0 };

    publicValue.data = b;
    publicValue.len = length;
    if (!ssl_IsValidDHEShare(&pubKey->u.dh.prime, &publicValue)) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_DHE_KEY_SHARE);
        return SECFailure;
    }

    peerKey->keyType = dhKey;
    rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.prime,
                          &pubKey->u.dh.prime);
    if (rv != SECSuccess)
        return SECFailure;
    rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.base,
                          &pubKey->u.dh.base);
    if (rv != SECSuccess)
        return SECFailure;
    rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.dh.publicValue,
                          &publicValue);
    if (rv != SECSuccess)
        return SECFailure;

    return SECSuccess;
}

static SECStatus
tls13_ImportKEMKeyShare(SECKEYPublicKey *peerKey, TLS13KeyShareEntry *entry)
{
    SECItem pk = { siBuffer, NULL, 0 };
    SECStatus rv;
    size_t expected_len;

    switch (entry->group->name) {
        case ssl_grp_kem_xyber768d00:
            expected_len = X25519_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES;
            break;
        case ssl_grp_kem_mlkem768x25519:
            expected_len = X25519_PUBLIC_KEY_BYTES + KYBER768_PUBLIC_KEY_BYTES;
            break;
        default:
            PORT_SetError(SEC_ERROR_UNSUPPORTED_KEYALG);
            return SECFailure;
    }

    if (entry->key_exchange.len != expected_len) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE);
        return SECFailure;
    }

    switch (entry->group->name) {
        case ssl_grp_kem_xyber768d00:
            peerKey->keyType = kyberKey;
            peerKey->u.kyber.params = params_kyber768_round3;
            // key_exchange.data is `x25519 || kyber768`
            pk.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES;
            pk.len = KYBER768_PUBLIC_KEY_BYTES;
            break;
        case ssl_grp_kem_mlkem768x25519:
            peerKey->keyType = kyberKey;
            peerKey->u.kyber.params = params_ml_kem768;
            // key_exchange.data is `mlkem768 || x25519`
            pk.data = entry->key_exchange.data;
            pk.len = KYBER768_PUBLIC_KEY_BYTES;
            break;
        default:
            PORT_Assert(0);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
    }

    rv = SECITEM_CopyItem(peerKey->arena, &peerKey->u.kyber.publicValue, &pk);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_NO_MEMORY);
        return SECFailure;
    }

    return SECSuccess;
}

static SECStatus
tls13_HandleKEMCiphertext(sslSocket *ss, TLS13KeyShareEntry *entry, sslKeyPair *keyPair, PK11SymKey **outKey)
{
    SECItem ct = { siBuffer, NULL, 0 };
    SECStatus rv;

    switch (entry->group->name) {
        case ssl_grp_kem_xyber768d00:
            if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) {
                ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE);
                return SECFailure;
            }
            ct.data = entry->key_exchange.data + X25519_PUBLIC_KEY_BYTES;
            ct.len = KYBER768_CIPHERTEXT_BYTES;
            break;
        case ssl_grp_kem_mlkem768x25519:
            if (entry->key_exchange.len != X25519_PUBLIC_KEY_BYTES + KYBER768_CIPHERTEXT_BYTES) {
                ssl_MapLowLevelError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE);
                return SECFailure;
            }
            ct.data = entry->key_exchange.data;
            ct.len = KYBER768_CIPHERTEXT_BYTES;
            break;
        default:
            PORT_Assert(0);
            ssl_MapLowLevelError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
    }

    rv = PK11_Decapsulate(keyPair->privKey, &ct, CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE, CKF_DERIVE, outKey);
    if (rv != SECSuccess) {
        ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE);
    }
    return rv;
}

static SECStatus
tls13_HandleKEMKey(sslSocket *ss,
                   TLS13KeyShareEntry *entry,
                   PK11SymKey **key,
                   SECItem **ciphertext)
{
    PORTCheapArenaPool arena;
    SECKEYPublicKey *peerKey;
    CK_OBJECT_HANDLE handle;
    SECStatus rv;

    PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE);
    peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey);
    if (peerKey == NULL) {
        goto loser;
    }
    peerKey->arena = &arena.arena;
    peerKey->pkcs11Slot = NULL;
    peerKey->pkcs11ID = CK_INVALID_HANDLE;

    rv = tls13_ImportKEMKeyShare(peerKey, entry);
    if (rv != SECSuccess) {
        goto loser;
    }

    PK11SlotInfo *slot = PK11_GetBestSlot(CKM_NSS_KYBER, ss->pkcs11PinArg);
    if (!slot) {
        goto loser;
    }

    handle = PK11_ImportPublicKey(slot, peerKey, PR_FALSE);
    PK11_FreeSlot(slot); /* peerKey holds a slot reference on success. */
    if (handle == CK_INVALID_HANDLE) {
        goto loser;
    }

    rv = PK11_Encapsulate(peerKey,
                          CKM_HKDF_DERIVE, PK11_ATTR_SESSION | PK11_ATTR_INSENSITIVE | PK11_ATTR_PUBLIC,
                          CKF_DERIVE, key, ciphertext);

    /* Destroy the imported public key */
    PORT_Assert(peerKey->pkcs11Slot);
    PK11_DestroyObject(peerKey->pkcs11Slot, peerKey->pkcs11ID);
    PK11_FreeSlot(peerKey->pkcs11Slot);

    PORT_DestroyCheapArena(&arena);
    return rv;

loser:
    PORT_DestroyCheapArena(&arena);
    return SECFailure;
}

SECStatus
tls13_HandleKeyShare(sslSocket *ss,
                     TLS13KeyShareEntry *entry,
                     sslKeyPair *keyPair,
                     SSLHashType hash,
                     PK11SymKey **out)
{
    PORTCheapArenaPool arena;
    SECKEYPublicKey *peerKey;
    CK_MECHANISM_TYPE mechanism;
    PK11SymKey *key;
    unsigned char *ec_data;
    SECStatus rv;
    int keySize = 0;

    PORT_InitCheapArena(&arena, DER_DEFAULT_CHUNKSIZE);
    peerKey = PORT_ArenaZNew(&arena.arena, SECKEYPublicKey);
    if (peerKey == NULL) {
        goto loser;
    }
    peerKey->arena = &arena.arena;
    peerKey->pkcs11Slot = NULL;
    peerKey->pkcs11ID = CK_INVALID_HANDLE;

    switch (entry->group->keaType) {
        case ssl_kea_ecdh_hybrid:
            switch (entry->group->name) {
                case ssl_grp_kem_xyber768d00:
                    // x25519 share is at the beginning
                    ec_data = entry->key_exchange.len < X25519_PUBLIC_KEY_BYTES
                                  ? NULL
                                  : entry->key_exchange.data;
                    break;
                case ssl_grp_kem_mlkem768x25519:
                    // x25519 share is at the end
                    ec_data = entry->key_exchange.len < X25519_PUBLIC_KEY_BYTES
                                  ? NULL
                                  : entry->key_exchange.data + entry->key_exchange.len - X25519_PUBLIC_KEY_BYTES;
                    break;
                default:
                    ec_data = NULL;
                    break;
            }
            if (!ec_data) {
                PORT_SetError(SSL_ERROR_RX_MALFORMED_HYBRID_KEY_SHARE);
                goto loser;
            }
            rv = ssl_ImportECDHKeyShare(peerKey,
                                        ec_data,
                                        X25519_PUBLIC_KEY_BYTES,
                                        ssl_LookupNamedGroup(ssl_grp_ec_curve25519));
            mechanism = CKM_ECDH1_DERIVE;
            break;
        case ssl_kea_ecdh:
            rv = ssl_ImportECDHKeyShare(peerKey,
                                        entry->key_exchange.data,
                                        entry->key_exchange.len,
                                        entry->group);
            mechanism = CKM_ECDH1_DERIVE;
            break;
        case ssl_kea_dh:
            rv = tls13_ImportDHEKeyShare(peerKey,
                                         entry->key_exchange.data,
                                         entry->key_exchange.len,
                                         keyPair->pubKey);
            mechanism = CKM_DH_PKCS_DERIVE;
            keySize = peerKey->u.dh.publicValue.len;
            break;
        default:
            PORT_Assert(0);
            goto loser;
    }
    if (rv != SECSuccess) {
        goto loser;
    }

    key = PK11_PubDeriveWithKDF(
        keyPair->privKey, peerKey, PR_FALSE, NULL, NULL, mechanism,
        CKM_HKDF_DERIVE, CKA_DERIVE, keySize, CKD_NULL, NULL, NULL);
    if (!key) {
        ssl_MapLowLevelError(SSL_ERROR_KEY_EXCHANGE_FAILURE);
        goto loser;
    }

    *out = key;
    PORT_DestroyCheapArena(&arena);
    return SECSuccess;

loser:
    PORT_DestroyCheapArena(&arena);
    return SECFailure;
}

static PRBool
tls13_UseServerSecret(sslSocket *ss, SSLSecretDirection direction)
{
    return ss->sec.isServer == (direction == ssl_secret_write);
}

static PK11SymKey **
tls13_TrafficSecretRef(sslSocket *ss, SSLSecretDirection direction)
{
    if (tls13_UseServerSecret(ss, direction)) {
        return &ss->ssl3.hs.serverTrafficSecret;
    }
    return &ss->ssl3.hs.clientTrafficSecret;
}

SECStatus
tls13_UpdateTrafficKeys(sslSocket *ss, SSLSecretDirection direction)
{
    PK11SymKey **secret;
    PK11SymKey *updatedSecret;
    PRUint16 epoch;
    SECStatus rv;

    secret = tls13_TrafficSecretRef(ss, direction);
    rv = tls13_HkdfExpandLabel(*secret, tls13_GetHash(ss),
                               NULL, 0,
                               kHkdfLabelTrafficUpdate,
                               strlen(kHkdfLabelTrafficUpdate),
                               tls13_GetHmacMechanism(ss),
                               tls13_GetHashSize(ss),
                               ss->protocolVariant,
                               &updatedSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    PK11_FreeSymKey(*secret);
    *secret = updatedSecret;

    ssl_GetSpecReadLock(ss);
    if (direction == ssl_secret_read) {
        epoch = ss->ssl3.crSpec->epoch;
    } else {
        epoch = ss->ssl3.cwSpec->epoch;
    }
    ssl_ReleaseSpecReadLock(ss);

    if (epoch == PR_UINT16_MAX) {
        /* Good chance that this is an overflow from too many updates. */
        FATAL_ERROR(ss, SSL_ERROR_TOO_MANY_KEY_UPDATES, internal_error);
        return SECFailure;
    }
    ++epoch;

    if (ss->secretCallback) {
        ss->secretCallback(ss->fd, epoch, direction, updatedSecret,
                           ss->secretCallbackArg);
    }
    rv = tls13_SetCipherSpec(ss, epoch, direction, PR_FALSE);
    if (rv != SECSuccess) {
        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
        return SECFailure;
    }
    return SECSuccess;
}

SECStatus
tls13_SendKeyUpdate(sslSocket *ss, tls13KeyUpdateRequest request, PRBool buffer)
{
    SECStatus rv;

    SSL_TRC(3, ("%d: TLS13[%d]: %s send key update, response %s",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss),
                (request == update_requested) ? "requested"
                                              : "not requested"));

    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));
    PORT_Assert(!ss->sec.isServer || !ss->ssl3.clientCertRequested);

    if (!tls13_IsPostHandshake(ss)) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_LIBRARY_FAILURE,
                              idle_handshake);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (IS_DTLS(ss)) {
        rv = dtls13_MaybeSendKeyUpdate(ss, request, buffer);
        if (rv != SECSuccess) {
            /* Error code set already. */
            return SECFailure;
        }
        return rv;
    }

    ssl_GetXmitBufLock(ss);
    rv = ssl3_AppendHandshakeHeader(ss, ssl_hs_key_update, 1);
    if (rv != SECSuccess) {
        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
        goto loser;
    }
    rv = ssl3_AppendHandshakeNumber(ss, request, 1);
    if (rv != SECSuccess) {
        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
        goto loser;
    }

    /* If we have been asked to buffer, then do so.  This allows us to coalesce
     * a KeyUpdate with a pending write. */

    rv = ssl3_FlushHandshake(ss, buffer ? ssl_SEND_FLAG_FORCE_INTO_BUFFER : 0);
    if (rv != SECSuccess) {
        goto loser; /* error code set by ssl3_FlushHandshake */
    }
    ssl_ReleaseXmitBufLock(ss);

    rv = tls13_UpdateTrafficKeys(ss, ssl_secret_write);
    if (rv != SECSuccess) {
        goto loser; /* error code set by tls13_UpdateTrafficKeys */
    }

    return SECSuccess;

loser:
    ssl_ReleaseXmitBufLock(ss);
    return SECFailure;
}

SECStatus
SSLExp_KeyUpdate(PRFileDesc *fd, PRBool requestUpdate)
{
    SECStatus rv;
    sslSocket *ss = ssl_FindSocket(fd);
    if (!ss) {
        return SECFailure;
    }

    if (!tls13_IsPostHandshake(ss)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (ss->ssl3.clientCertRequested) {
        PORT_SetError(PR_WOULD_BLOCK_ERROR);
        return SECFailure;
    }

    rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS,
                              idle_handshake);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    ssl_GetSSL3HandshakeLock(ss);
    rv = tls13_SendKeyUpdate(ss, requestUpdate ? update_requested : update_not_requested,
                             PR_FALSE /* don't buffer */);

    /* Remember that we are the ones that initiated this KeyUpdate. */
    if (rv == SECSuccess) {
        ss->ssl3.peerRequestedKeyUpdate = PR_FALSE;
    }
    ssl_ReleaseSSL3HandshakeLock(ss);
    return rv;
}

SECStatus
SSLExp_SetCertificateCompressionAlgorithm(PRFileDesc *fd, SSLCertificateCompressionAlgorithm alg)
{
    sslSocket *ss = ssl_FindSocket(fd);
    if (!ss) {
        return SECFailure; /* Code already set. */
    }

    ssl_GetSSL3HandshakeLock(ss);
    if (ss->ssl3.supportedCertCompressionAlgorithmsCount == MAX_SUPPORTED_CERTIFICATE_COMPRESSION_ALGS) {
        goto loser;
    }

    /* Reserved ID */
    if (alg.id == 0) {
        goto loser;
    }

    if (alg.encode == NULL && alg.decode == NULL) {
        goto loser;
    }

    /* Checking that we have not yet registed an algorithm with the same ID. */
    for (int i = 0; i < ss->ssl3.supportedCertCompressionAlgorithmsCount; i++) {
        if (ss->ssl3.supportedCertCompressionAlgorithms[i].id == alg.id) {
            goto loser;
        }
    }

    PORT_Memcpy(&ss->ssl3.supportedCertCompressionAlgorithms
                     [ss->ssl3.supportedCertCompressionAlgorithmsCount],
                &alg, sizeof(alg));
    ss->ssl3.supportedCertCompressionAlgorithmsCount += 1;
    ssl_ReleaseSSL3HandshakeLock(ss);
    return SECSuccess;

loser:
    PORT_SetError(SEC_ERROR_INVALID_ARGS);
    ssl_ReleaseSSL3HandshakeLock(ss);
    return SECFailure;
}

/*
 * enum {
 *     update_not_requested(0), update_requested(1), (255)
 * } KeyUpdateRequest;
 *
 * struct {
 *     KeyUpdateRequest request_update;
 * } KeyUpdate;
 */


/* If we're handing the DTLS1.3 message, we silently fail if there is a parsing problem. */
static SECStatus
tls13_HandleKeyUpdate(sslSocket *ss, PRUint8 *b, unsigned int length)
{
    SECStatus rv;
    PRUint32 update;

    SSL_TRC(3, ("%d: TLS13[%d]: %s handle key update",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    PORT_Assert(ss->opt.noLocks || ssl_HaveRecvBufLock(ss));
    PORT_Assert(ss->opt.noLocks || ssl_HaveSSL3HandshakeLock(ss));

    if (!tls13_IsPostHandshake(ss)) {
        FATAL_ERROR(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE, unexpected_message);
        return SECFailure;
    }

    rv = TLS13_CHECK_HS_STATE(ss, SSL_ERROR_RX_UNEXPECTED_KEY_UPDATE,
                              idle_handshake);
    if (rv != SECSuccess) {
        /* We should never be idle_handshake prior to firstHsDone. */
        FATAL_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE, internal_error);
        return SECFailure;
    }

    rv = ssl3_ConsumeHandshakeNumber(ss, &update, 1, &b, &length);
    if (rv != SECSuccess) {
        return SECFailure; /* Error code set already. */
    }
    if (length != 0) {
        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error);
        return SECFailure;
    }
    if (!(update == update_requested ||
          update == update_not_requested)) {
        FATAL_ERROR(ss, SSL_ERROR_RX_MALFORMED_KEY_UPDATE, decode_error);
        return SECFailure;
    }

    if (IS_DTLS(ss)) {
        return dtls13_HandleKeyUpdate(ss, b, length, update);
    }

    rv = tls13_UpdateTrafficKeys(ss, ssl_secret_read);
    if (rv != SECSuccess) {
        return SECFailure; /* Error code set by tls13_UpdateTrafficKeys. */
    }

    if (update == update_requested) {
        PRBool sendUpdate;
        if (ss->ssl3.clientCertRequested) {
            /* Post-handshake auth is in progress; defer sending a key update. */
            ss->ssl3.hs.keyUpdateDeferred = PR_TRUE;
            ss->ssl3.hs.deferredKeyUpdateRequest = update_not_requested;
            sendUpdate = PR_FALSE;
        } else if (ss->ssl3.peerRequestedKeyUpdate) {
            /* Only send an update if we have sent with the current spec.  This
             * prevents us from being forced to crank forward pointlessly. */

            ssl_GetSpecReadLock(ss);
            sendUpdate = ss->ssl3.cwSpec->nextSeqNum > 0;
            ssl_ReleaseSpecReadLock(ss);
        } else {
            sendUpdate = PR_TRUE;
        }
        if (sendUpdate) {
            /* Respond immediately (don't buffer). */
            rv = tls13_SendKeyUpdate(ss, update_not_requested, PR_FALSE);
            if (rv != SECSuccess) {
                return SECFailure; /* Error already set. */
            }
        }
        ss->ssl3.peerRequestedKeyUpdate = PR_TRUE;
    }

    return SECSuccess;
}

SECStatus
SSLExp_SendCertificateRequest(PRFileDesc *fd)
{
    SECStatus rv;
    sslSocket *ss = ssl_FindSocket(fd);
    if (!ss) {
        return SECFailure;
    }

    /* Not supported. */
    if (IS_DTLS(ss)) {
        PORT_SetError(SSL_ERROR_FEATURE_NOT_SUPPORTED_FOR_VERSION);
        return SECFailure;
    }

    if (!tls13_IsPostHandshake(ss)) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        return SECFailure;
    }

    if (ss->ssl3.clientCertRequested) {
        PORT_SetError(PR_WOULD_BLOCK_ERROR);
        return SECFailure;
    }

    /* Disallow a CertificateRequest if this connection uses an external PSK. */
    if (ss->sec.authType == ssl_auth_psk) {
        PORT_SetError(SSL_ERROR_FEATURE_DISABLED);
        return SECFailure;
    }

    rv = TLS13_CHECK_HS_STATE(ss, SEC_ERROR_INVALID_ARGS,
                              idle_handshake);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_post_handshake_auth_xtn)) {
        PORT_SetError(SSL_ERROR_MISSING_POST_HANDSHAKE_AUTH_EXTENSION);
        return SECFailure;
    }

    ssl_GetSSL3HandshakeLock(ss);

    rv = tls13_SendCertificateRequest(ss);
    if (rv == SECSuccess) {
        ssl_GetXmitBufLock(ss);
        rv = ssl3_FlushHandshake(ss, 0);
        ssl_ReleaseXmitBufLock(ss);
        ss->ssl3.clientCertRequested = PR_TRUE;
    }

    ssl_ReleaseSSL3HandshakeLock(ss);
    return rv;
}

SECStatus
tls13_HandlePostHelloHandshakeMessage(sslSocket *ss, PRUint8 *b, PRUint32 length)
{
    if (ss->sec.isServer && ss->ssl3.hs.zeroRttIgnore != ssl_0rtt_ignore_none) {
        SSL_TRC(3, ("%d: TLS13[%d]: successfully decrypted handshake after "
                    "failed 0-RTT",
                    SSL_GETPID(), ss->fd));
        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
    }

    /* TODO(ekr@rtfm.com): Would it be better to check all the states here? */
    switch (ss->ssl3.hs.msg_type) {
        case ssl_hs_certificate:
            return tls13_HandleCertificate(ss, b, length, PR_FALSE);
        case ssl_hs_compressed_certificate:
            return tls13_HandleCertificateDecode(ss, b, length);
        case ssl_hs_certificate_request:
            return tls13_HandleCertificateRequest(ss, b, length);

        case ssl_hs_certificate_verify:
            return tls13_HandleCertificateVerify(ss, b, length);

        case ssl_hs_encrypted_extensions:
            return tls13_HandleEncryptedExtensions(ss, b, length);

        case ssl_hs_new_session_ticket:
            return tls13_HandleNewSessionTicket(ss, b, length);

        case ssl_hs_finished:
            if (ss->sec.isServer) {
                return tls13_ServerHandleFinished(ss, b, length);
            } else {
                return tls13_ClientHandleFinished(ss, b, length);
            }

        case ssl_hs_end_of_early_data:
            return tls13_HandleEndOfEarlyData(ss, b, length);

        case ssl_hs_key_update:
            return tls13_HandleKeyUpdate(ss, b, length);

        default:
            FATAL_ERROR(ss, SSL_ERROR_RX_UNKNOWN_HANDSHAKE, unexpected_message);
            return SECFailure;
    }

    PORT_Assert(0); /* Unreached */
    return SECFailure;
}

static SECStatus
tls13_RecoverWrappedSharedSecret(sslSocket *ss, sslSessionID *sid)
{
    PK11SymKey *wrapKey; /* wrapping key */
    SECItem wrappedMS = { siBuffer, NULL, 0 };
    SSLHashType hashType;

    SSL_TRC(3, ("%d: TLS13[%d]: recovering static secret (%s)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    /* Now find the hash used as the PRF for the previous handshake. */
    hashType = tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite);

    /* If we are the server, we compute the wrapping key, but if we
     * are the client, its coordinates are stored with the ticket. */

    if (ss->sec.isServer) {
        wrapKey = ssl3_GetWrappingKey(ss, NULL,
                                      sid->u.ssl3.masterWrapMech,
                                      ss->pkcs11PinArg);
    } else {
        PK11SlotInfo *slot = SECMOD_LookupSlot(sid->u.ssl3.masterModuleID,
                                               sid->u.ssl3.masterSlotID);
        if (!slot)
            return SECFailure;

        wrapKey = PK11_GetWrapKey(slot,
                                  sid->u.ssl3.masterWrapIndex,
                                  sid->u.ssl3.masterWrapMech,
                                  sid->u.ssl3.masterWrapSeries,
                                  ss->pkcs11PinArg);
        PK11_FreeSlot(slot);
    }
    if (!wrapKey) {
        return SECFailure;
    }

    wrappedMS.data = sid->u.ssl3.keys.wrapped_master_secret;
    wrappedMS.len = sid->u.ssl3.keys.wrapped_master_secret_len;

    PK11SymKey *unwrappedPsk = ssl_unwrapSymKey(wrapKey, sid->u.ssl3.masterWrapMech,
                                                NULL, &wrappedMS, CKM_SSL3_MASTER_KEY_DERIVE,
                                                CKA_DERIVE, tls13_GetHashSizeForHash(hashType),
                                                CKF_SIGN | CKF_VERIFY, ss->pkcs11PinArg);
    PK11_FreeSymKey(wrapKey);
    if (!unwrappedPsk) {
        return SECFailure;
    }
    sslPsk *rpsk = tls13_MakePsk(unwrappedPsk, ssl_psk_resume, hashType, NULL);
    if (!rpsk) {
        PK11_FreeSymKey(unwrappedPsk);
        return SECFailure;
    }
    if (sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data) {
        rpsk->maxEarlyData = sid->u.ssl3.locked.sessionTicket.max_early_data_size;
        rpsk->zeroRttSuite = sid->u.ssl3.cipherSuite;
    }
    PRINT_KEY(50, (ss, "Recovered RMS", rpsk->key));
    PORT_Assert(PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks) ||
                ((sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks))->type != ssl_psk_resume);

    if (ss->sec.isServer) {
        /* In server, we couldn't select the RPSK in the extension handler
         * since it was not unwrapped yet. We're committed now, so select
         * it and add it to the list (to ensure it is freed). */

        ss->xtnData.selectedPsk = rpsk;
    }
    PR_APPEND_LINK(&rpsk->link, &ss->ssl3.hs.psks);

    return SECSuccess;
}

/* Key Derivation Functions.
 *
 *                 0
 *                 |
 *                 v
 *   PSK ->  HKDF-Extract = Early Secret
 *                 |
 *                 +-----> Derive-Secret(., "ext binder" | "res binder", "")
 *                 |                     = binder_key
 *                 |
 *                 +-----> Derive-Secret(., "c e traffic",
 *                 |                     ClientHello)
 *                 |                     = client_early_traffic_secret
 *                 |
 *                 +-----> Derive-Secret(., "e exp master",
 *                 |                     ClientHello)
 *                 |                     = early_exporter_secret
 *                 v
 *           Derive-Secret(., "derived", "")
 *                 |
 *                 v
 *(EC)DHE -> HKDF-Extract = Handshake Secret
 *                 |
 *                 +-----> Derive-Secret(., "c hs traffic",
 *                 |                     ClientHello...ServerHello)
 *                 |                     = client_handshake_traffic_secret
 *                 |
 *                 +-----> Derive-Secret(., "s hs traffic",
 *                 |                     ClientHello...ServerHello)
 *                 |                     = server_handshake_traffic_secret
 *                 v
 *           Derive-Secret(., "derived", "")
 *                 |
 *                 v
 *      0 -> HKDF-Extract = Master Secret
 *                 |
 *                 +-----> Derive-Secret(., "c ap traffic",
 *                 |                     ClientHello...Server Finished)
 *                 |                     = client_traffic_secret_0
 *                 |
 *                 +-----> Derive-Secret(., "s ap traffic",
 *                 |                     ClientHello...Server Finished)
 *                 |                     = server_traffic_secret_0
 *                 |
 *                 +-----> Derive-Secret(., "exp master",
 *                 |                     ClientHello...Server Finished)
 *                 |                     = exporter_secret
 *                 |
 *                 +-----> Derive-Secret(., "res master",
 *                                       ClientHello...Client Finished)
 *                                       = resumption_master_secret
 *
 */

static SECStatus
tls13_ComputeEarlySecretsWithPsk(sslSocket *ss)
{
    SECStatus rv;

    SSL_TRC(5, ("%d: TLS13[%d]: compute early secrets (%s)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    PORT_Assert(!ss->ssl3.hs.currentSecret);
    sslPsk *psk = NULL;

    if (ss->sec.isServer) {
        psk = ss->xtnData.selectedPsk;
    } else {
        /* Client to use the first PSK for early secrets. */
        PORT_Assert(!PR_CLIST_IS_EMPTY(&ss->ssl3.hs.psks));
        psk = (sslPsk *)PR_LIST_HEAD(&ss->ssl3.hs.psks);
    }
    PORT_Assert(psk && psk->key);
    PORT_Assert(psk->hash != ssl_hash_none);

    PK11SymKey *earlySecret = NULL;
    rv = tls13_HkdfExtract(NULL, psk->key, psk->hash, &earlySecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    /* No longer need the raw input key */
    PK11_FreeSymKey(psk->key);
    psk->key = NULL;
    const char *label = (psk->type == ssl_psk_resume) ? kHkdfLabelResPskBinderKey : kHkdfLabelExtPskBinderKey;
    rv = tls13_DeriveSecretNullHash(ss, earlySecret,
                                    label, strlen(label),
                                    &psk->binderKey, psk->hash);
    if (rv != SECSuccess) {
        PK11_FreeSymKey(earlySecret);
        return SECFailure;
    }
    ss->ssl3.hs.currentSecret = earlySecret;

    return SECSuccess;
}

/* This derives the early traffic and early exporter secrets. */
static SECStatus
tls13_DeriveEarlySecrets(sslSocket *ss)
{
    SECStatus rv;
    PORT_Assert(ss->ssl3.hs.currentSecret);
    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                kHkdfLabelClient,
                                kHkdfLabelEarlyTrafficSecret,
                                keylogLabelClientEarlyTrafficSecret,
                                &ss->ssl3.hs.clientEarlyTrafficSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (ss->secretCallback) {
        ss->secretCallback(ss->fd, (PRUint16)TrafficKeyEarlyApplicationData,
                           ss->sec.isServer ? ssl_secret_read : ssl_secret_write,
                           ss->ssl3.hs.clientEarlyTrafficSecret,
                           ss->secretCallbackArg);
    }

    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                NULL, kHkdfLabelEarlyExporterSecret,
                                keylogLabelEarlyExporterSecret,
                                &ss->ssl3.hs.earlyExporterSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    return SECSuccess;
}

static SECStatus
tls13_ComputeHandshakeSecret(sslSocket *ss)
{
    SECStatus rv;
    PK11SymKey *derivedSecret = NULL;
    PK11SymKey *newSecret = NULL;
    SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secret (%s)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    /* If no PSK, generate the default early secret. */
    if (!ss->ssl3.hs.currentSecret) {
        PORT_Assert(!ss->xtnData.selectedPsk);
        rv = tls13_HkdfExtract(NULL, NULL,
                               tls13_GetHash(ss), &ss->ssl3.hs.currentSecret);
        if (rv != SECSuccess) {
            return SECFailure;
        }
    }
    PORT_Assert(ss->ssl3.hs.currentSecret);
    PORT_Assert(ss->ssl3.hs.dheSecret);

    /* Derive-Secret(., "derived", "") */
    rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
                                    kHkdfLabelDerivedSecret,
                                    strlen(kHkdfLabelDerivedSecret),
                                    &derivedSecret, tls13_GetHash(ss));
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return rv;
    }

    /* HKDF-Extract(ECDHE, .) = Handshake Secret */
    rv = tls13_HkdfExtract(derivedSecret, ss->ssl3.hs.dheSecret,
                           tls13_GetHash(ss), &newSecret);
    PK11_FreeSymKey(derivedSecret);
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return rv;
    }

    PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
    ss->ssl3.hs.currentSecret = newSecret;
    return SECSuccess;
}

static SECStatus
tls13_ComputeHandshakeSecrets(sslSocket *ss)
{
    SECStatus rv;
    PK11SymKey *derivedSecret = NULL;
    PK11SymKey *newSecret = NULL;

    PK11_FreeSymKey(ss->ssl3.hs.dheSecret);
    ss->ssl3.hs.dheSecret = NULL;

    SSL_TRC(5, ("%d: TLS13[%d]: compute handshake secrets (%s)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    /* Now compute |*HsTrafficSecret| */
    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                kHkdfLabelClient,
                                kHkdfLabelHandshakeTrafficSecret,
                                keylogLabelClientHsTrafficSecret,
                                &ss->ssl3.hs.clientHsTrafficSecret);
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return rv;
    }
    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                kHkdfLabelServer,
                                kHkdfLabelHandshakeTrafficSecret,
                                keylogLabelServerHsTrafficSecret,
                                &ss->ssl3.hs.serverHsTrafficSecret);
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return rv;
    }

    if (ss->secretCallback) {
        SSLSecretDirection dir =
            ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
        ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir,
                           ss->ssl3.hs.clientHsTrafficSecret,
                           ss->secretCallbackArg);
        dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read;
        ss->secretCallback(ss->fd, (PRUint16)TrafficKeyHandshake, dir,
                           ss->ssl3.hs.serverHsTrafficSecret,
                           ss->secretCallbackArg);
    }

    SSL_TRC(5, ("%d: TLS13[%d]: compute master secret (%s)",
                SSL_GETPID(), ss->fd, SSL_ROLE(ss)));

    /* Crank HKDF forward to make master secret, which we
     * stuff in current secret. */

    rv = tls13_DeriveSecretNullHash(ss, ss->ssl3.hs.currentSecret,
                                    kHkdfLabelDerivedSecret,
                                    strlen(kHkdfLabelDerivedSecret),
                                    &derivedSecret, tls13_GetHash(ss));
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return rv;
    }
    rv = tls13_HkdfExtract(derivedSecret,
                           NULL,
                           tls13_GetHash(ss),
                           &newSecret);
    PK11_FreeSymKey(derivedSecret);
    if (rv != SECSuccess) {
        LOG_ERROR(ss, SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
    ss->ssl3.hs.currentSecret = newSecret;

    return SECSuccess;
}

static SECStatus
tls13_ComputeApplicationSecrets(sslSocket *ss)
{
    SECStatus rv;

    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                kHkdfLabelClient,
                                kHkdfLabelApplicationTrafficSecret,
                                keylogLabelClientTrafficSecret,
                                &ss->ssl3.hs.clientTrafficSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                kHkdfLabelServer,
                                kHkdfLabelApplicationTrafficSecret,
                                keylogLabelServerTrafficSecret,
                                &ss->ssl3.hs.serverTrafficSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (ss->secretCallback) {
        SSLSecretDirection dir =
            ss->sec.isServer ? ssl_secret_read : ssl_secret_write;
        ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData,
                           dir, ss->ssl3.hs.clientTrafficSecret,
                           ss->secretCallbackArg);
        dir = ss->sec.isServer ? ssl_secret_write : ssl_secret_read;
        ss->secretCallback(ss->fd, (PRUint16)TrafficKeyApplicationData,
                           dir, ss->ssl3.hs.serverTrafficSecret,
                           ss->secretCallbackArg);
    }

    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                NULL, kHkdfLabelExporterMasterSecret,
                                keylogLabelExporterSecret,
                                &ss->ssl3.hs.exporterSecret);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    return SECSuccess;
}

static SECStatus
tls13_ComputeFinalSecrets(sslSocket *ss)
{
    SECStatus rv;

    PORT_Assert(!ss->ssl3.crSpec->masterSecret);
    PORT_Assert(!ss->ssl3.cwSpec->masterSecret);
    PORT_Assert(ss->ssl3.hs.currentSecret);
    rv = tls13_DeriveSecretWrap(ss, ss->ssl3.hs.currentSecret,
                                NULL, kHkdfLabelResumptionMasterSecret,
                                NULL,
                                &ss->ssl3.hs.resumptionMasterSecret);
    PK11_FreeSymKey(ss->ssl3.hs.currentSecret);
    ss->ssl3.hs.currentSecret = NULL;
    if (rv != SECSuccess) {
        return SECFailure;
    }

    return SECSuccess;
}

static void
tls13_RestoreCipherInfo(sslSocket *ss, sslSessionID *sid)
{
    /* Set these to match the cached value.
     * TODO(ekr@rtfm.com): Make a version with the "true" values.
     * Bug 1256137.
     */

    ss->sec.authType = sid->authType;
    ss->sec.authKeyBits = sid->authKeyBits;
    ss->sec.originalKeaGroup = ssl_LookupNamedGroup(sid->keaGroup);
    ss->sec.signatureScheme = sid->sigScheme;
}

/* Check whether resumption-PSK is allowed. */
static PRBool
tls13_CanResume(sslSocket *ss, const sslSessionID *sid)
{
    const sslServerCert *sc;

    if (!sid) {
        return PR_FALSE;
    }

    if (sid->version != ss->version) {
        return PR_FALSE;
    }

#ifdef UNSAFE_FUZZER_MODE
    /* When fuzzing, sid could contain garbage that will crash tls13_GetHashForCipherSuite.
     * Do a direct comparison of cipher suites.  This makes us refuse to resume when the
     * protocol allows it, but resumption is discretionary anyway. */

    if (sid->u.ssl3.cipherSuite != ss->ssl3.hs.cipher_suite) {
#else
    if (tls13_GetHashForCipherSuite(sid->u.ssl3.cipherSuite) != tls13_GetHashForCipherSuite(ss->ssl3.hs.cipher_suite)) {
#endif
        return PR_FALSE;
    }

    /* Server sids don't remember the server cert we previously sent, but they
     * do remember the type of certificate we originally used, so we can locate
     * it again, provided that the current ssl socket has had its server certs
     * configured the same as the previous one. */

    sc = ssl_FindServerCert(ss, sid->authType, sid->namedCurve);
    if (!sc || !sc->serverCert) {
        return PR_FALSE;
    }

    return PR_TRUE;
}

static PRBool
tls13_CanNegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
{
    PORT_Assert(ss->ssl3.hs.zeroRttState == ssl_0rtt_sent);
    sslPsk *psk = ss->xtnData.selectedPsk;

    if (!ss->opt.enable0RttData) {
        return PR_FALSE;
    }
    if (!psk) {
        return PR_FALSE;
    }
    if (psk->zeroRttSuite == TLS_NULL_WITH_NULL_NULL) {
        return PR_FALSE;
    }
    if (!psk->maxEarlyData) {
        return PR_FALSE;
    }
    if (ss->ssl3.hs.cipher_suite != psk->zeroRttSuite) {
        return PR_FALSE;
    }
    if (psk->type == ssl_psk_resume) {
        if (!sid) {
            return PR_FALSE;
        }
        PORT_Assert(sid->u.ssl3.locked.sessionTicket.flags & ticket_allow_early_data);
        PORT_Assert(ss->statelessResume);
        if (!ss->statelessResume) {
            return PR_FALSE;
        }
        if (SECITEM_CompareItem(&ss->xtnData.nextProto,
                                &sid->u.ssl3.alpnSelection) != 0) {
            return PR_FALSE;
        }
    } else if (psk->type != ssl_psk_external) {
        PORT_Assert(0);
        return PR_FALSE;
    }

    if (tls13_IsReplay(ss, sid)) {
        return PR_FALSE;
    }

    return PR_TRUE;
}

/* Called from tls13_HandleClientHelloPart2 to update the state of 0-RTT handling.
 *
 * 0-RTT is only permitted if:
 * 1. The early data extension was present.
 * 2. We are resuming a session.
 * 3. The 0-RTT option is set.
 * 4. The ticket allowed 0-RTT.
 * 5. We negotiated the same ALPN value as in the ticket.
 */

static void
tls13_NegotiateZeroRtt(sslSocket *ss, const sslSessionID *sid)
{
    SSL_TRC(3, ("%d: TLS13[%d]: negotiate 0-RTT %p",
                SSL_GETPID(), ss->fd, sid));

    /* tls13_ServerHandleEarlyDataXtn sets this to ssl_0rtt_sent, so this will
     * be ssl_0rtt_none unless early_data is present. */

    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_none) {
        return;
    }

    if (ss->ssl3.hs.zeroRttState == ssl_0rtt_ignored) {
        /* HelloRetryRequest causes 0-RTT to be ignored. On the second
         * ClientHello, reset the ignore state so that decryption failure is
         * handled normally. */

        if (ss->ssl3.hs.zeroRttIgnore == ssl_0rtt_ignore_hrr) {
            PORT_Assert(ss->ssl3.hs.helloRetry);
            ss->ssl3.hs.zeroRttState = ssl_0rtt_none;
            ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
        } else {
            SSL_TRC(3, ("%d: TLS13[%d]: application ignored 0-RTT",
                        SSL_GETPID(), ss->fd));
        }
        return;
    }

    if (!tls13_CanNegotiateZeroRtt(ss, sid)) {
        SSL_TRC(3, ("%d: TLS13[%d]: ignore 0-RTT", SSL_GETPID(), ss->fd));
        ss->ssl3.hs.zeroRttState = ssl_0rtt_ignored;
        ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_trial;
        return;
    }

    SSL_TRC(3, ("%d: TLS13[%d]: enable 0-RTT", SSL_GETPID(), ss->fd));
    PORT_Assert(ss->xtnData.selectedPsk);
    ss->ssl3.hs.zeroRttState = ssl_0rtt_accepted;
    ss->ssl3.hs.zeroRttIgnore = ssl_0rtt_ignore_none;
    ss->ssl3.hs.zeroRttSuite = ss->ssl3.hs.cipher_suite;
    ss->ssl3.hs.preliminaryInfo |= ssl_preinfo_0rtt_cipher_suite;
}

/* Check if the offered group is acceptable. */
static PRBool
tls13_isGroupAcceptable(const sslNamedGroupDef *offered,
                        const sslNamedGroupDef *preferredGroup)
{
    /* We accept epsilon (e) bits around the offered group size. */
    const unsigned int e = 2;

    PORT_Assert(offered);
    PORT_Assert(preferredGroup);

    if (offered->bits >= preferredGroup->bits - e &&
        offered->bits <= preferredGroup->bits + e) {
        return PR_TRUE;
    }

    return PR_FALSE;
}

/* Find remote key share for given group and return it.
 * Returns NULL if no key share is found. */

static TLS13KeyShareEntry *
tls13_FindKeyShareEntry(sslSocket *ss, const sslNamedGroupDef *group)
{
    PRCList *cur_p = PR_NEXT_LINK(&ss->xtnData.remoteKeyShares);
    while (cur_p != &ss->xtnData.remoteKeyShares) {
        TLS13KeyShareEntry *offer = (TLS13KeyShareEntry *)cur_p;
        if (offer->group == group) {
            return offer;
        }
        cur_p = PR_NEXT_LINK(cur_p);
    }
    return NULL;
}

static SECStatus
tls13_NegotiateKeyExchange(sslSocket *ss,
                           const sslNamedGroupDef **requestedGroup,
                           TLS13KeyShareEntry **clientShare)
{
    unsigned int index;
    TLS13KeyShareEntry *entry = NULL;
    const sslNamedGroupDef *preferredGroup = NULL;

    /* We insist on DHE. */
    if (ssl3_ExtensionNegotiated(ss, ssl_tls13_pre_shared_key_xtn)) {
        if (!ssl3_ExtensionNegotiated(ss, ssl_tls13_psk_key_exchange_modes_xtn)) {
            FATAL_ERROR(ss, SSL_ERROR_MISSING_PSK_KEY_EXCHANGE_MODES,
                        missing_extension);
            return SECFailure;
        }
        /* Since the server insists on DHE to provide forward secracy, for
--> --------------------

--> maximum size reached

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

Messung V0.5
C=97 H=93 G=94

¤ Dauer der Verarbeitung: 0.24 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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge