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

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.
 */
#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.
 *
 * 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))

/* Set the ESNI key pair on a socket (server side)
 *
 * fd -- the socket
 * record/recordLen -- the encoded DNS record (not base64)
 *
 * Important: the suites that are advertised in the record must
 * be configured on, or this call will fail.
 */
#define SSL_SetESNIKeyPair(fd,                                              \
                           privKey, record, recordLen)                      \
    SSL_EXPERIMENTAL_API("SSL_SetESNIKeyPair",                              \
                         (PRFileDesc * _fd,                                 \
                          SECKEYPrivateKey * _privKey,                      \
                          const PRUint8 *_record, unsigned int _recordLen), \
                         (fd, privKey,                                      \
                          record, recordLen))

/* Set the ESNI keys on a client
 *
 * fd -- the socket
 * ensikeys/esniKeysLen -- the ESNI key structure (not base64)
 * dummyESNI -- the dummy ESNI to use (if any)
 */
#define SSL_EnableESNI(fd, esniKeys, esniKeysLen, dummySNI) \
    SSL_EXPERIMENTAL_API("SSL_EnableESNI",                  \
                         (PRFileDesc * _fd,                 \
                          const PRUint8 *_esniKeys,         \
                          unsigned int _esniKeysLen,        \
                          const char *_dummySNI),           \
                         (fd, esniKeys, esniKeysLen, dummySNI))

/*
 * Generate an encoded ESNIKeys structure (presumably server side).
 *
 * cipherSuites -- the cipher suites that can be used
 * cipherSuitesCount -- the number of suites in cipherSuites
 * group -- the named group this key corresponds to
 * pubKey -- the public key for the key pair
 * pad -- the length to pad to
 * notBefore/notAfter -- validity range
 * out/outlen/maxlen -- where to output the data
 */
#define SSL_EncodeESNIKeys(cipherSuites, cipherSuiteCount,          \
                           group, pubKey, pad, notBefore, notAfter, \
                           out, outlen, maxlen)                     \
    SSL_EXPERIMENTAL_API("SSL_EncodeESNIKeys",                      \
                         (PRUint16 * _cipherSuites,                 \
                          unsigned int _cipherSuiteCount,           \
                          SSLNamedGroup _group,                     \
                          SECKEYPublicKey *_pubKey,                 \
                          PRUint16 _pad,                            \
                          PRUint64 _notBefore, PRUint64 _notAfter,  \
                          PRUint8 *_out, unsigned int *_outlen,     \
                          unsigned int _maxlen),                    \
                         (cipherSuites, cipherSuiteCount,           \
                          group, pubKey, pad, notBefore, notAfter,  \
                          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.
 */
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_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 produces a key with a mechanism that is suitable for
 * input to SSL_HkdfExpandLabel or SSL_MakeAead. */
#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))

/* SSL_HkdfExpandLabelWithMech uses 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. */
#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))

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

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

SEC_END_PROTOS

#endif /* __sslexp_h_ */
