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 65 kB image not shown  

Quelle  ssl3exthandle.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 "blapit.h"
#include "prinit.h"
#include "selfencrypt.h"
#include "ssl3ext.h"
#include "ssl3exthandle.h"
#include "tls13ech.h"
#include "tls13exthandle.h" /* For tls13_ServerSendStatusRequestXtn. */

PRBool
ssl_ShouldSendSNIExtension(const sslSocket *ss, const char *url)
{
    PRNetAddr netAddr;

    /* must have a hostname */
    if (!url || !url[0]) {
        return PR_FALSE;
    }
    /* must not be an IPv4 or IPv6 address */
    if (PR_SUCCESS == PR_StringToNetAddr(url, &netAddr)) {
        /* is an IP address (v4 or v6) */
        return PR_FALSE;
    }

    return PR_TRUE;
}

/* Format an SNI extension, using the name from the socket's URL,
 * unless that name is a dotted decimal string.
 * Used by client and server.
 */

SECStatus
ssl3_ClientFormatServerNameXtn(const sslSocket *ss, const char *url,
                               unsigned int len, TLSExtensionData *xtnData,
                               sslBuffer *buf)
{
    SECStatus rv;

    /* length of server_name_list */
    rv = sslBuffer_AppendNumber(buf, len + 3, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* Name Type (sni_host_name) */
    rv = sslBuffer_AppendNumber(buf, 0, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* HostName (length and value) */
    rv = sslBuffer_AppendVariable(buf, (const PRUint8 *)url, len, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    return SECSuccess;
}

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

    const char *url = ss->url;

    if (!ssl_ShouldSendSNIExtension(ss, url)) {
        return SECSuccess;
    }

    /* If ECH, write the public name. The real server name
     * is emplaced while constructing CHInner extensions. */

    sslEchConfig *cfg = (sslEchConfig *)PR_LIST_HEAD(&ss->echConfigs);
    const char *sniContents = PR_CLIST_IS_EMPTY(&ss->echConfigs) ? url : cfg->contents.publicName;
    rv = ssl3_ClientFormatServerNameXtn(ss, sniContents, strlen(sniContents), xtnData, buf);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_HandleServerNameXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                         SECItem *data)
{
    SECItem *names = NULL;
    PRUint32 listLenBytes = 0;
    SECStatus rv;

    if (!ss->sec.isServer) {
        return SECSuccess; /* ignore extension */
    }

    /* Server side - consume client data and register server sender. */
    /* do not parse the data if don't have user extension handling function. */
    if (!ss->sniSocketConfig) {
        return SECSuccess;
    }

    /* length of server_name_list */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &listLenBytes, 2, &data->data, &data->len);
    if (rv != SECSuccess) {
        goto loser; /* alert already sent */
    }
    if (listLenBytes == 0 || listLenBytes != data->len) {
        goto alert_loser;
    }

    /* Read ServerNameList. */
    while (data->len > 0) {
        SECItem tmp;
        PRUint32 type;

        /* Read Name Type. */
        rv = ssl3_ExtConsumeHandshakeNumber(ss, &type, 1, &data->data, &data->len);
        if (rv != SECSuccess) {
            /* alert sent in ConsumeHandshakeNumber */
            goto loser;
        }

        /* Read ServerName (length and value). */
        rv = ssl3_ExtConsumeHandshakeVariable(ss, &tmp, 2, &data->data, &data->len);
        if (rv != SECSuccess) {
            goto loser;
        }

        /* Record the value for host_name(0). */
        if (type == sni_nametype_hostname) {
            /* Fail if we encounter a second host_name entry. */
            if (names) {
                goto alert_loser;
            }

            /* Create an array for the only supported NameType. */
            names = PORT_ZNewArray(SECItem, 1);
            if (!names) {
                goto loser;
            }

            /* Copy ServerName into the array. */
            if (SECITEM_CopyItem(NULL, &names[0], &tmp) != SECSuccess) {
                goto loser;
            }
        }

        /* Even if we don't support NameTypes other than host_name at the
         * moment, we continue parsing the whole list to check its validity.
         * We do not check for duplicate entries with NameType != host_name(0).
         */

    }
    if (names) {
        /* Free old and set the new data. */
        ssl3_FreeSniNameArray(xtnData);
        xtnData->sniNameArr = names;
        xtnData->sniNameArrSize = 1;
        xtnData->negotiated[xtnData->numNegotiated++] = ssl_server_name_xtn;
    }
    return SECSuccess;

alert_loser:
    ssl3_ExtDecodeError(ss);
loser:
    if (names) {
        PORT_Free(names);
    }
    return SECFailure;
}

/* Frees a given xtnData->sniNameArr and its elements. */
void
ssl3_FreeSniNameArray(TLSExtensionData *xtnData)
{
    PRUint32 i;

    if (!xtnData->sniNameArr) {
        return;
    }

    for (i = 0; i < xtnData->sniNameArrSize; i++) {
        SECITEM_FreeItem(&xtnData->sniNameArr[i], PR_FALSE);
    }

    PORT_Free(xtnData->sniNameArr);
    xtnData->sniNameArr = NULL;
    xtnData->sniNameArrSize = 0;
}

/* Called by both clients and servers.
 * Clients sends a filled in session ticket if one is available, and otherwise
 * sends an empty ticket.  Servers always send empty tickets.
 */

SECStatus
ssl3_ClientSendSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                sslBuffer *buf, PRBool *added)
{
    NewSessionTicket *session_ticket = NULL;
    sslSessionID *sid = ss->sec.ci.sid;
    SECStatus rv;

    PORT_Assert(!ss->sec.isServer);

    /* Never send an extension with a ticket for TLS 1.3, but
     * OK to send the empty one in case the server does 1.2. */

    if ((sid->cached == in_client_cache || sid->cached == in_external_cache) &&
        sid->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
        return SECSuccess;
    }

    /* Ignore the SessionTicket extension if processing is disabled. */
    if (!ss->opt.enableSessionTickets) {
        return SECSuccess;
    }

    /* Send a session ticket if one is available.
     *
     * The caller must be holding sid->u.ssl3.lock for reading. We cannot
     * just acquire and release the lock within this function because the
     * caller will call this function twice, and we need the inputs to be
     * consistent between the two calls. Note that currently the caller
     * will only be holding the lock when we are the client and when we're
     * attempting to resume an existing session.
     */

    session_ticket = &sid->u.ssl3.locked.sessionTicket;
    if (session_ticket->ticket.data &&
        (xtnData->ticketTimestampVerified ||
         ssl_TicketTimeValid(ss, session_ticket))) {

        xtnData->ticketTimestampVerified = PR_FALSE;

        rv = sslBuffer_Append(buf, session_ticket->ticket.data,
                              session_ticket->ticket.len);
        if (rv != SECSuccess) {
            return SECFailure;
        }

        xtnData->sentSessionTicketInClientHello = PR_TRUE;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

PRBool
ssl_AlpnTagAllowed(const sslSocket *ss, const SECItem *tag)
{
    const unsigned char *data = ss->opt.nextProtoNego.data;
    unsigned int length = ss->opt.nextProtoNego.len;
    unsigned int offset = 0;

    if (!tag->len)
        return PR_TRUE;

    while (offset < length) {
        unsigned int taglen = (unsigned int)data[offset];
        if ((taglen == tag->len) &&
            !PORT_Memcmp(data + offset + 1, tag->data, tag->len))
            return PR_TRUE;
        offset += 1 + taglen;
    }

    return PR_FALSE;
}

/* ssl3_ValidateAppProtocol checks that the given block of data is valid: none
 * of the lengths may be 0 and the sum of the lengths must equal the length of
 * the block. */

SECStatus
ssl3_ValidateAppProtocol(const unsigned char *data, unsigned int length)
{
    unsigned int offset = 0;

    while (offset < length) {
        unsigned int newOffset = offset + 1 + (unsigned int)data[offset];
        /* Reject embedded nulls to protect against buggy applications that
         * store protocol identifiers in null-terminated strings.
         */

        if (newOffset > length || data[offset] == 0) {
            return SECFailure;
        }
        offset = newOffset;
    }

    return SECSuccess;
}

/* Protocol selection handler for ALPN. */
static SECStatus
ssl3_SelectAppProtocol(const sslSocket *ss, TLSExtensionData *xtnData,
                       PRUint16 extension, SECItem *data)
{
    SECStatus rv;
    unsigned char resultBuffer[255];
    SECItem result = { siBuffer, resultBuffer, 0 };

    rv = ssl3_ValidateAppProtocol(data->data, data->len);
    if (rv != SECSuccess) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    PORT_Assert(ss->nextProtoCallback);
    /* Neither the cipher suite nor ECH are selected yet Note that extensions
     * sometimes affect what cipher suite is selected, e.g., for ECC. */

    PORT_Assert((ss->ssl3.hs.preliminaryInfo &
                 ssl_preinfo_all & ~ssl_preinfo_cipher_suite & ~ssl_preinfo_ech) ==
                (ssl_preinfo_all & ~ssl_preinfo_cipher_suite & ~ssl_preinfo_ech));
    /* The callback has to make sure that either rv != SECSuccess or that result
     * is not set if there is no common protocol. */

    rv = ss->nextProtoCallback(ss->nextProtoArg, ss->fd, data->data, data->len,
                               result.data, &result.len, sizeof(resultBuffer));
    if (rv != SECSuccess) {
        /* Expect callback to call PORT_SetError() */
        ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
        return SECFailure;
    }

    /* If the callback wrote more than allowed to |result| it has corrupted our
     * stack. */

    if (result.len > sizeof(resultBuffer)) {
        PORT_SetError(SEC_ERROR_OUTPUT_LEN);
        PORT_Assert(PR_FALSE);
        return SECFailure;
    }

    SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);

    if (result.len < 1 || !result.data) {
        /* Check that we actually got a result. */
        ssl3_ExtSendAlert(ss, alert_fatal, no_application_protocol);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_NO_PROTOCOL);
        return SECFailure;
    }

    xtnData->nextProtoState = SSL_NEXT_PROTO_NEGOTIATED;
    xtnData->negotiated[xtnData->numNegotiated++] = extension;
    return SECITEM_CopyItem(NULL, &xtnData->nextProto, &result);
}

