
#include <openssl/ssl.h>
#include "internal/recordmethod.h"
#include "internal/quic_tls.h"
#include "../ssl_local.h"
#include "internal/quic_error.h"

#define QUIC_TLS_FATAL(rl, ad, err) \
    do { \
        if ((rl) != NULL) (rl)->alert = (ad); \
        ERR_raise(ERR_LIB_SSL, (err)); \
        if ((rl) != NULL) (rl)->qtls->inerror = 1; \
    } while(0)

struct quic_tls_st {
    QUIC_TLS_ARGS args;

    
    const unsigned char *local_transport_params;
    size_t local_transport_params_len;

    ERR_STATE *error_state;

    
    uint64_t error_code;

    
    const char *error_msg;

    
    unsigned int configured : 1;

    
    unsigned int inerror : 1;

    
    unsigned int complete : 1;
};

struct ossl_record_layer_st {
    QUIC_TLS *qtls;

    
    int level;

    
    BIO *dummybio;

    
    size_t written;

    
    OSSL_RECORD_TEMPLATE template;

    
    int alert;

    
    size_t recread;

    
    size_t recunreleased;

    
    OSSL_FUNC_rlayer_msg_callback_fn *msg_callback;
    void *cbarg;
};

static int quic_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio);
static int quic_free(OSSL_RECORD_LAYER *r);

static int
quic_new_record_layer(OSSL_LIB_CTX *libctx, const char *propq, int vers,
                      int role, int direction, int level, uint16_t epoch,
                      unsigned char *secret, size_t secretlen,
                      unsigned char *key, size_t keylen, unsigned char *iv,
                      size_t ivlen, unsigned char *mackey, size_t mackeylen,
                      const EVP_CIPHER *ciph, size_t taglen,
                      int mactype,
                      const EVP_MD *md, COMP_METHOD *comp,
                      const EVP_MD *kdfdigest, BIO *prev, BIO *transport,
                      BIO *next, BIO_ADDR *local, BIO_ADDR *peer,
                      const OSSL_PARAM *settings, const OSSL_PARAM *options,
                      const OSSL_DISPATCH *fns, void *cbarg, void *rlarg,
                      OSSL_RECORD_LAYER **retrl)
{
    OSSL_RECORD_LAYER *rl = OPENSSL_zalloc(sizeof(*rl));
    uint32_t enc_level;
    int qdir;
    uint32_t suite_id = 0;

    if (rl == NULL) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return 0;
    }

    rl->qtls = (QUIC_TLS *)rlarg;
    rl->level = level;
    if (!quic_set1_bio(rl, transport)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        goto err;
    }
    rl->cbarg = cbarg;
    *retrl = rl;

    if (fns != NULL) {
        for (; fns->function_id != 0; fns++) {
            switch (fns->function_id) {
                break;
            case OSSL_FUNC_RLAYER_MSG_CALLBACK:
                rl->msg_callback = OSSL_FUNC_rlayer_msg_callback(fns);
                break;
            default:
                
                break;
            }
        }
    }

    switch (level) {
    case OSSL_RECORD_PROTECTION_LEVEL_NONE:
        return 1;

    case OSSL_RECORD_PROTECTION_LEVEL_EARLY:
        enc_level = QUIC_ENC_LEVEL_0RTT;
        break;

    case OSSL_RECORD_PROTECTION_LEVEL_HANDSHAKE:
        enc_level = QUIC_ENC_LEVEL_HANDSHAKE;
        break;

    case OSSL_RECORD_PROTECTION_LEVEL_APPLICATION:
        enc_level = QUIC_ENC_LEVEL_1RTT;
        break;

    default:
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        goto err;
    }

    if (direction == OSSL_RECORD_DIRECTION_READ)
        qdir = 0;
    else
        qdir = 1;

    if (EVP_CIPHER_is_a(ciph, "AES-128-GCM")) {
        suite_id = QRL_SUITE_AES128GCM;
    } else if (EVP_CIPHER_is_a(ciph, "AES-256-GCM")) {
        suite_id = QRL_SUITE_AES256GCM;
    } else if (EVP_CIPHER_is_a(ciph, "CHACHA20-POLY1305")) {
        suite_id = QRL_SUITE_CHACHA20POLY1305;
    } else {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_UNKNOWN_CIPHER_TYPE);
        goto err;
    }

    
    
    if (!EVP_MD_up_ref((EVP_MD *)kdfdigest)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        goto err;
    }

    if (!rl->qtls->args.yield_secret_cb(enc_level, qdir, suite_id,
                                        (EVP_MD *)kdfdigest, secret, secretlen,
                                        rl->qtls->args.yield_secret_cb_arg)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        EVP_MD_free((EVP_MD *)kdfdigest);
        goto err;
    }

    return 1;
 err:
    *retrl = NULL;
    quic_free(rl);
    return 0;
}

