1
0
Fork 0
firefox/security/nss/lib/ssl/sslexp.h
Daniel Baumann 5e9a113729
Adding upstream version 140.0.
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
2025-06-25 09:37:52 +02:00

1112 lines
56 KiB
C
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
* This file contains prototypes for experimental SSL functions.
*
* 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/. */
#ifndef __sslexp_h_
#define __sslexp_h_
#include "ssl.h"
#include "sslerr.h"
#include "pk11hpke.h"
SEC_BEGIN_PROTOS
/* The functions in this header file are not guaranteed to remain available in
* future NSS versions. Code that uses these functions needs to safeguard
* against the function not being available. */
#define SSL_EXPERIMENTAL_API(name, arglist, args) \
(SSL_GetExperimentalAPI(name) \
? ((SECStatus(*) arglist)SSL_GetExperimentalAPI(name))args \
: SECFailure)
#define SSL_DEPRECATED_EXPERIMENTAL_API \
(PR_SetError(SSL_ERROR_UNSUPPORTED_EXPERIMENTAL_API, 0), SECFailure)
/*
* SSL_GetExtensionSupport() returns whether NSS supports a particular TLS
* extension.
*
* - ssl_ext_none indicates that NSS does not support the extension and
* extension hooks can be installed.
*
* - ssl_ext_native indicates that NSS supports the extension natively, but
* allows an application to override that support and install its own
* extension hooks.
*
* - ssl_ext_native_only indicates that NSS supports the extension natively
* and does not permit custom extension hooks to be installed. These
* extensions are critical to the functioning of NSS.
*/
typedef enum {
ssl_ext_none,
ssl_ext_native,
ssl_ext_native_only
} SSLExtensionSupport;
#define SSL_GetExtensionSupport(extension, support) \
SSL_EXPERIMENTAL_API("SSL_GetExtensionSupport", \
(PRUint16 _extension, \
SSLExtensionSupport * _support), \
(extension, support))
/*
* Custom extension hooks.
*
* The SSL_InstallExtensionHooks() registers two callback functions for use
* with the identified extension type.
*
* Installing extension hooks disables the checks in TLS 1.3 that ensure that
* extensions are only added to the correct messages. The application is
* responsible for ensuring that extensions are only sent with the right message
* or messages.
*
* Installing an extension handler does not disable checks for whether an
* extension can be used in a message that is a response to an extension in
* another message. Extensions in ServerHello, EncryptedExtensions and the
* server Certificate messages are rejected unless the client sends an extension
* in the ClientHello. Similarly, a client Certificate message cannot contain
* extensions that don't appear in a CertificateRequest (in TLS 1.3).
*
* Setting both |writer| and |handler| to NULL removes any existing hooks for
* that extension.
*
* == SSLExtensionWriter
*
* An SSLExtensionWriter function is responsible for constructing the contents
* of an extension. This function is called during the construction of all
* handshake messages where an extension might be included.
*
* - The |fd| argument is the socket file descriptor.
*
* - The |message| argument is the TLS handshake message type. The writer will
* be called for every handshake message that NSS sends. Most extensions
* should only be sent in a subset of messages. NSS doesnt check that
* extension writers dont violate protocol rules regarding which message an
* extension can be sent in.
*
* - The |data| argument is a pointer to a buffer that should be written to with
* any data for the extension.
*
* - The |len| argument is an outparam indicating how many bytes were written to
* |data|. The value referenced by |len| is initialized to zero, so an
* extension that is empty does not need to write to this value.
*
* - The |maxLen| indicates the maximum number of bytes that can be written to
* |data|.
*
* - The |arg| argument is the value of the writerArg that was passed during
* installation.
*
* An SSLExtensionWriter function returns PR_TRUE if an extension should be
* written, and PR_FALSE otherwise.
*
* If there is an error, return PR_FALSE; if the error is truly fatal, the
* application can mark the connection as failed. However, recursively calling
* functions that alter the file descriptor in the callback - such as PR_Close()
* - should be avoided.
*
* Note: The ClientHello message can be sent twice in TLS 1.3. An
* SSLExtensionWriter will be called twice with the same arguments in that case;
* NSS does not distinguish between a first and second ClientHello. It is up to
* the application to track this if it needs to act differently each time. In
* most cases the correct behaviour is to provide an identical extension on each
* invocation.
*
* == SSLExtensionHandler
*
* An SSLExtensionHandler function consumes a handshake message. This function
* is called when an extension is present.
*
* - The |fd| argument is the socket file descriptor.
*
* - The |message| argument is the TLS handshake message type. This can be used
* to validate that the extension was included in the correct handshake
* message.
*
* - The |data| argument points to the contents of the extension.
*
* - The |len| argument contains the length of the extension.
*
* - The |alert| argument is an outparam that allows an application to choose
* which alert is sent in the case of a fatal error.
*
* - The |arg| argument is the value of the handlerArg that was passed during
* installation.
*
* An SSLExtensionHandler function returns SECSuccess when the extension is
* process successfully. It can return SECFailure to cause the handshake to
* fail. If the value of alert is written to, NSS will generate a fatal alert
* using the provided alert code. The value of |alert| is otherwise not used.
*/
typedef PRBool(PR_CALLBACK *SSLExtensionWriter)(
PRFileDesc *fd, SSLHandshakeType message,
PRUint8 *data, unsigned int *len, unsigned int maxLen, void *arg);
typedef SECStatus(PR_CALLBACK *SSLExtensionHandler)(
PRFileDesc *fd, SSLHandshakeType message,
const PRUint8 *data, unsigned int len,
SSLAlertDescription *alert, void *arg);
#define SSL_InstallExtensionHooks(fd, extension, writer, writerArg, \
handler, handlerArg) \
SSL_EXPERIMENTAL_API("SSL_InstallExtensionHooks", \
(PRFileDesc * _fd, PRUint16 _extension, \
SSLExtensionWriter _writer, void *_writerArg, \
SSLExtensionHandler _handler, void *_handlerArg), \
(fd, extension, writer, writerArg, \
handler, handlerArg))
/*
* Create an anti-replay context for supporting 0-RTT in TLS 1.3 on servers.
*
* To use 0-RTT on a server, you must create an anti-replay context using
* SSL_CreateAntiReplayContext and set that on the socket with
* SSL_SetAntiReplayContext. Failing to set a context on the server will result
* in all 0-RTT being rejected. Connections will complete, but early data will
* be rejected.
*
* Anti-replay contexts are reference counted and are released with
* SSL_ReleaseAntiReplayContext.
*
* NSS uses a Bloom filter to track the ClientHello messages that it receives
* (specifically, it uses the PSK binder). This function initializes a pair of
* Bloom filters. The two filters are alternated over time, with new
* ClientHello messages recorded in the current filter and, if they are not
* already present, being checked against the previous filter. If the
* ClientHello is found, then early data is rejected, but the handshake is
* allowed to proceed.
*
* The false-positive probability of Bloom filters means that some valid
* handshakes will be marked as potential replays. Early data will be rejected
* for a false positive. To minimize this and to allow a trade-off of space
* against accuracy, the size of the Bloom filter can be set by this function.
*
* The first tuning parameter to consider is |window|, which determines the
* window over which ClientHello messages will be tracked. This also causes
* early data to be rejected if a ClientHello contains a ticket age parameter
* that is outside of this window (see Section 8.3 of RFC 8446 for details).
* Set |window| to account for any potential sources of clock error. |window|
* is the entire width of the window, which is symmetrical. Therefore to allow
* 5 seconds of clock error in both directions, set the value to 10 seconds
* (i.e., 10 * PR_USEC_PER_SEC).
*
* After calling this function, early data will be rejected until |window|
* elapses. This prevents replay across crashes and restarts. Only call this
* function once to avoid inadvertently disabling 0-RTT (use PR_CallOnce() to
* avoid this problem).
*
* The primary tuning parameter is |bits| which determines the amount of memory
* allocated to each Bloom filter. NSS will allocate two Bloom filters, each
* |2^(bits - 3)| octets in size. The value of |bits| is primarily driven by
* the number of connections that are expected in any time window. Note that
* this needs to account for there being two filters both of which have
* (presumably) independent false positive rates. The following formulae can be
* used to find a value of |bits| and |k| given a chosen false positive
* probability |p| and the number of requests expected in a given window |n|:
*
* bits = log2(n) + log2(-ln(1 - sqrt(1 - p))) + 1.0575327458897952
* k = -log2(p)
*
* ... where log2 and ln are base 2 and e logarithms respectively. For a target
* false positive rate of 1% and 1000 handshake attempts, this produces bits=14
* and k=7. This results in two Bloom filters that are 2kB each in size. Note
* that rounding |k| and |bits| up causes the false positive probability for
* these values to be a much lower 0.123%.
*
* IMPORTANT: This anti-replay scheme has several weaknesses. See the TLS 1.3
* specification for the details of the generic problems with this technique.
*
* In addition to the generic anti-replay weaknesses, the state that the server
* maintains is in local memory only. Servers that operate in a cluster, even
* those that use shared memory for tickets, will not share anti-replay state.
* Early data can be replayed at least once with every server instance that will
* accept tickets that are encrypted with the same key.
*/
typedef struct SSLAntiReplayContextStr SSLAntiReplayContext;
#define SSL_CreateAntiReplayContext(now, window, k, bits, ctx) \
SSL_EXPERIMENTAL_API("SSL_CreateAntiReplayContext", \
(PRTime _now, PRTime _window, \
unsigned int _k, unsigned int _bits, \
SSLAntiReplayContext **_ctx), \
(now, window, k, bits, ctx))
#define SSL_SetAntiReplayContext(fd, ctx) \
SSL_EXPERIMENTAL_API("SSL_SetAntiReplayContext", \
(PRFileDesc * _fd, SSLAntiReplayContext * _ctx), \
(fd, ctx))
#define SSL_ReleaseAntiReplayContext(ctx) \
SSL_EXPERIMENTAL_API("SSL_ReleaseAntiReplayContext", \
(SSLAntiReplayContext * _ctx), \
(ctx))
/*
* This function allows a server application to generate a session ticket that
* will embed the provided token.
*
* This function will cause a NewSessionTicket message to be sent by a server.
* This happens even if SSL_ENABLE_SESSION_TICKETS is disabled. This allows a
* server to suppress the usually automatic generation of a session ticket at
* the completion of the handshake - which do not include any token - and to
* control when session tickets are transmitted.
*
* This function will fail unless the socket has an active TLS 1.3 session.
* Earlier versions of TLS do not support the spontaneous sending of the
* NewSessionTicket message. It will also fail when external PSK
* authentication has been negotiated.
*/
#define SSL_SendSessionTicket(fd, appToken, appTokenLen) \
SSL_EXPERIMENTAL_API("SSL_SendSessionTicket", \
(PRFileDesc * _fd, const PRUint8 *_appToken, \
unsigned int _appTokenLen), \
(fd, appToken, appTokenLen))
/*
* A stateless retry handler gives an application some control over NSS handling
* of ClientHello messages.
*
* SSL_HelloRetryRequestCallback() installs a callback that allows an
* application to control how NSS sends HelloRetryRequest messages. This
* handler is only used on servers and will only be called if the server selects
* TLS 1.3. Support for older TLS versions could be added in other releases.
*
* The SSLHelloRetryRequestCallback is invoked during the processing of a
* TLS 1.3 ClientHello message. It takes the following arguments:
*
* - |firstHello| indicates if the NSS believes that this is an initial
* ClientHello. An initial ClientHello will never include a cookie extension,
* though it may contain a session ticket.
*
* - |clientToken| includes a token previously provided by the application. If
* |clientTokenLen| is 0, then |clientToken| may be NULL.
*
* - If |firstHello| is PR_FALSE, the value that was provided in the
* |retryToken| outparam of previous invocations of this callback will be
* present here.
*
* - If |firstHello| is PR_TRUE, and the handshake is resuming a session, then
* this will contain any value that was passed in the |token| parameter of
* SSL_SendNewSessionTicket() method (see below). If this is not resuming a
* session, then the token will be empty (and this value could be NULL).
*
* - |clientTokenLen| is the length of |clientToken|.
*
* - |retryToken| is an item that callback can write to. This provides NSS with
* a token. This token is encrypted and integrity protected and embedded in
* the cookie extension of a HelloRetryRequest. The value of this field is
* only used if the handler returns ssl_stateless_retry_check. NSS allocates
* space for this value.
*
* - |retryTokenLen| is an outparam for the length of the token. If this value
* is not set, or set to 0, an empty token will be sent.
*
* - |retryTokenMax| is the size of the space allocated for retryToken. An
* application cannot write more than this many bytes to retryToken.
*
* - |arg| is the same value that was passed to
* SSL_InstallStatelessRetryHandler().
*
* The handler can validate any the value of |clientToken|, query the socket
* status (using SSL_GetPreliminaryChannelInfo() for example) and decide how to
* proceed:
*
* - Returning ssl_hello_retry_fail causes the handshake to fail. This might be
* used if the token is invalid or the application wishes to abort the
* handshake.
*
* - Returning ssl_hello_retry_accept causes the handshake to proceed.
*
* - Returning ssl_hello_retry_request causes NSS to send a HelloRetryRequest
* message and request a second ClientHello. NSS generates a cookie extension
* and embeds the value of |retryToken|. The value of |retryToken| value may
* be left empty if the application does not require any additional context to
* validate a second ClientHello attempt. This return code cannot be used to
* reject a second ClientHello (i.e., when firstHello is PR_FALSE); NSS will
* abort the handshake if this value is returned from a second call.
*
* - Returning ssl_hello_retry_reject_0rtt causes NSS to proceed normally, but
* to reject 0-RTT. Use this if there is something in the token that
* indicates that 0-RTT might be unsafe.
*
* An application that chooses to perform a stateless retry can discard the
* server socket. All necessary state to continue the TLS handshake will be
* included in the cookie extension. This makes it possible to use a new socket
* to handle the remainder of the handshake. The existing socket can be safely
* discarded.
*
* If the same socket is retained, the information in the cookie will be checked
* for consistency against the existing state of the socket. Any discrepancy
* will result in the connection being closed.
*
* Tokens should be kept as small as possible. NSS sets a limit on the size of
* tokens, which it passes in |retryTokenMax|. Depending on circumstances,
* observing a smaller limit might be desirable or even necessary. For
* instance, having HelloRetryRequest and ClientHello fit in a single packet has
* significant performance benefits.
*/
typedef enum {
ssl_hello_retry_fail,
ssl_hello_retry_accept,
ssl_hello_retry_request,
ssl_hello_retry_reject_0rtt
} SSLHelloRetryRequestAction;
typedef SSLHelloRetryRequestAction(PR_CALLBACK *SSLHelloRetryRequestCallback)(
PRBool firstHello, const PRUint8 *clientToken, unsigned int clientTokenLen,
PRUint8 *retryToken, unsigned int *retryTokenLen, unsigned int retryTokMax,
void *arg);
#define SSL_HelloRetryRequestCallback(fd, cb, arg) \
SSL_EXPERIMENTAL_API("SSL_HelloRetryRequestCallback", \
(PRFileDesc * _fd, \
SSLHelloRetryRequestCallback _cb, void *_arg), \
(fd, cb, arg))
/* Update traffic keys (TLS 1.3 only).
*
* The |requestUpdate| flag determines whether to request an update from the
* remote peer.
*/
#define SSL_KeyUpdate(fd, requestUpdate) \
SSL_EXPERIMENTAL_API("SSL_KeyUpdate", \
(PRFileDesc * _fd, PRBool _requestUpdate), \
(fd, requestUpdate))
/* This function allows a server application to trigger
* re-authentication (TLS 1.3 only) after handshake.
*
* This function will cause a CertificateRequest message to be sent by
* a server. This can be called once at a time, and is not allowed
* until an answer is received.
*
* This function is not allowed for use with DTLS or when external
* PSK authentication has been negotiated. SECFailure is returned
* in both cases.
*
* The AuthCertificateCallback is called when the answer is received.
* If the answer is accepted by the server, the value returned by
* SSL_PeerCertificate() is replaced. If you need to remember all the
* certificates, you will need to call SSL_PeerCertificate() and save
* what you get before calling this.
*
* If the AuthCertificateCallback returns SECFailure, the connection
* is aborted.
*/
#define SSL_SendCertificateRequest(fd) \
SSL_EXPERIMENTAL_API("SSL_SendCertificateRequest", \
(PRFileDesc * _fd), \
(fd))
/*
* Session cache API.
*/
/*
* Information that can be retrieved about a resumption token.
* See SSL_GetResumptionTokenInfo for details about how to use this API.
* Note that peerCert points to a certificate in the NSS database and must be
* copied by the application if it should be used after NSS shutdown or after
* calling SSL_DestroyResumptionTokenInfo.
*/
typedef struct SSLResumptionTokenInfoStr {
PRUint16 length;
CERTCertificate *peerCert;
PRUint8 *alpnSelection;
PRUint32 alpnSelectionLen;
PRUint32 maxEarlyDataSize;
PRTime expirationTime; /* added in NSS 3.41 */
} SSLResumptionTokenInfo;
/*
* Allows applications to retrieve information about a resumption token.
* This does not require a TLS session.
*
* - The |tokenData| argument is a pointer to the resumption token as byte array
* of length |tokenLen|.
* - The |token| argument is a pointer to a SSLResumptionTokenInfo struct of
* of |len|. The struct gets filled by this function.
* See SSL_DestroyResumptionTokenInfo for information about how to manage the
* |token| memory.
*/
#define SSL_GetResumptionTokenInfo(tokenData, tokenLen, token, len) \
SSL_EXPERIMENTAL_API("SSL_GetResumptionTokenInfo", \
(const PRUint8 *_tokenData, unsigned int _tokenLen, \
SSLResumptionTokenInfo *_token, PRUintn _len), \
(tokenData, tokenLen, token, len))
/*
* SSL_GetResumptionTokenInfo allocates memory in order to populate |tokenInfo|.
* Any SSLResumptionTokenInfo struct filled with SSL_GetResumptionTokenInfo
* has to be freed with SSL_DestroyResumptionTokenInfo.
*/
#define SSL_DestroyResumptionTokenInfo(tokenInfo) \
SSL_EXPERIMENTAL_API( \
"SSL_DestroyResumptionTokenInfo", \
(SSLResumptionTokenInfo * _tokenInfo), \
(tokenInfo))
/*
* This is the function signature for function pointers used as resumption
* token callback. The caller has to copy the memory at |resumptionToken| with
* length |len| before returning.
*
* - The |fd| argument is the socket file descriptor.
* - The |resumptionToken| is a pointer to the resumption token as byte array
* of length |len|.
* - The |ctx| is a void pointer to the context set by the application in
* SSL_SetResumptionTokenCallback.
*/
typedef SECStatus(PR_CALLBACK *SSLResumptionTokenCallback)(
PRFileDesc *fd, const PRUint8 *resumptionToken, unsigned int len,
void *ctx);
/*
* This allows setting a callback for external session caches to store
* resumption tokens.
*
* - The |fd| argument is the socket file descriptor.
* - The |cb| is a function pointer to an implementation of
* SSLResumptionTokenCallback.
* - The |ctx| is a pointer to some application specific context, which is
* returned when |cb| is called.
*/
#define SSL_SetResumptionTokenCallback(fd, cb, ctx) \
SSL_EXPERIMENTAL_API( \
"SSL_SetResumptionTokenCallback", \
(PRFileDesc * _fd, SSLResumptionTokenCallback _cb, void *_ctx), \
(fd, cb, ctx))
/*
* This allows setting a resumption token for a session.
* The function returns SECSuccess iff the resumption token can be used,
* SECFailure in any other case. The caller should remove the |token| from its
* cache when the function returns SECFailure.
*
* - The |fd| argument is the socket file descriptor.
* - The |token| is a pointer to the resumption token as byte array
* of length |len|.
*/
#define SSL_SetResumptionToken(fd, token, len) \
SSL_EXPERIMENTAL_API( \
"SSL_SetResumptionToken", \
(PRFileDesc * _fd, const PRUint8 *_token, const unsigned int _len), \
(fd, token, len))
/* TLS 1.3 allows a server to set a limit on the number of bytes of early data
* that can be received. This allows that limit to be set. This function has no
* effect on a client. */
#define SSL_SetMaxEarlyDataSize(fd, size) \
SSL_EXPERIMENTAL_API("SSL_SetMaxEarlyDataSize", \
(PRFileDesc * _fd, PRUint32 _size), \
(fd, size))
/* Client:
* If |enabled|, a GREASE ECH extension will be sent in every ClientHello,
* unless a valid and supported ECHConfig is configured to the socket
* (in which case real ECH takes precedence). If |!enabled|, it is not sent.
*
* Server:
* If |enabled|, a GREASE ECH extensions will be sent in every HelloRetryRequest,
* provided that the corresponding ClientHello contained an ECH extension. If ECH
* is enabled, the real ECH HRR extension takes precedence.
*/
#define SSL_EnableTls13GreaseEch(fd, enabled) \
SSL_EXPERIMENTAL_API("SSL_EnableTls13GreaseEch", \
(PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
/*
* Client:
* When sending a GREASE ECH extension in a ClientHello, pad it as though the
* hypothetical ECHConfig had |maximum_name_length| equal to |size|. |size| may
* vary between 1 and 255 and defaults to 100.
*
* Server:
* Has no effect.
*/
#define SSL_SetTls13GreaseEchSize(fd, size) \
SSL_EXPERIMENTAL_API("SSL_SetTls13GreaseEchSize", \
(PRFileDesc * _fd, PRUint8 _size), (fd, size))
/* If |enabled|, a server receiving a Client Hello containing an encrypted_client_hello
* of type inner will respond with the ECH
* acceptance signal. This signals the client to continue with the inner
* transcript rather than outer. */
#define SSL_EnableTls13BackendEch(fd, enabled) \
SSL_EXPERIMENTAL_API("SSL_EnableTls13BackendEch", \
(PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
/* This allows an extension writer to supply different values for inner and
* outer ClientHello when using encrypted ClientHello.
*
* When enabled, each extension writer can be called more than once for the same
* message; it must provide the same response when called for the same message
* type. When calling the writer to construct the outer ClientHello, the
* function will be called with ssl_hs_ech_outer_client_hello as the message
* type (a value from outside the range of valid TLS handshake messages).
*
* When disabled, the extension writer is called once for the outer ClientHello
* and the value is copied to the inner ClientHello.
*
* Enabling this affects all extension writers. The order in which extension
* writers are added is also important. Any extension writer that writes
* different values for inner and outer ClientHello will prevent later
* extensions from being compressed.
*/
#define SSL_CallExtensionWriterOnEchInner(fd, enabled) \
SSL_EXPERIMENTAL_API("SSL_CallExtensionWriterOnEchInner", \
(PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
/* Called by the client after an initial ECH connection fails with
* SSL_ERROR_ECH_RETRY_WITH_ECH. Returns compatible ECHConfigs, which
* are configured via SetClientEchConfigs for an ECH retry attempt.
* These configs MUST NOT be used for more than the single retry
* attempt. Subsequent connections MUST use advertised ECHConfigs. */
#define SSL_GetEchRetryConfigs(fd, out) \
SSL_EXPERIMENTAL_API("SSL_GetEchRetryConfigs", \
(PRFileDesc * _fd, \
SECItem * _out), \
(fd, out))
/* Called to remove all ECHConfigs from a socket (fd). */
#define SSL_RemoveEchConfigs(fd) \
SSL_EXPERIMENTAL_API("SSL_RemoveEchConfigs", \
(PRFileDesc * _fd), \
(fd))
/* Set the ECHConfig and key pair on a socket (server side)
*
* fd -- the socket
* pubKey -- the server's SECKEYPublicKey for HPKE/ECH.
* privateKey -- the server's SECKEYPrivateKey for HPKE/ECH.
* record/recordLen -- the encoded DNS record (not base64)
*/
#define SSL_SetServerEchConfigs(fd, pubKey, \
privKey, record, recordLen) \
SSL_EXPERIMENTAL_API("SSL_SetServerEchConfigs", \
(PRFileDesc * _fd, \
const SECKEYPublicKey *_pubKey, \
const SECKEYPrivateKey *_privKey, \
const PRUint8 *_record, unsigned int _recordLen), \
(fd, pubKey, privKey, \
record, recordLen))
/* Set ECHConfig(s) on a client. The first supported ECHConfig will be used.
*
* fd -- the socket
* echConfigs/echConfigsLen -- the ECHConfigs structure (not base64)
*/
#define SSL_SetClientEchConfigs(fd, echConfigs, echConfigsLen) \
SSL_EXPERIMENTAL_API("SSL_SetClientEchConfigs", \
(PRFileDesc * _fd, \
const PRUint8 *_echConfigs, \
unsigned int _echConfigsLen), \
(fd, echConfigs, echConfigsLen))
/*
* Generate an encoded ECHConfig structure (presumably server side).
*
* configId -- an identifier for the configuration.
* publicName -- the public_name value to be placed in SNI.
* maxNameLen -- the maximum length of protected names
* kemId -- the HKPE KEM ID value
* pubKey -- the public key for the key pair
* hpkeSuites -- the HPKE cipher suites that can be used
* hpkeSuitesCount -- the number of suites in hpkeSuites
* out/outlen/maxlen -- where to output the data
*/
typedef struct HpkeSymmetricSuiteStr {
HpkeKdfId kdfId;
HpkeAeadId aeadId;
} HpkeSymmetricSuite;
#define SSL_EncodeEchConfigId(configId, publicName, maxNameLen, \
kemId, pubKey, hpkeSuites, hpkeSuiteCount, \
out, outlen, maxlen) \
SSL_EXPERIMENTAL_API("SSL_EncodeEchConfigId", \
(PRUint8 _configId, const char *_publicName, \
unsigned int _maxNameLen, HpkeKemId _kemId, \
const SECKEYPublicKey *_pubKey, \
const HpkeSymmetricSuite *_hpkeSuites, \
unsigned int _hpkeSuiteCount, \
PRUint8 *_out, unsigned int *_outlen, \
unsigned int _maxlen), \
(configId, publicName, maxNameLen, \
kemId, pubKey, hpkeSuites, hpkeSuiteCount, \
out, outlen, maxlen))
/* SSL_SetSecretCallback installs a callback that TLS calls when it installs new
* traffic secrets.
*
* SSLSecretCallback is called with the current epoch and the corresponding
* secret; this matches the epoch used in DTLS 1.3, even if the socket is
* operating in stream mode:
*
* - client_early_traffic_secret corresponds to epoch 1
* - {client|server}_handshake_traffic_secret is epoch 2
* - {client|server}_application_traffic_secret_{N} is epoch 3+N
*
* The callback is invoked separately for read secrets (client secrets on the
* server; server secrets on the client), and write secrets.
*
* This callback is only called if (D)TLS 1.3 is negotiated.
*/
typedef void(PR_CALLBACK *SSLSecretCallback)(
PRFileDesc *fd, PRUint16 epoch, SSLSecretDirection dir, PK11SymKey *secret,
void *arg);
#define SSL_SecretCallback(fd, cb, arg) \
SSL_EXPERIMENTAL_API("SSL_SecretCallback", \
(PRFileDesc * _fd, SSLSecretCallback _cb, void *_arg), \
(fd, cb, arg))
/* SSL_RecordLayerWriteCallback() is used to replace the TLS record layer. This
* function installs a callback that TLS calls when it would otherwise encrypt
* and write a record to the underlying NSPR IO layer. The application is
* responsible for ensuring that these records are encrypted and written.
*
* Calling this API also disables reads from the underlying NSPR layer. The
* application is expected to push data when it is available using
* SSL_RecordLayerData().
*
* When data would be written, the provided SSLRecordWriteCallback with the
* epoch, TLS content type, and the data. The data provided to the callback is
* not split into record-sized writes. If the callback returns SECFailure, the
* write will be considered to have failed; in particular, PR_WOULD_BLOCK_ERROR
* is not handled specially.
*
* If TLS 1.3 is in use, the epoch indicates the expected level of protection
* that the record would receive, this matches that used in DTLS 1.3:
*
* - epoch 0 corresponds to no record protection
* - epoch 1 corresponds to 0-RTT
* - epoch 2 corresponds to TLS handshake
* - epoch 3 and higher are application data
*
* Prior versions of TLS use epoch 1 and higher for application data.
*
* This API is not supported for DTLS.
*/
typedef SECStatus(PR_CALLBACK *SSLRecordWriteCallback)(
PRFileDesc *fd, PRUint16 epoch, SSLContentType contentType,
const PRUint8 *data, unsigned int len, void *arg);
#define SSL_RecordLayerWriteCallback(fd, writeCb, arg) \
SSL_EXPERIMENTAL_API("SSL_RecordLayerWriteCallback", \
(PRFileDesc * _fd, SSLRecordWriteCallback _wCb, \
void *_arg), \
(fd, writeCb, arg))
/* SSL_RecordLayerData() is used to provide new data to TLS. The application
* indicates the epoch (see the description of SSL_RecordLayerWriteCallback()),
* content type, and the data that was received. The application is responsible
* for removing any encryption or other protection before passing data to this
* function.
*
* This returns SECSuccess if the data was successfully processed. If this
* function is used to drive the handshake and the caller needs to know when the
* handshake is complete, a call to SSL_ForceHandshake will return SECSuccess
* when the handshake is complete.
*
* This API is not supported for DTLS sockets.
*/
#define SSL_RecordLayerData(fd, epoch, ct, data, len) \
SSL_EXPERIMENTAL_API("SSL_RecordLayerData", \
(PRFileDesc * _fd, PRUint16 _epoch, \
SSLContentType _contentType, \
const PRUint8 *_data, unsigned int _len), \
(fd, epoch, ct, data, len))
/*
* SSL_GetCurrentEpoch() returns the read and write epochs that the socket is
* currently using. NULL values for readEpoch or writeEpoch are ignored.
*
* See SSL_RecordLayerWriteCallback() for details on epochs.
*/
#define SSL_GetCurrentEpoch(fd, readEpoch, writeEpoch) \
SSL_EXPERIMENTAL_API("SSL_GetCurrentEpoch", \
(PRFileDesc * _fd, PRUint16 * _readEpoch, \
PRUint16 * _writeEpoch), \
(fd, readEpoch, writeEpoch))
/*
* The following AEAD functions expose an AEAD primitive that uses a ciphersuite
* to set parameters. The ciphersuite determines the Hash function used by
* HKDF, the AEAD function, and the size of key and IV. This is only supported
* for TLS 1.3.
*
* The key and IV are generated using the TLS KDF with a custom label. That is
* HKDF-Expand-Label(secret, labelPrefix + " key" or " iv", "", L).
*
* The encrypt and decrypt functions use a nonce construction identical to that
* used in TLS. The lower bits of the IV are XORed with the 64-bit counter to
* produce the nonce. Otherwise, this is an AEAD interface similar to that
* described in RFC 5116.
*
* Note: SSL_MakeAead internally calls SSL_MakeVariantAead with a variant of
* "stream", behaving as noted above. If "datagram" variant is passed instead,
* the Label prefix used in HKDF-Expand is "dtls13" instead of "tls13 ". See
* 7.1 of RFC 8446 and draft-ietf-tls-dtls13-34. */
typedef struct SSLAeadContextStr SSLAeadContext;
#define SSL_MakeAead(version, cipherSuite, secret, \
labelPrefix, labelPrefixLen, ctx) \
SSL_EXPERIMENTAL_API("SSL_MakeAead", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _secret, \
const char *_labelPrefix, \
unsigned int _labelPrefixLen, \
SSLAeadContext **_ctx), \
(version, cipherSuite, secret, \
labelPrefix, labelPrefixLen, ctx))
#define SSL_MakeVariantAead(version, cipherSuite, variant, secret, \
labelPrefix, labelPrefixLen, ctx) \
SSL_EXPERIMENTAL_API("SSL_MakeVariantAead", \
(PRUint16 _version, PRUint16 _cipherSuite, \
SSLProtocolVariant _variant, \
PK11SymKey * _secret, \
const char *_labelPrefix, \
unsigned int _labelPrefixLen, \
SSLAeadContext **_ctx), \
(version, cipherSuite, variant, secret, \
labelPrefix, labelPrefixLen, ctx))
#define SSL_AeadEncrypt(ctx, counter, aad, aadLen, in, inLen, \
output, outputLen, maxOutputLen) \
SSL_EXPERIMENTAL_API("SSL_AeadEncrypt", \
(const SSLAeadContext *_ctx, PRUint64 _counter, \
const PRUint8 *_aad, unsigned int _aadLen, \
const PRUint8 *_in, unsigned int _inLen, \
PRUint8 *_out, unsigned int *_outLen, \
unsigned int _maxOut), \
(ctx, counter, aad, aadLen, in, inLen, \
output, outputLen, maxOutputLen))
#define SSL_AeadDecrypt(ctx, counter, aad, aadLen, in, inLen, \
output, outputLen, maxOutputLen) \
SSL_EXPERIMENTAL_API("SSL_AeadDecrypt", \
(const SSLAeadContext *_ctx, PRUint64 _counter, \
const PRUint8 *_aad, unsigned int _aadLen, \
const PRUint8 *_in, unsigned int _inLen, \
PRUint8 *_output, unsigned int *_outLen, \
unsigned int _maxOut), \
(ctx, counter, aad, aadLen, in, inLen, \
output, outputLen, maxOutputLen))
#define SSL_DestroyAead(ctx) \
SSL_EXPERIMENTAL_API("SSL_DestroyAead", \
(SSLAeadContext * _ctx), \
(ctx))
/* SSL_HkdfExtract and SSL_HkdfExpandLabel implement the functions from TLS,
* using the version and ciphersuite to set parameters. This allows callers to
* use these TLS functions as a KDF. This is only supported for TLS 1.3.
*
* SSL_HkdfExtract produces a key with a mechanism that is suitable for input to
* SSL_HkdfExpandLabel (and SSL_HkdfExpandLabelWithMech). */
#define SSL_HkdfExtract(version, cipherSuite, salt, ikm, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfExtract", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _salt, PK11SymKey * _ikm, \
PK11SymKey * *_keyp), \
(version, cipherSuite, salt, ikm, keyp))
/* SSL_HkdfExpandLabel and SSL_HkdfVariantExpandLabel produce a key with a
* mechanism that is suitable for input to SSL_HkdfExpandLabel or SSL_MakeAead.
*
* Note: SSL_HkdfVariantExpandLabel internally calls SSL_HkdfExpandLabel with
* a default "stream" variant. If "datagram" variant is passed instead, the
* Label prefix used in HKDF-Expand is "dtls13" instead of "tls13 ". See 7.1 of
* RFC 8446 and draft-ietf-tls-dtls13-34. */
#define SSL_HkdfExpandLabel(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfExpandLabel", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _prk, \
const PRUint8 *_hsHash, unsigned int _hsHashLen, \
const char *_label, unsigned int _labelLen, \
PK11SymKey **_keyp), \
(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, keyp))
#define SSL_HkdfVariantExpandLabel(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, variant, \
keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfVariantExpandLabel", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _prk, \
const PRUint8 *_hsHash, unsigned int _hsHashLen, \
const char *_label, unsigned int _labelLen, \
SSLProtocolVariant _variant, \
PK11SymKey **_keyp), \
(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, variant, \
keyp))
/* SSL_HkdfExpandLabelWithMech and SSL_HkdfVariantExpandLabelWithMech use the KDF
* from the selected TLS version and cipher suite, as with the other calls, but
* the provided mechanism and key size. This allows the key to be used more widely.
*
* Note: SSL_HkdfExpandLabelWithMech internally calls SSL_HkdfVariantExpandLabelWithMech
* with a default "stream" variant. If "datagram" variant is passed instead, the
* Label prefix used in HKDF-Expand is "dtls13" instead of "tls13 ". See 7.1 of
* RFC 8446 and draft-ietf-tls-dtls13-34. */
#define SSL_HkdfExpandLabelWithMech(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, \
mech, keySize, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfExpandLabelWithMech", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _prk, \
const PRUint8 *_hsHash, unsigned int _hsHashLen, \
const char *_label, unsigned int _labelLen, \
CK_MECHANISM_TYPE _mech, unsigned int _keySize, \
PK11SymKey **_keyp), \
(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, \
mech, keySize, keyp))
#define SSL_HkdfVariantExpandLabelWithMech(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, \
mech, keySize, variant, keyp) \
SSL_EXPERIMENTAL_API("SSL_HkdfVariantExpandLabelWithMech", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _prk, \
const PRUint8 *_hsHash, unsigned int _hsHashLen, \
const char *_label, unsigned int _labelLen, \
CK_MECHANISM_TYPE _mech, unsigned int _keySize, \
SSLProtocolVariant _variant, \
PK11SymKey **_keyp), \
(version, cipherSuite, prk, \
hsHash, hsHashLen, label, labelLen, \
mech, keySize, variant, keyp))
/* SSL_SetTimeFunc overrides the default time function (PR_Now()) and provides
* an alternative source of time for the socket. This is used in testing, and in
* applications that need better control over how the clock is accessed. Set the
* function to NULL to use PR_Now().*/
typedef PRTime(PR_CALLBACK *SSLTimeFunc)(void *arg);
#define SSL_SetTimeFunc(fd, f, arg) \
SSL_EXPERIMENTAL_API("SSL_SetTimeFunc", \
(PRFileDesc * _fd, SSLTimeFunc _f, void *_arg), \
(fd, f, arg))
/* Create a delegated credential (DC) for the draft-ietf-tls-subcerts extension
* using the given certificate |cert| and its signing key |certPriv| and write
* the serialized DC to |out|. The
* parameters are:
* - the DC public key |dcPub|;
* - the DC signature scheme |dcCertVerifyAlg|, used to verify the handshake.
* - the DC time-to-live |dcValidFor|, the number of seconds from now for which
* the DC should be valid; and
* - the current time |now|.
*
* The signing algorithm used to verify the DC signature is deduced from
* |cert|.
*
* It's the caller's responsibility to ensure the input parameters are all
* valid. This procedure is meant primarily for testing; for this purpose it is
* useful to do no validation.
*/
#define SSL_DelegateCredential(cert, certPriv, dcPub, dcCertVerifyAlg, \
dcValidFor, now, out) \
SSL_EXPERIMENTAL_API("SSL_DelegateCredential", \
(const CERTCertificate *_cert, \
const SECKEYPrivateKey *_certPriv, \
const SECKEYPublicKey *_dcPub, \
SSLSignatureScheme _dcCertVerifyAlg, \
PRUint32 _dcValidFor, \
PRTime _now, \
SECItem *_out), \
(cert, certPriv, dcPub, dcCertVerifyAlg, dcValidFor, \
now, out))
/* New functions created to permit get/set the CipherSuites Order for the
* handshake (Client Hello).
*
* The *Get function puts the current set of active (enabled and policy set as
* PR_TRUE) cipher suites in the cipherOrder outparam. Cipher suites that
* aren't active aren't included. The paramenters are:
* - PRFileDesc *fd = FileDescriptor to get information.
* - PRUint16 *cipherOrder = The memory allocated for cipherOrder needs to be
* SSL_GetNumImplementedCiphers() * sizeof(PRUint16) or more.
* - PRUint16 numCiphers = The number of active ciphersuites listed in
* *cipherOrder is written here.
*
* The *Set function permits reorder the CipherSuites list for the Handshake
* (Client Hello). The default ordering defined in ssl3con.c is enough in
* almost all cases. But, if the client needs some hardening or performance
* adjusts related to CipherSuites, this can be done with this function.
* The caller has to be aware about the risk of call this function while a
* handshake are being processed in this fd/socket. For example, if you disable
* a cipher after the handshake and this cipher was choosen for that
* connection, something bad will happen.
* The parameters are:
* - PRFileDesc *fd = FileDescriptor to change.
* - const PRUint16 *cipherOrder = Must receive all ciphers to be ordered, in
* the desired order. They will be set in the begin of the list. Only
* suites listed by SSL_ImplementedCiphers() can be included.
* - PRUint16 numCiphers = Must receive the number of items in *cipherOrder.
* */
#define SSL_CipherSuiteOrderGet(fd, cipherOrder, numCiphers) \
SSL_EXPERIMENTAL_API("SSL_CipherSuiteOrderGet", \
(PRFileDesc * _fd, PRUint16 * _cipherOrder, \
unsigned int *_numCiphers), \
(fd, cipherOrder, numCiphers))
#define SSL_CipherSuiteOrderSet(fd, cipherOrder, numCiphers) \
SSL_EXPERIMENTAL_API("SSL_CipherSuiteOrderSet", \
(PRFileDesc * _fd, const PRUint16 *_cipherOrder, \
PRUint16 _numCiphers), \
(fd, cipherOrder, numCiphers))
/*
* The following functions expose a masking primitive that uses ciphersuite and
* version information to set paramaters for the masking key and mask generation
* logic. This is only supported for TLS 1.3.
*
* The key and IV are generated using the TLS KDF with a custom label. That is
* HKDF-Expand-Label(secret, label, "", L), where |label| is an input to
* SSL_CreateMaskingContext.
*
* The mask generation logic in SSL_CreateMask is determined by the underlying
* symmetric cipher:
* - For AES-ECB, mask = AES-ECB(mask_key, sample). |len| must be <= 16 as
* the output is limited to a single block.
* - For CHACHA20, mask = ChaCha20(mask_key, sample[0..3], sample[4..15], {0}.len)
* That is, the low 4 bytes of |sample| used as the counter, the remaining 12 bytes
* the nonce. We encrypt |len| bytes of zeros, returning the raw key stream.
*
* The caller must pre-allocate at least |len| bytes for output. If the underlying
* cipher cannot produce the requested amount of data, SECFailure is returned.
*/
typedef struct SSLMaskingContextStr {
CK_MECHANISM_TYPE mech;
PRUint16 version;
PRUint16 cipherSuite;
PK11SymKey *secret;
} SSLMaskingContext;
#define SSL_CreateMaskingContext(version, cipherSuite, secret, \
label, labelLen, ctx) \
SSL_EXPERIMENTAL_API("SSL_CreateMaskingContext", \
(PRUint16 _version, PRUint16 _cipherSuite, \
PK11SymKey * _secret, \
const char *_label, \
unsigned int _labelLen, \
SSLMaskingContext **_ctx), \
(version, cipherSuite, secret, label, labelLen, ctx))
#define SSL_CreateVariantMaskingContext(version, cipherSuite, variant, \
secret, label, labelLen, ctx) \
SSL_EXPERIMENTAL_API("SSL_CreateVariantMaskingContext", \
(PRUint16 _version, PRUint16 _cipherSuite, \
SSLProtocolVariant _variant, \
PK11SymKey * _secret, \
const char *_label, \
unsigned int _labelLen, \
SSLMaskingContext **_ctx), \
(version, cipherSuite, variant, secret, \
label, labelLen, ctx))
#define SSL_DestroyMaskingContext(ctx) \
SSL_EXPERIMENTAL_API("SSL_DestroyMaskingContext", \
(SSLMaskingContext * _ctx), \
(ctx))
#define SSL_CreateMask(ctx, sample, sampleLen, mask, maskLen) \
SSL_EXPERIMENTAL_API("SSL_CreateMask", \
(SSLMaskingContext * _ctx, const PRUint8 *_sample, \
unsigned int _sampleLen, PRUint8 *_mask, \
unsigned int _maskLen), \
(ctx, sample, sampleLen, mask, maskLen))
#define SSL_SetDtls13VersionWorkaround(fd, enabled) \
SSL_EXPERIMENTAL_API("SSL_SetDtls13VersionWorkaround", \
(PRFileDesc * _fd, PRBool _enabled), (fd, enabled))
/* SSL_AddExternalPsk() and SSL_AddExternalPsk0Rtt() can be used to
* set an external PSK on a socket. If successful, this PSK will
* be used in all subsequent connection attempts for this socket.
* This has no effect if the maximum TLS version is < 1.3.
*
* This API currently only accepts a single PSK, so multiple calls to
* either function will fail. An EPSK can be replaced by calling
* SSL_RemoveExternalPsk followed by SSL_AddExternalPsk.
* For both functions, the label is expected to be a unique identifier
* for the external PSK. Should en external PSK have the same label
* as a configured resumption PSK identity, the external PSK will
* take precedence.
*
* If you want to enable early data, you need to also provide a
* cipher suite for 0-RTT and a limit for the early data using
* SSL_AddExternalPsk0Rtt(). If you want to explicitly disallow
* certificate authentication, use SSL_AuthCertificateHook to set
* a callback that rejects all certificate chains.
*/
#define SSL_AddExternalPsk(fd, psk, identity, identityLen, hash) \
SSL_EXPERIMENTAL_API("SSL_AddExternalPsk", \
(PRFileDesc * _fd, PK11SymKey * _psk, \
const PRUint8 *_identity, unsigned int _identityLen, \
SSLHashType _hash), \
(fd, psk, identity, identityLen, hash))
#define SSL_AddExternalPsk0Rtt(fd, psk, identity, identityLen, hash, \
zeroRttSuite, maxEarlyData) \
SSL_EXPERIMENTAL_API("SSL_AddExternalPsk0Rtt", \
(PRFileDesc * _fd, PK11SymKey * _psk, \
const PRUint8 *_identity, unsigned int _identityLen, \
SSLHashType _hash, PRUint16 _zeroRttSuite, \
PRUint32 _maxEarlyData), \
(fd, psk, identity, identityLen, hash, \
zeroRttSuite, maxEarlyData))
/* SSLExp_RemoveExternalPsk() removes an external PSK from socket
* configuration. Returns SECSuccess if the PSK was removed
* successfully, and SECFailure otherwise. */
#define SSL_RemoveExternalPsk(fd, identity, identityLen) \
SSL_EXPERIMENTAL_API("SSL_RemoveExternalPsk", \
(PRFileDesc * _fd, const PRUint8 *_identity, \
unsigned int _identityLen), \
(fd, identity, identityLen))
/* The next function is used to provide support for TLS RFC 8879
* (Certificate Compression).
*
* The function SSL_SetCertificateCompressionAlgorithm() adds a certificate
* compression mechanism to the socket fd. */
#define SSL_SetCertificateCompressionAlgorithm(fd, t) \
SSL_EXPERIMENTAL_API("SSL_SetCertificateCompressionAlgorithm", \
(PRFileDesc * _fd, \
SSLCertificateCompressionAlgorithm t), \
(fd, t))
/*
* SSL_PeerCertificateChainDER() returns the certificates that were presented
* by the peer as a SECItemArray of DER certificates. It is a replacement for
* SSL_PeerCertificateChain() which returns a CERTCertList. Returns SECFailure
* with error SSL_ERROR_NO_CERTIFICATE if the peer did not present
* certificates. The caller is responsible for freeing the output with
* SECItem_FreeArray(..., PR_TRUE) when this function returns SECSuccess.
*/
#define SSL_PeerCertificateChainDER(fd, out) \
SSL_EXPERIMENTAL_API("SSL_PeerCertificateChainDER", \
(PRFileDesc * _fd, SECItemArray * *_out), \
(fd, out))
/* Deprecated experimental APIs */
#define SSL_UseAltServerHelloType(fd, enable) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_SetupAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_InitAntiReplay(a, b, c) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_EnableESNI(a, b, c, d) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_EncodeESNIKeys(a, b, c, d, e, f, g, h, i, j) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_SetESNIKeyPair(a, b, c, d) SSL_DEPRECATED_EXPERIMENTAL_API
#define SSL_EncodeEchConfig(a, b, c, d, e, f, g, h, i) SSL_DEPRECATED_EXPERIMENTAL_API
SEC_END_PROTOS
#endif /* __sslexp_h_ */