/* handle an incoming ALPN extension at the server */
SECStatus
ssl3_ServerHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             SECItem *data)
{
    PRUint32 count;
    SECStatus rv;

    /* We expressly don't want to allow ALPN on renegotiation,
     * despite it being permitted by the spec. */

    if (ss->firstHsDone || data->len == 0) {
        /* Clients MUST send a non-empty ALPN extension. */
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    /* ALPN has extra redundant length information so that
     * the extension is the same in both ClientHello and ServerHello. */

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &count, 2, &data->data, &data->len);
    if (rv != SECSuccess || count != data->len) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    if (!ss->nextProtoCallback) {
        /* we're not configured for it */
        return SECSuccess;
    }

    rv = ssl3_SelectAppProtocol(ss, xtnData, ssl_app_layer_protocol_xtn, data);
    if (rv != SECSuccess) {
        return rv;
    }

    /* prepare to send back a response, if we negotiated */
    if (xtnData->nextProtoState == SSL_NEXT_PROTO_NEGOTIATED) {
        rv = ssl3_RegisterExtensionSender(ss, xtnData,
                                          ssl_app_layer_protocol_xtn,
                                          ssl3_ServerSendAppProtoXtn);
        if (rv != SECSuccess) {
            ssl3_ExtSendAlert(ss, alert_fatal, internal_error);
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return rv;
        }
    }
    return SECSuccess;
}