static int quic_free(OSSL_RECORD_LAYER *rl)
{
    if (rl == NULL)
        return 1;

    BIO_free(rl->dummybio);
    OPENSSL_free(rl);
    return 1;
}

static int quic_unprocessed_read_pending(OSSL_RECORD_LAYER *rl)
{
    
    return 0;
}

static int quic_processed_read_pending(OSSL_RECORD_LAYER *rl)
{
    
    return 0;
}

static size_t quic_get_max_records(OSSL_RECORD_LAYER *rl, uint8_t type,
                                   size_t len,
                                   size_t maxfrag, size_t *preffrag)
{
    return 1;
}

static int quic_write_records(OSSL_RECORD_LAYER *rl,
                              OSSL_RECORD_TEMPLATE *template,
                              size_t numtempl)
{
    size_t consumed;
    unsigned char alert;

    if (!ossl_assert(numtempl == 1)) {
        
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return OSSL_RECORD_RETURN_FATAL;
    }

    BIO_clear_retry_flags(rl->dummybio);

    if (rl->msg_callback != NULL) {
        unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];

        
        dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
                        ? template->type
                        : SSL3_RT_APPLICATION_DATA;
        dummyrec[1] = (unsigned char)((template->version >> 8) & 0xff);
        dummyrec[2] = (unsigned char)(template->version & 0xff);
        
        dummyrec[3] = (unsigned char)((template->buflen >> 8) & 0xff);
        dummyrec[4] = (unsigned char)(template->buflen & 0xff);

        rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
                            SSL3_RT_HEADER_LENGTH, rl->cbarg);

        if (rl->level != OSSL_RECORD_PROTECTION_LEVEL_NONE) {
            rl->msg_callback(1, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE,
                             &template->type, 1, rl->cbarg);
        }
    }

    switch (template->type) {
    case SSL3_RT_ALERT:
        if (template->buflen != 2) {
            
            QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, SSL_R_BAD_VALUE);
            return OSSL_RECORD_RETURN_FATAL;
        }
        
        alert = template->buf[1];

        if (!rl->qtls->args.alert_cb(rl->qtls->args.alert_cb_arg, alert)) {
            QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
            return OSSL_RECORD_RETURN_FATAL;
        }
        break;

    case SSL3_RT_HANDSHAKE:
        
        if (!rl->qtls->args.crypto_send_cb(template->buf + rl->written,
                                           template->buflen - rl->written,
                                           &consumed,
                                           rl->qtls->args.crypto_send_cb_arg)) {
            QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
            return OSSL_RECORD_RETURN_FATAL;
        }
        
        if (consumed + rl->written != template->buflen) {
            if (!ossl_assert(consumed + rl->written < template->buflen)) {
                QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
                return OSSL_RECORD_RETURN_FATAL;
            }

            
            rl->written += consumed;
            rl->template = *template;
            BIO_set_retry_write(rl->dummybio);

            return OSSL_RECORD_RETURN_RETRY;
        }
        rl->written = 0;
        break;

    default:
        
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return OSSL_RECORD_RETURN_FATAL;
    }

    return OSSL_RECORD_RETURN_SUCCESS;
}

static int quic_retry_write_records(OSSL_RECORD_LAYER *rl)
{
    return quic_write_records(rl, &rl->template, 1);
}

