diff options
Diffstat (limited to 'security/nss/lib/ssl/sslexp.h')
-rw-r--r-- | security/nss/lib/ssl/sslexp.h | 1087 |
1 files changed, 1087 insertions, 0 deletions
diff --git a/security/nss/lib/ssl/sslexp.h b/security/nss/lib/ssl/sslexp.h new file mode 100644 index 0000000000..6eeafa5029 --- /dev/null +++ b/security/nss/lib/ssl/sslexp.h @@ -0,0 +1,1087 @@ +/* + * 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 doesn’t check that + * extension writers don’t 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)) + +/* 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_ */ |