SECStatus
ssl3_ClientHandleAppProtoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             SECItem *data)
{
    SECStatus rv;
    PRUint32 list_len;
    SECItem protocol_name;

    if (ssl3_ExtensionNegotiated(ss, ssl_next_proto_nego_xtn)) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* The extension data from the server has the following format:
     *   uint16 name_list_len;
     *   uint8 len;  // where len >= 1
     *   uint8 protocol_name[len]; */

    if (data->len < 4 || data->len > 2 + 1 + 255) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &list_len, 2, &data->data,
                                        &data->len);
    /* The list has to be the entire extension. */
    if (rv != SECSuccess || list_len != data->len) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    rv = ssl3_ExtConsumeHandshakeVariable(ss, &protocol_name, 1,
                                          &data->data, &data->len);
    /* The list must have exactly one value. */
    if (rv != SECSuccess || data->len != 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    if (!ssl_AlpnTagAllowed(ss, &protocol_name)) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_NEXT_PROTOCOL_DATA_INVALID);
        return SECFailure;
    }

    SECITEM_FreeItem(&xtnData->nextProto, PR_FALSE);
    xtnData->nextProtoState = SSL_NEXT_PROTO_SELECTED;
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_app_layer_protocol_xtn;
    return SECITEM_CopyItem(NULL, &xtnData->nextProto, &protocol_name);
}

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

    /* Renegotiations do not send this extension. */
    if (!ss->opt.enableALPN || !ss->opt.nextProtoNego.len || ss->firstHsDone) {
        PR_ASSERT(!ss->opt.nextProtoNego.data);
        return SECSuccess;
    }
    PRBool addGrease = ss->opt.enableGrease && ss->vrange.max >= SSL_LIBRARY_VERSION_TLS_1_3;

    /* The list of protocol strings is prefixed with a 2-byte length */
    rv = sslBuffer_AppendNumber(buf, ss->opt.nextProtoNego.len + (addGrease ? 3 : 0), 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* The list of protocol strings */
    rv = sslBuffer_Append(buf, ss->opt.nextProtoNego.data, ss->opt.nextProtoNego.len);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* A client MAY select one or more GREASE ALPN identifiers and advertise
     * them in the "application_layer_protocol_negotiation" extension, if sent
     * [RFC8701, Section 3.1]. */

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

    *added = PR_TRUE;
    return SECSuccess;
}

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

    /* We're in over our heads if any of these fail */
    PORT_Assert(ss->opt.enableALPN);
    PORT_Assert(xtnData->nextProto.data);
    PORT_Assert(xtnData->nextProto.len > 0);
    PORT_Assert(xtnData->nextProtoState == SSL_NEXT_PROTO_NEGOTIATED);
    PORT_Assert(!ss->firstHsDone);

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

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_ServerHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    sslExtensionBuilderFunc sender;

    PORT_Assert(ss->sec.isServer);

    /* remember that we got this extension. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_cert_status_xtn;

    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
        sender = tls13_ServerSendStatusRequestXtn;
    } else {
        sender = ssl3_ServerSendStatusRequestXtn;
    }
    return ssl3_RegisterExtensionSender(ss, xtnData, ssl_cert_status_xtn, sender);
}

SECStatus
ssl3_ServerSendStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                sslBuffer *buf, PRBool *added)
{
    const sslServerCert *serverCert = ss->sec.serverCert;

    if (!serverCert->certStatusArray ||
        !serverCert->certStatusArray->len) {
        return SECSuccess;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/* ssl3_ClientSendStatusRequestXtn builds the status_request extension on the
 * client side. See RFC 6066 section 8. */

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

    if (!ss->opt.enableOCSPStapling) {
        return SECSuccess;
    }

    rv = sslBuffer_AppendNumber(buf, 1 /* status_type ocsp */, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* A zero length responder_id_list means that the responders are
     * implicitly known to the server. */

    rv = sslBuffer_AppendNumber(buf, 0, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* A zero length request_extensions means that there are no extensions.
     * Specifically, we don't set the id-pkix-ocsp-nonce extension. This
     * means that the server can replay a cached OCSP response to us. */

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

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_ClientHandleStatusRequestXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    /* In TLS 1.3, the extension carries the OCSP response. */
    if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
        SECStatus rv;
        rv = ssl_ReadCertificateStatus(CONST_CAST(sslSocket, ss),
                                       data->data, data->len);
        if (rv != SECSuccess) {
            return SECFailure; /* code already set */
        }
    } else if (data->len != 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
        return SECFailure;
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_cert_status_xtn;
    return SECSuccess;
}

#define TLS_EX_SESS_TICKET_VERSION (0x010a)

/*
 * Called from ssl3_SendNewSessionTicket, tls13_SendNewSessionTicket
 */

SECStatus
ssl3_EncodeSessionTicket(sslSocket *ss, const NewSessionTicket *ticket,
                         const PRUint8 *appToken, unsigned int appTokenLen,
                         PK11SymKey *secret, SECItem *ticket_data)
{
    SECStatus rv;
    sslBuffer plaintext = SSL_BUFFER_EMPTY;
    SECItem ticket_buf = { 0, NULL, 0 };
    sslSessionID sid;
    unsigned char wrapped_ms[SSL3_MASTER_SECRET_LENGTH];
    SECItem ms_item = { 0, NULL, 0 };
    PRTime now;
    SECItem *srvName = NULL;
    CK_MECHANISM_TYPE msWrapMech;
    SECItem *alpnSelection = NULL;
    PRUint32 ticketAgeBaseline;

    SSL_TRC(3, ("%d: SSL3[%d]: send session_ticket handshake",
                SSL_GETPID(), ss->fd));

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

    /* Extract the master secret wrapped. */

    PORT_Memset(&sid, 0, sizeof(sslSessionID));

    PORT_Assert(secret);
    rv = ssl3_CacheWrappedSecret(ss, &sid, secret);
    if (rv == SECSuccess) {
        if (sid.u.ssl3.keys.wrapped_master_secret_len > sizeof(wrapped_ms))
            goto loser;
        memcpy(wrapped_ms, sid.u.ssl3.keys.wrapped_master_secret,
               sid.u.ssl3.keys.wrapped_master_secret_len);
        ms_item.data = wrapped_ms;
        ms_item.len = sid.u.ssl3.keys.wrapped_master_secret_len;
        msWrapMech = sid.u.ssl3.masterWrapMech;
    } else {
        /* TODO: else send an empty ticket. */
        goto loser;
    }
    /* Prep to send negotiated name */
    srvName = &ss->sec.ci.sid->u.ssl3.srvName;

    /* ticket version */
    rv = sslBuffer_AppendNumber(&plaintext, TLS_EX_SESS_TICKET_VERSION,
                                sizeof(PRUint16));
    if (rv != SECSuccess)
        goto loser;

    /* ssl_version */
    rv = sslBuffer_AppendNumber(&plaintext, ss->version,
                                sizeof(SSL3ProtocolVersion));
    if (rv != SECSuccess)
        goto loser;

    /* ciphersuite */
    rv = sslBuffer_AppendNumber(&plaintext, ss->ssl3.hs.cipher_suite,
                                sizeof(ssl3CipherSuite));
    if (rv != SECSuccess)
        goto loser;

    /* cipher spec parameters */
    rv = sslBuffer_AppendNumber(&plaintext, ss->sec.authType, 1);
    if (rv != SECSuccess)
        goto loser;
    rv = sslBuffer_AppendNumber(&plaintext, ss->sec.authKeyBits, 4);
    if (rv != SECSuccess)
        goto loser;
    rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaType, 1);
    if (rv != SECSuccess)
        goto loser;
    rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaKeyBits, 4);
    if (rv != SECSuccess)
        goto loser;
    if (ss->sec.keaGroup) {
        rv = sslBuffer_AppendNumber(&plaintext, ss->sec.keaGroup->name, 4);
        if (rv != SECSuccess)
            goto loser;
    } else {
        /* No kea group. Write 0 as invalid value. */
        rv = sslBuffer_AppendNumber(&plaintext, 0, 4);
        if (rv != SECSuccess)
            goto loser;
    }
    rv = sslBuffer_AppendNumber(&plaintext, ss->sec.signatureScheme, 4);
    if (rv != SECSuccess)
        goto loser;

    /* certificate type */
    PORT_Assert(SSL_CERT_IS(ss->sec.serverCert, ss->sec.authType));
    if (SSL_CERT_IS_EC(ss->sec.serverCert)) {
        const sslServerCert *cert = ss->sec.serverCert;
        PORT_Assert(cert->namedCurve);
        /* EC curves only use the second of the two bytes. */
        PORT_Assert(cert->namedCurve->name < 256);
        rv = sslBuffer_AppendNumber(&plaintext, cert->namedCurve->name, 1);
    } else {
        rv = sslBuffer_AppendNumber(&plaintext, 0, 1);
    }
    if (rv != SECSuccess)
        goto loser;

    /* master_secret */
    rv = sslBuffer_AppendNumber(&plaintext, msWrapMech, 4);
    if (rv != SECSuccess)
        goto loser;
    rv = sslBuffer_AppendVariable(&plaintext, ms_item.data, ms_item.len, 2);
    if (rv != SECSuccess)
        goto loser;

    /* client identity */
    if (ss->opt.requestCertificate && ss->sec.ci.sid->peerCert) {
        rv = sslBuffer_AppendNumber(&plaintext, CLIENT_AUTH_CERTIFICATE, 1);
        if (rv != SECSuccess)
            goto loser;
        rv = sslBuffer_AppendVariable(&plaintext,
                                      ss->sec.ci.sid->peerCert->derCert.data,
                                      ss->sec.ci.sid->peerCert->derCert.len, 2);
        if (rv != SECSuccess)
            goto loser;
    } else {
        rv = sslBuffer_AppendNumber(&plaintext, 0, 1);
        if (rv != SECSuccess)
            goto loser;
    }

    /* timestamp */
    now = ssl_Time(ss);
    PORT_Assert(sizeof(now) == 8);
    rv = sslBuffer_AppendNumber(&plaintext, now, 8);
    if (rv != SECSuccess)
        goto loser;

    /* HostName (length and value) */
    rv = sslBuffer_AppendVariable(&plaintext, srvName->data, srvName->len, 2);
    if (rv != SECSuccess)
        goto loser;

    /* extendedMasterSecretUsed */
    rv = sslBuffer_AppendNumber(
        &plaintext, ss->sec.ci.sid->u.ssl3.keys.extendedMasterSecretUsed, 1);
    if (rv != SECSuccess)
        goto loser;

    /* Flags */
    rv = sslBuffer_AppendNumber(&plaintext, ticket->flags,
                                sizeof(ticket->flags));
    if (rv != SECSuccess)
        goto loser;

    /* ALPN value. */
    PORT_Assert(ss->xtnData.nextProtoState == SSL_NEXT_PROTO_SELECTED ||
                ss->xtnData.nextProtoState == SSL_NEXT_PROTO_NEGOTIATED ||
                ss->xtnData.nextProto.len == 0);
    alpnSelection = &ss->xtnData.nextProto;
    PORT_Assert(alpnSelection->len < 256);
    rv = sslBuffer_AppendVariable(&plaintext, alpnSelection->data,
                                  alpnSelection->len, 1);
    if (rv != SECSuccess)
        goto loser;

    rv = sslBuffer_AppendNumber(&plaintext, ss->opt.maxEarlyDataSize, 4);
    if (rv != SECSuccess)
        goto loser;

    /*
     * We store this in the ticket:
     *    ticket_age_baseline = 1rtt - ticket_age_add
     *
     * When the client resumes, it will provide:
     *    obfuscated_age = ticket_age_client + ticket_age_add
     *
     * We expect to receive the ticket at:
     *    ticket_create + 1rtt + ticket_age_server
     *
     * We calculate the client's estimate of this as:
     *    ticket_create + ticket_age_baseline + obfuscated_age
     *    = ticket_create + 1rtt + ticket_age_client
     *
     * This is compared to the expected time, which should differ only as a
     * result of clock errors or errors in the RTT estimate.
     */

    ticketAgeBaseline = ss->ssl3.hs.rttEstimate / PR_USEC_PER_MSEC;
    ticketAgeBaseline -= ticket->ticket_age_add;
    rv = sslBuffer_AppendNumber(&plaintext, ticketAgeBaseline, 4);
    if (rv != SECSuccess)
        goto loser;

    /* Application token */
    rv = sslBuffer_AppendVariable(&plaintext, appToken, appTokenLen, 2);
    if (rv != SECSuccess)
        goto loser;

    /* This really only happens if appTokenLen is too much, and that always
     * comes from the using application. */

    if (SSL_BUFFER_LEN(&plaintext) > 0xffff) {
        PORT_SetError(SEC_ERROR_INVALID_ARGS);
        goto loser;
    }

    ticket_buf.len = ssl_SelfEncryptGetProtectedSize(SSL_BUFFER_LEN(&plaintext));
    PORT_Assert(ticket_buf.len > 0);
    if (SECITEM_AllocItem(NULL, &ticket_buf, ticket_buf.len) == NULL) {
        goto loser;
    }

    /* Finally, encrypt the ticket. */
    rv = ssl_SelfEncryptProtect(ss, SSL_BUFFER_BASE(&plaintext),
                                SSL_BUFFER_LEN(&plaintext),
                                ticket_buf.data, &ticket_buf.len, ticket_buf.len);
    if (rv != SECSuccess) {
        goto loser;
    }

    /* Give ownership of memory to caller. */
    *ticket_data = ticket_buf;

    sslBuffer_Clear(&plaintext);
    return SECSuccess;