static int quic_read_record(OSSL_RECORD_LAYER *rl, void **rechandle,
                            int *rversion, uint8_t *type, const unsigned char **data,
                            size_t *datalen, uint16_t *epoch,
                            unsigned char *seq_num)
{
    if (rl->recread != 0 || rl->recunreleased != 0)
        return OSSL_RECORD_RETURN_FATAL;

    BIO_clear_retry_flags(rl->dummybio);

    if (!rl->qtls->args.crypto_recv_rcd_cb(data, datalen,
                                           rl->qtls->args.crypto_recv_rcd_cb_arg)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return OSSL_RECORD_RETURN_FATAL;
    }

    if (*datalen == 0) {
        BIO_set_retry_read(rl->dummybio);
        return OSSL_RECORD_RETURN_RETRY;
    }

    *rechandle = rl;
    *rversion = TLS1_3_VERSION;
    *type = SSL3_RT_HANDSHAKE;
    rl->recread = rl->recunreleased = *datalen;
    

    if (rl->msg_callback != NULL) {
        unsigned char dummyrec[SSL3_RT_HEADER_LENGTH];

        
        dummyrec[0] = (rl->level == OSSL_RECORD_PROTECTION_LEVEL_NONE)
                      ? SSL3_RT_HANDSHAKE
                      : SSL3_RT_APPLICATION_DATA;
        dummyrec[1] = (unsigned char)((TLS1_2_VERSION >> 8) & 0xff);
        dummyrec[2] = (unsigned char)(TLS1_2_VERSION & 0xff);
        
        dummyrec[3] = (unsigned char)((*datalen >> 8) & 0xff);
        dummyrec[4] = (unsigned char)(*datalen & 0xff);

        rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_HEADER, dummyrec,
                         SSL3_RT_HEADER_LENGTH, rl->cbarg);
        rl->msg_callback(0, TLS1_3_VERSION, SSL3_RT_INNER_CONTENT_TYPE, type, 1,
                         rl->cbarg);
    }

    return OSSL_RECORD_RETURN_SUCCESS;
}

static int quic_release_record(OSSL_RECORD_LAYER *rl, void *rechandle,
                               size_t length)
{
    if (!ossl_assert(rl->recread > 0)
            || !ossl_assert(rl->recunreleased <= rl->recread)
            || !ossl_assert(rl == rechandle)
            || !ossl_assert(length <= rl->recunreleased)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return OSSL_RECORD_RETURN_FATAL;
    }

    rl->recunreleased -= length;

    if (rl->recunreleased > 0)
        return OSSL_RECORD_RETURN_SUCCESS;

    if (!rl->qtls->args.crypto_release_rcd_cb(rl->recread,
                                              rl->qtls->args.crypto_release_rcd_cb_arg)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return OSSL_RECORD_RETURN_FATAL;
    }

    rl->recread = 0;
    return OSSL_RECORD_RETURN_SUCCESS;
}

static int quic_get_alert_code(OSSL_RECORD_LAYER *rl)
{
    return rl->alert;
}

static int quic_set_protocol_version(OSSL_RECORD_LAYER *rl, int version)
{
    
    if (!ossl_assert(version == TLS1_3_VERSION)) {
        QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return 0;
    }

    return 1;
}

static void quic_set_plain_alerts(OSSL_RECORD_LAYER *rl, int allow)
{
    
}

static void quic_set_first_handshake(OSSL_RECORD_LAYER *rl, int first)
{
    
}

static void quic_set_max_pipelines(OSSL_RECORD_LAYER *rl, size_t max_pipelines)
{
    
}

static void quic_get_state(OSSL_RECORD_LAYER *rl, const char **shortstr,
                    const char **longstr)
{
    

    if (rl->qtls->inerror) {
        if (shortstr != NULL)
            *shortstr = "unknown";
        if (longstr != NULL)
            *longstr = "unknown";
    } else {
        if (shortstr != NULL)
            *shortstr = "RH";
        if (longstr != NULL)
            *longstr = "read header";
    }
}

static int quic_set_options(OSSL_RECORD_LAYER *rl, const OSSL_PARAM *options)
{
    
    return 1;
}

static const COMP_METHOD *quic_get_compression(OSSL_RECORD_LAYER *rl)
{
    
    return NULL;
}

static void quic_set_max_frag_len(OSSL_RECORD_LAYER *rl, size_t max_frag_len)
{
    
}

static int quic_alloc_buffers(OSSL_RECORD_LAYER *rl)
{
    
    return 1;
}

static int quic_free_buffers(OSSL_RECORD_LAYER *rl)
{
    
    return 1;
}

static int quic_set1_bio(OSSL_RECORD_LAYER *rl, BIO *bio)
{
    if (bio != NULL && !BIO_up_ref(bio))
        return 0;
    BIO_free(rl->dummybio);
    rl->dummybio = bio;

    return 1;
}



static size_t quic_app_data_pending(OSSL_RECORD_LAYER *rl)
{
    QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
    return (size_t)ossl_assert(0);
}

static size_t quic_get_max_record_overhead(OSSL_RECORD_LAYER *rl)
{
    QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
    return (size_t)ossl_assert(0);
}