loser:
    sslBuffer_Clear(&plaintext);
    if (ticket_buf.data) {
        SECITEM_FreeItem(&ticket_buf, PR_FALSE);
    }

    return SECFailure;
}

/* When a client receives a SessionTicket extension a NewSessionTicket
 * message is expected during the handshake.
 */

SECStatus
ssl3_ClientHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);

    if (data->len != 0) {
        return SECSuccess; /* Ignore the extension. */
    }

    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_session_ticket_xtn;
    return SECSuccess;
}

PR_STATIC_ASSERT((TLS_EX_SESS_TICKET_VERSION >> 8) == 1);

static SECStatus
ssl_ParseSessionTicket(sslSocket *ss, const SECItem *decryptedTicket,
                       SessionTicket *parsedTicket)
{
    PRUint32 temp;
    SECStatus rv;

    PRUint8 *buffer = decryptedTicket->data;
    unsigned int len = decryptedTicket->len;

    PORT_Memset(parsedTicket, 0, sizeof(*parsedTicket));
    parsedTicket->valid = PR_FALSE;

    /* If the decrypted ticket is empty, then report success, but leave the
     * ticket marked as invalid. */

    if (decryptedTicket->len == 0) {
        return SECSuccess;
    }

    /* Read ticket version. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* All ticket versions start with 0x01, so check to see if this
     * is a ticket or some other self-encrypted thing. */

    if ((temp >> 8) != 1) {
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }
    /* Skip the ticket if the version is wrong.  This won't result in a
     * handshake failure, just a failure to resume. */

    if (temp != TLS_EX_SESS_TICKET_VERSION) {
        return SECSuccess;
    }

    /* Read SSLVersion. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->ssl_version = (SSL3ProtocolVersion)temp;
    if (!ssl3_VersionIsSupported(ss->protocolVariant,
                                 parsedTicket->ssl_version)) {
        /* This socket doesn't support the version from the ticket. */
        return SECSuccess;
    }

    /* Read cipher_suite. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->cipher_suite = (ssl3CipherSuite)temp;

    /* Read cipher spec parameters. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

#ifndef UNSAFE_FUZZER_MODE
    PORT_Assert(temp < ssl_auth_size);
#else
    temp %= (8 * sizeof(SSLAuthType)) - 1;
#endif

    parsedTicket->authType = (SSLAuthType)temp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->authKeyBits = temp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->keaType = (SSLKEAType)temp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->keaKeyBits = temp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->originalKeaGroup = temp;
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->signatureScheme = (SSLSignatureScheme)temp;

    /* Read the optional named curve. */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    if (parsedTicket->authType == ssl_auth_ecdsa ||
        parsedTicket->authType == ssl_auth_ecdh_rsa ||
        parsedTicket->authType == ssl_auth_ecdh_ecdsa) {
        const sslNamedGroupDef *group =
            ssl_LookupNamedGroup((SSLNamedGroup)temp);
        if (!group || group->keaType != ssl_kea_ecdh) {
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
        }
        parsedTicket->namedCurve = group;
    }

    /* Read the master secret (and how it is wrapped). */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->msWrapMech = (CK_MECHANISM_TYPE)temp;

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 2, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    if (temp == 0 || temp > sizeof(parsedTicket->master_secret)) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->ms_length = (PRUint16)temp;

    /* Read the master secret. */
    rv = ssl3_ExtConsumeHandshake(ss, parsedTicket->master_secret,
                                  parsedTicket->ms_length, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    /* Read client identity */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->client_auth_type = (ClientAuthenticationType)temp;
    switch (parsedTicket->client_auth_type) {
        case CLIENT_AUTH_ANONYMOUS:
            break;
        case CLIENT_AUTH_CERTIFICATE:
            rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->peer_cert, 2,
                                                  &buffer, &len);
            if (rv != SECSuccess) {
                PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
                return SECFailure;
            }
            break;
        default:
            PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
            return SECFailure;
    }

    /* Read timestamp.  This is a 64-bit value and
     * ssl3_ExtConsumeHandshakeNumber only reads 32-bits at a time. */

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* Cast to avoid undefined behavior if the top bit is set. */
    parsedTicket->timestamp = (PRTime)((PRUint64)temp << 32);
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->timestamp |= (PRTime)temp;

    /* Read server name */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->srvName, 2,
                                          &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    /* Read extendedMasterSecretUsed */
    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 1, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
#ifndef UNSAFE_FUZZER_MODE
    /* A well-behaving server should only write 0 or 1. */
    PORT_Assert(temp == PR_TRUE || temp == PR_FALSE);
#endif
    parsedTicket->extendedMasterSecretUsed = temp ? PR_TRUE : PR_FALSE;

    rv = ssl3_ExtConsumeHandshake(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->flags = PR_ntohl(temp);

    rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->alpnSelection, 1,
                                          &buffer, &len);
    PORT_Assert(parsedTicket->alpnSelection.len < 256);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->maxEarlyData = temp;

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &temp, 4, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
    parsedTicket->ticketAgeBaseline = temp;

    rv = ssl3_ExtConsumeHandshakeVariable(ss, &parsedTicket->applicationToken,
                                          2, &buffer, &len);
    if (rv != SECSuccess) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }

#ifndef UNSAFE_FUZZER_MODE
    /* Done parsing.  Check that all bytes have been consumed. */
    if (len != 0) {
        PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
        return SECFailure;
    }
#endif

    parsedTicket->valid = PR_TRUE;
    return SECSuccess;
}

static SECStatus
ssl_CreateSIDFromTicket(sslSocket *ss, const SECItem *rawTicket,
                        SessionTicket *parsedTicket, sslSessionID **out)
{
    sslSessionID *sid;
    SECStatus rv;

    sid = ssl3_NewSessionID(ss, PR_TRUE);
    if (sid == NULL) {
        return SECFailure;
    }

    /* Copy over parameters. */
    sid->version = parsedTicket->ssl_version;
    sid->creationTime = parsedTicket->timestamp;
    sid->u.ssl3.cipherSuite = parsedTicket->cipher_suite;
    sid->authType = parsedTicket->authType;
    sid->authKeyBits = parsedTicket->authKeyBits;
    sid->keaType = parsedTicket->keaType;
    sid->keaKeyBits = parsedTicket->keaKeyBits;
    sid->keaGroup = parsedTicket->originalKeaGroup;
    sid->namedCurve = parsedTicket->namedCurve;
    sid->sigScheme = parsedTicket->signatureScheme;

    rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.locked.sessionTicket.ticket,
                          rawTicket);
    if (rv != SECSuccess) {
        goto loser;
    }
    sid->u.ssl3.locked.sessionTicket.flags = parsedTicket->flags;
    sid->u.ssl3.locked.sessionTicket.max_early_data_size =
        parsedTicket->maxEarlyData;

    if (parsedTicket->ms_length >
        sizeof(sid->u.ssl3.keys.wrapped_master_secret)) {
        goto loser;
    }
    PORT_Memcpy(sid->u.ssl3.keys.wrapped_master_secret,
                parsedTicket->master_secret, parsedTicket->ms_length);
    sid->u.ssl3.keys.wrapped_master_secret_len = parsedTicket->ms_length;
    sid->u.ssl3.masterWrapMech = parsedTicket->msWrapMech;
    sid->u.ssl3.masterValid = PR_TRUE;
    sid->u.ssl3.keys.resumable = PR_TRUE;
    sid->u.ssl3.keys.extendedMasterSecretUsed = parsedTicket->extendedMasterSecretUsed;

    /* Copy over client cert from session ticket if there is one. */
    if (parsedTicket->peer_cert.data != NULL) {
        PORT_Assert(!sid->peerCert);
        sid->peerCert = CERT_NewTempCertificate(ss->dbHandle,
                                                &parsedTicket->peer_cert,
                                                NULL, PR_FALSE, PR_TRUE);
        if (!sid->peerCert) {
            goto loser;
        }
    }

    /* Transfer ownership of the remaining items. */
    if (parsedTicket->srvName.data != NULL) {
        SECITEM_FreeItem(&sid->u.ssl3.srvName, PR_FALSE);
        rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.srvName,
                              &parsedTicket->srvName);
        if (rv != SECSuccess) {
            goto loser;
        }
    }
    if (parsedTicket->alpnSelection.data != NULL) {
        SECITEM_FreeItem(&sid->u.ssl3.alpnSelection, PR_FALSE);
        rv = SECITEM_CopyItem(NULL, &sid->u.ssl3.alpnSelection,
                              &parsedTicket->alpnSelection);
        if (rv != SECSuccess) {
            goto loser;
        }
    }

    *out = sid;
    return SECSuccess;

loser:
    ssl_FreeSID(sid);
    return SECFailure;
}