static int quic_increment_sequence_ctr(OSSL_RECORD_LAYER *rl)
{
    QUIC_TLS_FATAL(rl, SSL_AD_INTERNAL_ERROR, ERR_R_SHOULD_NOT_HAVE_BEEN_CALLED);
    return ossl_assert(0);
}



static const OSSL_RECORD_METHOD quic_tls_record_method = {
    quic_new_record_layer,
    quic_free,
    quic_unprocessed_read_pending,
    quic_processed_read_pending,
    quic_app_data_pending, 
    quic_get_max_records,
    quic_write_records,
    quic_retry_write_records,
    quic_read_record,
    quic_release_record,
    quic_get_alert_code,
    quic_set1_bio,
    quic_set_protocol_version,
    quic_set_plain_alerts,
    quic_set_first_handshake,
    quic_set_max_pipelines,
    NULL, 
    quic_get_state,
    quic_set_options,
    quic_get_compression,
    quic_set_max_frag_len,
    quic_get_max_record_overhead, 
    quic_increment_sequence_ctr, 
    quic_alloc_buffers,
    quic_free_buffers
};

static int add_transport_params_cb(SSL *s, unsigned int ext_type,
                                   unsigned int context,
                                   const unsigned char **out, size_t *outlen,
                                   X509 *x, size_t chainidx, int *al,
                                   void *add_arg)
{
    QUIC_TLS *qtls = add_arg;

    *out = qtls->local_transport_params;
    *outlen = qtls->local_transport_params_len;
    return 1;
}

static void free_transport_params_cb(SSL *s, unsigned int ext_type,
                                     unsigned int context,
                                     const unsigned char *out,
                                     void *add_arg)
{
}

static int parse_transport_params_cb(SSL *s, unsigned int ext_type,
                                     unsigned int context,
                                     const unsigned char *in,
                                     size_t inlen, X509 *x,
                                     size_t chainidx,
                                     int *al, void *parse_arg)
{
    QUIC_TLS *qtls = parse_arg;

    return qtls->args.got_transport_params_cb(in, inlen,
                                              qtls->args.got_transport_params_cb_arg);
}

QUIC_TLS *ossl_quic_tls_new(const QUIC_TLS_ARGS *args)
{
    QUIC_TLS *qtls;

    if (args->crypto_send_cb == NULL
        || args->crypto_recv_rcd_cb == NULL
        || args->crypto_release_rcd_cb == NULL) {
        ERR_raise(ERR_LIB_SSL, ERR_R_PASSED_NULL_PARAMETER);
        return NULL;
    }

    qtls = OPENSSL_zalloc(sizeof(*qtls));
    if (qtls == NULL)
        return NULL;

    if ((qtls->error_state = OSSL_ERR_STATE_new()) == NULL) {
        OPENSSL_free(qtls);
        return NULL;
    }

    qtls->args = *args;
    return qtls;
}

void ossl_quic_tls_free(QUIC_TLS *qtls)
{
    if (qtls == NULL)
        return;
    OSSL_ERR_STATE_free(qtls->error_state);
    OPENSSL_free(qtls);
}

static int raise_error(QUIC_TLS *qtls, uint64_t error_code,
                       const char *error_msg,
                       const char *src_file,
                       int src_line,
                       const char *src_func)
{
    
    ERR_new();
    ERR_set_debug(src_file, src_line, src_func);
    ERR_set_error(ERR_LIB_SSL, SSL_R_QUIC_HANDSHAKE_LAYER_ERROR,
                  "handshake layer error, error code %llu (0x%llx) (\"%s\")",
                  error_code, error_code, error_msg);
    OSSL_ERR_STATE_save_to_mark(qtls->error_state);

    
    qtls->error_code        = error_code;
    qtls->error_msg         = error_msg;
    qtls->inerror           = 1;

    ERR_pop_to_mark();
    return 0;
}

#define RAISE_ERROR(qtls, error_code, error_msg) \
    raise_error((qtls), (error_code), (error_msg), \
                OPENSSL_FILE, OPENSSL_LINE, OPENSSL_FUNC)

#define RAISE_INTERNAL_ERROR(qtls) \
    RAISE_ERROR((qtls), OSSL_QUIC_ERR_INTERNAL_ERROR, "internal error")