/* Generic ticket processing code, common to all TLS versions. */
SECStatus
ssl3_ProcessSessionTicketCommon(sslSocket *ss, const SECItem *ticket,
                                SECItem *appToken)
{
    SECItem decryptedTicket = { siBuffer, NULL, 0 };
    SessionTicket parsedTicket;
    sslSessionID *sid = NULL;
    SECStatus rv;

    if (ss->sec.ci.sid != NULL) {
        ssl_UncacheSessionID(ss);
        ssl_FreeSID(ss->sec.ci.sid);
        ss->sec.ci.sid = NULL;
    }

    if (!SECITEM_AllocItem(NULL, &decryptedTicket, ticket->len)) {
        return SECFailure;
    }

    /* Decrypt the ticket. */
    rv = ssl_SelfEncryptUnprotect(ss, ticket->data, ticket->len,
                                  decryptedTicket.data,
                                  &decryptedTicket.len,
                                  decryptedTicket.len);
    if (rv != SECSuccess) {
        /* Ignore decryption failure if we are doing TLS 1.3; that
         * means the server rejects the client's resumption
         * attempt. In TLS 1.2, however, it's a hard failure, unless
         * it's just because we're not the recipient of the ticket. */

        if (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3 ||
            PORT_GetError() == SEC_ERROR_NOT_A_RECIPIENT) {
            SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
            return SECSuccess;
        }

        SSL3_SendAlert(ss, alert_fatal, illegal_parameter);
        goto loser;
    }

    rv = ssl_ParseSessionTicket(ss, &decryptedTicket, &parsedTicket);
    if (rv != SECSuccess) {
        SSL3Statistics *ssl3stats;

        SSL_DBG(("%d: SSL[%d]: Session ticket parsing failed.",
                 SSL_GETPID(), ss->fd));
        ssl3stats = SSL_GetStatistics();
        SSL_AtomicIncrementLong(&ssl3stats->hch_sid_ticket_parse_failures);
        goto loser; /* code already set */
    }

    /* Use the ticket if it is valid and unexpired. */
    PRTime end = parsedTicket.timestamp + (ssl_ticket_lifetime * PR_USEC_PER_SEC);
    if (end > ssl_Time(ss)) {

        rv = ssl_CreateSIDFromTicket(ss, ticket, &parsedTicket, &sid);
        if (rv != SECSuccess) {
            goto loser; /* code already set */
        }
        if (appToken && parsedTicket.applicationToken.len) {
            rv = SECITEM_CopyItem(NULL, appToken,
                                  &parsedTicket.applicationToken);
            if (rv != SECSuccess) {
                goto loser; /* code already set */
            }
        }

        ss->statelessResume = PR_TRUE;
        ss->sec.ci.sid = sid;

        /* We have the baseline value for the obfuscated ticket age here.  Save
         * that in xtnData temporarily.  This value is updated in
         * tls13_ServerHandlePreSharedKeyXtn with the final estimate. */

        ss->xtnData.ticketAge = parsedTicket.ticketAgeBaseline;
    }

    SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
    PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
    return SECSuccess;

loser:
    if (sid) {
        ssl_FreeSID(sid);
    }
    SECITEM_ZfreeItem(&decryptedTicket, PR_FALSE);
    PORT_Memset(&parsedTicket, 0, sizeof(parsedTicket));
    return SECFailure;
}

SECStatus
ssl3_ServerHandleSessionTicketXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                  SECItem *data)
{
    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);

    /* Ignore the SessionTicket extension if processing is disabled. */
    if (!ss->opt.enableSessionTickets) {
        return SECSuccess;
    }

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

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

    /* Parse the received ticket sent in by the client.  We are
     * lenient about some parse errors, falling back to a fullshake
     * instead of terminating the current connection.
     */

    if (data->len == 0) {
        xtnData->emptySessionTicket = PR_TRUE;
        return SECSuccess;
    }

    return ssl3_ProcessSessionTicketCommon(CONST_CAST(sslSocket, ss), data,
                                           NULL);
}

/* Extension format:
 * Extension number:   2 bytes
 * Extension length:   2 bytes
 * Verify Data Length: 1 byte
 * Verify Data (TLS): 12 bytes (client) or 24 bytes (server)
 * Verify Data (SSL): 36 bytes (client) or 72 bytes (server)
 */

SECStatus
ssl3_SendRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                              sslBuffer *buf, PRBool *added)
{
    PRInt32 len = 0;
    SECStatus rv;

    /* In RFC 5746, it is NOT RECOMMENDED to send both the SCSV and the empty
     * RI, so when we send SCSV in the initial handshake, we don't also send RI.
     */

    if (ss->ssl3.hs.sendingSCSV) {
        return 0;
    }
    if (ss->firstHsDone) {
        len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes * 2
                               : ss->ssl3.hs.finishedBytes;
    }

    /* verify_Data from previous Finished message(s) */
    rv = sslBuffer_AppendVariable(buf,
                                  ss->ssl3.hs.finishedMsgs.data, len, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

/* This function runs in both the client and server.  */
SECStatus
ssl3_HandleRenegotiationInfoXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                SECItem *data)
{
    SECStatus rv = SECSuccess;
    PRUint32 len = 0;

    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);

    if (ss->firstHsDone) {
        len = ss->sec.isServer ? ss->ssl3.hs.finishedBytes
                               : ss->ssl3.hs.finishedBytes * 2;
    }
    if (data->len != 1 + len || data->data[0] != len) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }
    if (len && NSS_SecureMemcmp(ss->ssl3.hs.finishedMsgs.data,
                                data->data + 1, len)) {
        ssl3_ExtSendAlert(ss, alert_fatal, handshake_failure);
        PORT_SetError(SSL_ERROR_BAD_HANDSHAKE_HASH_VALUE);
        return SECFailure;
    }
    /* remember that we got this extension and it was correct. */
    CONST_CAST(sslSocket, ss)
        ->peerRequestedProtection = 1;
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_renegotiation_info_xtn;
    if (ss->sec.isServer) {
        /* prepare to send back the appropriate response */
        rv = ssl3_RegisterExtensionSender(ss, xtnData,
                                          ssl_renegotiation_info_xtn,
                                          ssl3_SendRenegotiationInfoXtn);
    }
    return rv;
}

SECStatus
ssl3_ClientSendUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                          sslBuffer *buf, PRBool *added)
{
    unsigned int i;
    SECStatus rv;

    if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) {
        return SECSuccess; /* Not relevant */
    }

    /* Length of the SRTP cipher list */
    rv = sslBuffer_AppendNumber(buf, 2 * ss->ssl3.dtlsSRTPCipherCount, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* The SRTP ciphers */
    for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
        rv = sslBuffer_AppendNumber(buf, ss->ssl3.dtlsSRTPCiphers[i], 2);
        if (rv != SECSuccess) {
            return SECFailure;
        }
    }
    /* Empty MKI value */
    rv = sslBuffer_AppendNumber(buf, 0, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

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

    /* Length of the SRTP cipher list */
    rv = sslBuffer_AppendNumber(buf, 2, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* The selected cipher */
    rv = sslBuffer_AppendNumber(buf, xtnData->dtlsSRTPCipherSuite, 2);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    /* Empty MKI value */
    rv = sslBuffer_AppendNumber(buf, 0, 1);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_ClientHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                            SECItem *data)
{
    SECStatus rv;
    SECItem ciphers = { siBuffer, NULL, 0 };
    PRUint16 i;
    PRUint16 cipher = 0;
    PRBool found = PR_FALSE;
    SECItem litem;

    if (!data->data || !data->len) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    /* Get the cipher list */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &ciphers, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure; /* fatal alert already sent */
    }
    /* Now check that the server has picked just 1 (i.e., len = 2) */
    if (ciphers.len != 2) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    /* Get the selected cipher */
    cipher = (ciphers.data[0] << 8) | ciphers.data[1];

    /* Now check that this is one of the ciphers we offered */
    for (i = 0; i < ss->ssl3.dtlsSRTPCipherCount; i++) {
        if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) {
            found = PR_TRUE;
            break;
        }
    }

    if (!found) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
        return SECFailure;
    }

    /* Get the srtp_mki value */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &litem, 1,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure; /* alert already sent */
    }

    /* We didn't offer an MKI, so this must be 0 length */
    if (litem.len != 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_SERVER_HELLO);
        return SECFailure;
    }

    /* extra trailing bytes */
    if (data->len != 0) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    /* OK, this looks fine. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_use_srtp_xtn;
    xtnData->dtlsSRTPCipherSuite = cipher;
    return SECSuccess;
}

SECStatus
ssl3_ServerHandleUseSRTPXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                            SECItem *data)
{
    SECStatus rv;
    SECItem ciphers = { siBuffer, NULL, 0 };
    PRUint16 i;
    unsigned int j;
    PRUint16 cipher = 0;
    PRBool found = PR_FALSE;
    SECItem litem;

    if (!IS_DTLS(ss) || !ss->ssl3.dtlsSRTPCipherCount) {
        /* Ignore the extension if we aren't doing DTLS or no DTLS-SRTP
         * preferences have been set. */

        return SECSuccess;
    }

    if (!data->data || data->len < 5) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    /* Get the cipher list */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &ciphers, 2,
                                          &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure; /* alert already sent */
    }
    /* Check that the list is even length */
    if (ciphers.len % 2) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }

    /* Walk through the offered list and pick the most preferred of our
     * ciphers, if any */

    for (i = 0; !found && i < ss->ssl3.dtlsSRTPCipherCount; i++) {
        for (j = 0; j + 1 < ciphers.len; j += 2) {
            cipher = (ciphers.data[j] << 8) | ciphers.data[j + 1];
            if (cipher == ss->ssl3.dtlsSRTPCiphers[i]) {
                found = PR_TRUE;
                break;
            }
        }
    }

    /* Get the srtp_mki value */
    rv = ssl3_ExtConsumeHandshakeVariable(ss, &litem, 1, &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    if (data->len != 0) {
        ssl3_ExtDecodeError(ss); /* trailing bytes */
        return SECFailure;
    }

    /* Now figure out what to do */
    if (!found) {
        /* No matching ciphers, pretend we don't support use_srtp */
        return SECSuccess;
    }

    /* OK, we have a valid cipher and we've selected it */
    xtnData->dtlsSRTPCipherSuite = cipher;
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_use_srtp_xtn;

    return ssl3_RegisterExtensionSender(ss, xtnData,
                                        ssl_use_srtp_xtn,
                                        ssl3_ServerSendUseSRTPXtn);
}

/* ssl3_HandleSigAlgsXtn handles the signature_algorithms extension from a
 * client.  In TLS 1.3, the client uses this to parse CertificateRequest
 * extensions.  See https://tools.ietf.org/html/rfc5246#section-7.4.1.4.1 */

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

    /* Ignore this extension if we aren't doing TLS 1.2 or greater. */
    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_2) {
        return SECSuccess;
    }

    if (xtnData->sigSchemes) {
        PORT_Free(xtnData->sigSchemes);
        xtnData->sigSchemes = NULL;
    }
    rv = ssl_ParseSignatureSchemes(ss, NULL,
                                   &xtnData->sigSchemes,
                                   &xtnData->numSigSchemes,
                                   &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->numSigSchemes == 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->negotiated[xtnData->numNegotiated++] = ssl_signature_algorithms_xtn;
    return SECSuccess;
}

/* ssl3_ClientSendSigAlgsXtn sends the signature_algorithm extension for TLS
 * 1.2 ClientHellos. */

SECStatus
ssl3_SendSigAlgsXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                    sslBuffer *buf, PRBool *added)
{
    if (ss->vrange.max < SSL_LIBRARY_VERSION_TLS_1_2) {
        return SECSuccess;
    }

    PRUint16 minVersion;
    if (ss->sec.isServer) {
        minVersion = ss->version; /* CertificateRequest */
    } else {
        minVersion = ss->vrange.min; /* ClientHello */
    }

    SECStatus rv = ssl3_EncodeSigAlgs(ss, minVersion, PR_TRUE /* forCert */,
                                      ss->opt.enableGrease, buf);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_SendExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                 sslBuffer *buf, PRBool *added)
{
    if (!ss->opt.enableExtendedMS) {
        return SECSuccess;
    }

    /* Always send the extension in this function, since the
     * client always sends it and this function is only called on
     * the server if we negotiated the extension. */

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_HandleExtendedMasterSecretXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                   SECItem *data)
{
    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);

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

    if (!ss->opt.enableExtendedMS) {
        return SECSuccess;
    }

    if (data->len != 0) {
        SSL_TRC(30, ("%d: SSL3[%d]: Bogus extended master secret extension",
                     SSL_GETPID(), ss->fd));
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        return SECFailure;
    }

    SSL_DBG(("%d: SSL[%d]: Negotiated extended master secret extension.",
             SSL_GETPID(), ss->fd));

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

    if (ss->sec.isServer) {
        return ssl3_RegisterExtensionSender(ss, xtnData,
                                            ssl_extended_master_secret_xtn,
                                            ssl_SendEmptyExtension);
    }
    return SECSuccess;
}

/* ssl3_ClientSendSignedCertTimestampXtn sends the signed_certificate_timestamp
 * extension for TLS ClientHellos. */

SECStatus
ssl3_ClientSendSignedCertTimestampXtn(const sslSocket *ss,
                                      TLSExtensionData *xtnData,
                                      sslBuffer *buf, PRBool *added)
{
    /* Only send the extension if processing is enabled. */
    if (!ss->opt.enableSignedCertTimestamps) {
        return SECSuccess;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_ClientHandleSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                        SECItem *data)
{
    /* We do not yet know whether we'll be resuming a session or creating
     * a new one, so we keep a pointer to the data in the TLSExtensionData
     * structure. This pointer is only valid in the scope of
     * ssl3_HandleServerHello, and, if not resuming a session, the data is
     * copied once a new session structure has been set up.
     * All parsing is currently left to the application and we accept
     * everything, including empty data.
     */

    SECItem *scts = &xtnData->signedCertTimestamps;
    PORT_Assert(!scts->data && !scts->len);

    if (!data->len) {
        /* Empty extension data: RFC 6962 mandates non-empty contents. */
        return SECFailure;
    }
    *scts = *data;
    /* Keep track of negotiated extensions. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_signed_cert_timestamp_xtn;
    return SECSuccess;
}

SECStatus
ssl3_ServerSendSignedCertTimestampXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                                      sslBuffer *buf, PRBool *added)
{
    const SECItem *scts = &ss->sec.serverCert->signedCertTimestamps;
    SECStatus rv;

    if (!scts->len) {
        /* No timestamps to send */
        return SECSuccess;
    }

    rv = sslBuffer_Append(buf, scts->data, scts->len);
    if (rv != SECSuccess) {
        return SECFailure;
    }

    *added = PR_TRUE;
    return SECSuccess;
}