int ossl_quic_tls_tick(QUIC_TLS *qtls)
{
    int ret, err;
    const unsigned char *alpn;
    unsigned int alpnlen;

    if (qtls->inerror)
        return 0;

    
    ERR_set_mark();

    if (!qtls->configured) {
        SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);
        SSL_CTX *sctx = SSL_CONNECTION_GET_CTX(sc);
        BIO *nullbio;

        

        
        if (qtls->args.is_server) {
            if (sctx->ext.alpn_select_cb == NULL)
                return RAISE_INTERNAL_ERROR(qtls);
        } else {
            if (sc->ext.alpn == NULL || sc->ext.alpn_len == 0)
                return RAISE_ERROR(qtls, OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO,
                                   "ALPN must be configured when using QUIC");
        }
        if (!SSL_set_min_proto_version(qtls->args.s, TLS1_3_VERSION))
            return RAISE_INTERNAL_ERROR(qtls);

        SSL_clear_options(qtls->args.s, SSL_OP_ENABLE_MIDDLEBOX_COMPAT);
        ossl_ssl_set_custom_record_layer(sc, &quic_tls_record_method, qtls);

        if (!ossl_tls_add_custom_ext_intern(NULL, &sc->cert->custext,
                                            qtls->args.is_server ? ENDPOINT_SERVER
                                                                 : ENDPOINT_CLIENT,
                                            TLSEXT_TYPE_quic_transport_parameters,
                                            SSL_EXT_TLS1_3_ONLY
                                            | SSL_EXT_CLIENT_HELLO
                                            | SSL_EXT_TLS1_3_ENCRYPTED_EXTENSIONS,
                                            add_transport_params_cb,
                                            free_transport_params_cb, qtls,
                                            parse_transport_params_cb, qtls))
            return RAISE_INTERNAL_ERROR(qtls);

        nullbio = BIO_new(BIO_s_null());
        if (nullbio == NULL)
            return RAISE_INTERNAL_ERROR(qtls);

        
        SSL_set_bio(qtls->args.s, nullbio, nullbio);

        if (qtls->args.is_server)
            SSL_set_accept_state(qtls->args.s);
        else
            SSL_set_connect_state(qtls->args.s);

        qtls->configured = 1;
    }

    if (qtls->complete)
        
        ret = SSL_read(qtls->args.s, NULL, 0);
    else
        ret = SSL_do_handshake(qtls->args.s);

    if (ret <= 0) {
        err = ossl_ssl_get_error(qtls->args.s, ret,
                                 ERR_count_to_mark() > 0);

        switch (err) {
        case SSL_ERROR_WANT_READ:
        case SSL_ERROR_WANT_WRITE:
        case SSL_ERROR_WANT_CLIENT_HELLO_CB:
        case SSL_ERROR_WANT_X509_LOOKUP:
        case SSL_ERROR_WANT_RETRY_VERIFY:
            ERR_pop_to_mark();
            return 1;

        default:
            return RAISE_INTERNAL_ERROR(qtls);
        }
    }

    if (!qtls->complete) {
        
        SSL_get0_alpn_selected(qtls->args.s, &alpn, &alpnlen);
        if (alpn == NULL || alpnlen == 0)
            return RAISE_ERROR(qtls, OSSL_QUIC_ERR_CRYPTO_NO_APP_PROTO,
                               "no application protocol negotiated");

        qtls->complete = 1;
        ERR_pop_to_mark();
        return qtls->args.handshake_complete_cb(qtls->args.handshake_complete_cb_arg);
    }

    ERR_pop_to_mark();
    return 1;
}

int ossl_quic_tls_set_transport_params(QUIC_TLS *qtls,
                                       const unsigned char *transport_params,
                                       size_t transport_params_len)
{
    qtls->local_transport_params       = transport_params;
    qtls->local_transport_params_len   = transport_params_len;
    return 1;
}

int ossl_quic_tls_get_error(QUIC_TLS *qtls,
                            uint64_t *error_code,
                            const char **error_msg,
                            ERR_STATE **error_state)
{
    if (qtls->inerror) {
        *error_code     = qtls->error_code;
        *error_msg      = qtls->error_msg;
        *error_state    = qtls->error_state;
    }

    return qtls->inerror;
}


int ossl_quic_tls_is_cert_request(QUIC_TLS *qtls)
{
    SSL_CONNECTION *sc = SSL_CONNECTION_FROM_SSL(qtls->args.s);

    return sc->s3.tmp.message_type == SSL3_MT_CERTIFICATE_REQUEST;
}


int ossl_quic_tls_has_bad_max_early_data(QUIC_TLS *qtls)
{
    uint32_t max_early_data = SSL_get0_session(qtls->args.s)->ext.max_early_data;

    
    return max_early_data != 0xffffffff && max_early_data != 0;
}