SECStatus
ssl3_ServerHandleSignedCertTimestampXtn(const sslSocket *ss,
                                        TLSExtensionData *xtnData,
                                        SECItem *data)
{
    if (data->len != 0) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_CLIENT_HELLO);
        return SECFailure;
    }

    xtnData->negotiated[xtnData->numNegotiated++] = ssl_signed_cert_timestamp_xtn;
    PORT_Assert(ss->sec.isServer);
    return ssl3_RegisterExtensionSender(ss, xtnData,
                                        ssl_signed_cert_timestamp_xtn,
                                        ssl3_ServerSendSignedCertTimestampXtn);
}

/* Just make sure that the remote client supports uncompressed points,
 * Since that is all we support.  Disable ECC cipher suites if it doesn't.
 */

SECStatus
ssl3_HandleSupportedPointFormatsXtn(const sslSocket *ss,
                                    TLSExtensionData *xtnData,
                                    SECItem *data)
{
    int i;

    PORT_Assert(ss->version < SSL_LIBRARY_VERSION_TLS_1_3);

    if (data->len < 2 || data->len > 255 || !data->data ||
        data->len != (unsigned int)data->data[0] + 1) {
        ssl3_ExtDecodeError(ss);
        return SECFailure;
    }
    for (i = data->len; --i > 0;) {
        if (data->data[i] == 0) {
            /* indicate that we should send a reply */
            return ssl3_RegisterExtensionSender(
                ss, xtnData, ssl_ec_point_formats_xtn,
                &ssl3_SendSupportedPointFormatsXtn);
        }
    }

    /* Poor client doesn't support uncompressed points.
     *
     * If the client sends the extension and the extension does not contain the
     * uncompressed point format, and the client has used the Supported Groups
     * extension to indicate support for any of the curves defined in this
     * specification, then the server MUST abort the handshake and return an
     * illegal_parameter alert. [RFC8422, Section 5.1.2] */

    ssl3_ExtSendAlert(ss, alert_fatal, illegal_parameter);
    PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);

    return SECFailure;
}

static SECStatus
ssl_UpdateSupportedGroups(sslSocket *ss, SECItem *data)
{
    SECStatus rv;
    PRUint32 list_len;
    unsigned int i;
    const sslNamedGroupDef *enabled[SSL_NAMED_GROUP_COUNT] = { 0 };
    PORT_Assert(SSL_NAMED_GROUP_COUNT == PR_ARRAY_SIZE(enabled));

    if (!data->data || data->len < 4) {
        (void)ssl3_DecodeError(ss);
        return SECFailure;
    }

    /* get the length of elliptic_curve_list */
    rv = ssl3_ConsumeHandshakeNumber(ss, &list_len, 2, &data->data, &data->len);
    if (rv != SECSuccess || data->len != list_len || (data->len % 2) != 0) {
        (void)ssl3_DecodeError(ss);
        return SECFailure;
    }

    /* disable all groups and remember the enabled groups */
    for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
        enabled[i] = ss->namedGroupPreferences[i];
        ss->namedGroupPreferences[i] = NULL;
    }

    /* Read groups from data and enable if in |enabled| */
    while (data->len) {
        const sslNamedGroupDef *group;
        PRUint32 curve_name;
        rv = ssl3_ConsumeHandshakeNumber(ss, &curve_name, 2, &data->data,
                                         &data->len);
        if (rv != SECSuccess) {
            return SECFailure; /* fatal alert already sent */
        }
        group = ssl_LookupNamedGroup(curve_name);
        if (group) {
            for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
                if (enabled[i] && group == enabled[i]) {
                    ss->namedGroupPreferences[i] = enabled[i];
                    break;
                }
            }
        }

        /* "Codepoints in the NamedCurve registry with a high byte of 0x01 (that
         * is, between 256 and 511 inclusive) are set aside for FFDHE groups,"
         * -- https://tools.ietf.org/html/draft-ietf-tls-negotiated-ff-dhe-10
         */

        if ((curve_name & 0xff00) == 0x0100) {
            ss->xtnData.peerSupportsFfdheGroups = PR_TRUE;
        }
    }

    /* Note: if ss->opt.requireDHENamedGroups is set, we disable DHE cipher
     * suites, but we do that in ssl3_config_match(). */

    if (ss->version < SSL_LIBRARY_VERSION_TLS_1_3 &&
        !ss->opt.requireDHENamedGroups && !ss->xtnData.peerSupportsFfdheGroups) {
        /* If we don't require that DHE use named groups, and no FFDHE was
         * included, we pretend that they support all the FFDHE groups we do. */

        for (i = 0; i < SSL_NAMED_GROUP_COUNT; ++i) {
            if (enabled[i] && enabled[i]->keaType == ssl_kea_dh) {
                ss->namedGroupPreferences[i] = enabled[i];
            }
        }
    }

    return SECSuccess;
}

/* Ensure that the curve in our server cert is one of the ones supported
 * by the remote client, and disable all ECC cipher suites if not.
 */

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

    rv = ssl_UpdateSupportedGroups(CONST_CAST(sslSocket, ss), data);
    if (rv != SECSuccess)
        return SECFailure;

    /* TLS 1.3 permits the server to send this extension so make it so. */
    if (ss->sec.isServer && ss->version >= SSL_LIBRARY_VERSION_TLS_1_3) {
        rv = ssl3_RegisterExtensionSender(ss, xtnData, ssl_supported_groups_xtn,
                                          &ssl_SendSupportedGroupsXtn);
        if (rv != SECSuccess) {
            return SECFailure; /* error already set. */
        }
    }

    /* Remember that we negotiated this extension. */
    xtnData->negotiated[xtnData->numNegotiated++] = ssl_supported_groups_xtn;

    return SECSuccess;
}

SECStatus
ssl_HandleRecordSizeLimitXtn(const sslSocket *ss, TLSExtensionData *xtnData,
                             SECItem *data)
{
    SECStatus rv;
    PRUint32 limit;
    PRUint32 maxLimit = (ss->version >= SSL_LIBRARY_VERSION_TLS_1_3)
                            ? (MAX_FRAGMENT_LENGTH + 1)
                            : MAX_FRAGMENT_LENGTH;

    rv = ssl3_ExtConsumeHandshakeNumber(ss, &limit, 2, &data->data, &data->len);
    if (rv != SECSuccess) {
        return SECFailure;
    }
    if (data->len != 0 || limit < 64) {
        ssl3_ExtSendAlert(ss, alert_fatal, decode_error);
        PORT_SetError(SSL_ERROR_RX_MALFORMED_HANDSHAKE);
        return SECFailure;
    }

    if (ss->sec.isServer) {
        rv = ssl3_RegisterExtensionSender(ss, xtnData, ssl_record_size_limit_xtn,
                                          &ssl_SendRecordSizeLimitXtn);
        if (rv != SECSuccess) {
            return SECFailure; /* error already set. */
        }
    } else if (limit > maxLimit) {
        /* The client can sensibly check the maximum. */
--> --------------------

--> maximum size reached

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

Messung V0.5
C=87 H=84 G=85

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