

#include <assert.h>
#include "../../ssl_local.h"
#include "../record_local.h"
#include "recmethod_local.h"


static int satsub64be(const unsigned char *v1, const unsigned char *v2)
{
    int64_t ret;
    uint64_t l1, l2;

    n2l8(v1, l1);
    n2l8(v2, l2);

    ret = l1 - l2;

    
    if (l1 > l2 && ret < 0)
        return 128;
    else if (l2 > l1 && ret > 0)
        return -128;

    if (ret > 128)
        return 128;
    else if (ret < -128)
        return -128;
    else
        return (int)ret;
}

static int dtls_record_replay_check(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap)
{
    int cmp;
    unsigned int shift;
    const unsigned char *seq = rl->sequence;

    cmp = satsub64be(seq, bitmap->max_seq_num);
    if (cmp > 0) {
        ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq);
        return 1;               
    }
    shift = -cmp;
    if (shift >= sizeof(bitmap->map) * 8)
        return 0;               
    else if (bitmap->map & ((uint64_t)1 << shift))
        return 0;               

    ossl_tls_rl_record_set_seq_num(&rl->rrec[0], seq);
    return 1;
}

static void dtls_record_bitmap_update(OSSL_RECORD_LAYER *rl,
                                      DTLS_BITMAP *bitmap)
{
    int cmp;
    unsigned int shift;
    const unsigned char *seq = rl->sequence;

    cmp = satsub64be(seq, bitmap->max_seq_num);
    if (cmp > 0) {
        shift = cmp;
        if (shift < sizeof(bitmap->map) * 8)
            bitmap->map <<= shift, bitmap->map |= 1UL;
        else
            bitmap->map = 1UL;
        memcpy(bitmap->max_seq_num, seq, SEQ_NUM_SIZE);
    } else {
        shift = -cmp;
        if (shift < sizeof(bitmap->map) * 8)
            bitmap->map |= (uint64_t)1 << shift;
    }
}

static DTLS_BITMAP *dtls_get_bitmap(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rr,
                                    unsigned int *is_next_epoch)
{
    *is_next_epoch = 0;

    
    if (rr->epoch == rl->epoch)
        return &rl->bitmap;

    
    else if (rr->epoch == rl->epoch + 1) {
        *is_next_epoch = 1;
        return &rl->next_bitmap;
    }

    return NULL;
}

static void dtls_set_in_init(OSSL_RECORD_LAYER *rl, int in_init)
{
    rl->in_init = in_init;
}

static int dtls_process_record(OSSL_RECORD_LAYER *rl, DTLS_BITMAP *bitmap)
{
    int i;
    int enc_err;
    TLS_RL_RECORD *rr;
    int imac_size;
    size_t mac_size = 0;
    unsigned char md[EVP_MAX_MD_SIZE];
    SSL_MAC_BUF macbuf = { NULL, 0 };
    int ret = 0;

    rr = &rl->rrec[0];

    
    rr->input = &(rl->packet[DTLS1_RT_HEADER_LENGTH]);

    

    

    
    if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
        RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, SSL_R_ENCRYPTED_LENGTH_TOO_LONG);
        return 0;
    }

    
    rr->data = rr->input;
    rr->orig_len = rr->length;

    if (rl->md_ctx != NULL) {
        const EVP_MD *tmpmd = EVP_MD_CTX_get0_md(rl->md_ctx);

        if (tmpmd != NULL) {
            imac_size = EVP_MD_get_size(tmpmd);
            if (!ossl_assert(imac_size >= 0 && imac_size <= EVP_MAX_MD_SIZE)) {
                RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_EVP_LIB);
                return 0;
            }
            mac_size = (size_t)imac_size;
        }
    }

    if (rl->use_etm && rl->md_ctx != NULL) {
        unsigned char *mac;

        if (rr->orig_len < mac_size) {
            RLAYERfatal(rl, SSL_AD_DECODE_ERROR, SSL_R_LENGTH_TOO_SHORT);
            return 0;
        }
        rr->length -= mac_size;
        mac = rr->data + rr->length;
        i = rl->funcs->mac(rl, rr, md, 0 );
        if (i == 0 || CRYPTO_memcmp(md, mac, (size_t)mac_size) != 0) {
            RLAYERfatal(rl, SSL_AD_BAD_RECORD_MAC,
                        SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC);
            return 0;
        }
        
        mac_size = 0;
    }

    
    ERR_set_mark();
    enc_err = rl->funcs->cipher(rl, rr, 1, 0, &macbuf, mac_size);

    
    if (enc_err == 0) {
        ERR_pop_to_mark();
        if (rl->alert != SSL_AD_NO_ALERT) {
            
            goto end;
        }
        
        rr->length = 0;
        rl->packet_length = 0;
        goto end;
    }
    ERR_clear_last_mark();
    OSSL_TRACE_BEGIN(TLS) {
        BIO_printf(trc_out, "dec %zd\n", rr->length);
        BIO_dump_indent(trc_out, rr->data, rr->length, 4);
    } OSSL_TRACE_END(TLS);

    
    if (!rl->use_etm
            && (rl->enc_ctx != NULL)
            && (EVP_MD_CTX_get0_md(rl->md_ctx) != NULL)) {
        

        i = rl->funcs->mac(rl, rr, md, 0 );
        if (i == 0 || macbuf.mac == NULL
            || CRYPTO_memcmp(md, macbuf.mac, mac_size) != 0)
            enc_err = 0;
        if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_size)
            enc_err = 0;
    }

    if (enc_err == 0) {
        
        rr->length = 0;
        rl->packet_length = 0;
        goto end;
    }

    
    if (rl->compctx != NULL) {
        if (rr->length > SSL3_RT_MAX_COMPRESSED_LENGTH) {
            RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW,
                        SSL_R_COMPRESSED_LENGTH_TOO_LONG);
            goto end;
        }
        if (!tls_do_uncompress(rl, rr)) {
            RLAYERfatal(rl, SSL_AD_DECOMPRESSION_FAILURE, SSL_R_BAD_DECOMPRESSION);
            goto end;
        }
    }

    
    if (rr->length > rl->max_frag_len) {
        RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, SSL_R_DATA_LENGTH_TOO_LONG);
        goto end;
    }

    rr->off = 0;
    

    
    rl->packet_length = 0;

    
    dtls_record_bitmap_update(rl, bitmap);

    ret = 1;
 end:
    if (macbuf.alloced)
        OPENSSL_free(macbuf.mac);
    return ret;
}

static int dtls_rlayer_buffer_record(OSSL_RECORD_LAYER *rl, struct pqueue_st *queue,
                                     unsigned char *priority)
{
    DTLS_RLAYER_RECORD_DATA *rdata;
    pitem *item;

    
    if (pqueue_size(queue) >= 100)
        return 0;

    rdata = OPENSSL_malloc(sizeof(*rdata));
    item = pitem_new(priority, rdata);
    if (rdata == NULL || item == NULL) {
        OPENSSL_free(rdata);
        pitem_free(item);
        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return -1;
    }

    rdata->packet = rl->packet;
    rdata->packet_length = rl->packet_length;
    memcpy(&(rdata->rbuf), &rl->rbuf, sizeof(TLS_BUFFER));
    memcpy(&(rdata->rrec), &rl->rrec[0], sizeof(TLS_RL_RECORD));

    item->data = rdata;

    rl->packet = NULL;
    rl->packet_length = 0;
    memset(&rl->rbuf, 0, sizeof(TLS_BUFFER));
    memset(&rl->rrec[0], 0, sizeof(rl->rrec[0]));

    if (!tls_setup_read_buffer(rl)) {
        
        OPENSSL_free(rdata->rbuf.buf);
        OPENSSL_free(rdata);
        pitem_free(item);
        return -1;
    }

    if (pqueue_insert(queue, item) == NULL) {
        
        OPENSSL_free(rdata->rbuf.buf);
        OPENSSL_free(rdata);
        pitem_free(item);
    }

    return 1;
}


static int dtls_copy_rlayer_record(OSSL_RECORD_LAYER *rl, pitem *item)
{
    DTLS_RLAYER_RECORD_DATA *rdata;

    rdata = (DTLS_RLAYER_RECORD_DATA *)item->data;

    ossl_tls_buffer_release(&rl->rbuf);

    rl->packet = rdata->packet;
    rl->packet_length = rdata->packet_length;
    memcpy(&rl->rbuf, &(rdata->rbuf), sizeof(TLS_BUFFER));
    memcpy(&rl->rrec[0], &(rdata->rrec), sizeof(TLS_RL_RECORD));

    
    memcpy(&(rl->sequence[2]), &(rdata->packet[5]), 6);

    return 1;
}

static int dtls_retrieve_rlayer_buffered_record(OSSL_RECORD_LAYER *rl,
                                                struct pqueue_st *queue)
{
    pitem *item;

    item = pqueue_pop(queue);
    if (item) {
        dtls_copy_rlayer_record(rl, item);

        OPENSSL_free(item->data);
        pitem_free(item);

        return 1;
    }

    return 0;
}


int dtls_get_more_records(OSSL_RECORD_LAYER *rl)
{
    int ssl_major, ssl_minor;
    int rret;
    size_t more, n;
    TLS_RL_RECORD *rr;
    unsigned char *p = NULL;
    DTLS_BITMAP *bitmap;
    unsigned int is_next_epoch;

    rl->num_recs = 0;
    rl->curr_rec = 0;
    rl->num_released = 0;

    rr = rl->rrec;

    if (rl->rbuf.buf == NULL) {
        if (!tls_setup_read_buffer(rl)) {
            
            return OSSL_RECORD_RETURN_FATAL;
        }
    }

 again:
    
    if (dtls_retrieve_rlayer_buffered_record(rl, rl->processed_rcds)) {
        rl->num_recs = 1;
        return OSSL_RECORD_RETURN_SUCCESS;
    }

    

    
    if ((rl->rstate != SSL_ST_READ_BODY) ||
        (rl->packet_length < DTLS1_RT_HEADER_LENGTH)) {
        rret = rl->funcs->read_n(rl, DTLS1_RT_HEADER_LENGTH,
                                 TLS_BUFFER_get_len(&rl->rbuf), 0, 1, &n);
        
        if (rret < OSSL_RECORD_RETURN_SUCCESS) {
            
            return rret;         
        }

        
        if (rl->packet_length != DTLS1_RT_HEADER_LENGTH) {
            rl->packet_length = 0;
            goto again;
        }

        rl->rstate = SSL_ST_READ_BODY;

        p = rl->packet;

        
        rr->type = *(p++);
        ssl_major = *(p++);
        ssl_minor = *(p++);
        rr->rec_version = (ssl_major << 8) | ssl_minor;

        
        n2s(p, rr->epoch);

        memcpy(&(rl->sequence[2]), p, 6);
        p += 6;

        n2s(p, rr->length);

        if (rl->msg_callback != NULL)
            rl->msg_callback(0, rr->rec_version, SSL3_RT_HEADER, rl->packet, DTLS1_RT_HEADER_LENGTH,
                             rl->cbarg);

        
        if (!rl->is_first_record && rr->type != SSL3_RT_ALERT) {
            if (rr->rec_version != rl->version) {
                
                rr->length = 0;
                rl->packet_length = 0;
                goto again;
            }
        }

        if (ssl_major !=
                (rl->version == DTLS_ANY_VERSION ? DTLS1_VERSION_MAJOR
                                                 : rl->version >> 8)) {
            
            rr->length = 0;
            rl->packet_length = 0;
            goto again;
        }

        if (rr->length > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
            
            rr->length = 0;
            rl->packet_length = 0;
            goto again;
        }

        
        if (rr->length > rl->max_frag_len + SSL3_RT_MAX_ENCRYPTED_OVERHEAD) {
            
            rr->length = 0;
            rl->packet_length = 0;
            goto again;
        }

        
    }

    

    if (rr->length > rl->packet_length - DTLS1_RT_HEADER_LENGTH) {
        
        more = rr->length;
        rret = rl->funcs->read_n(rl, more, more, 1, 1, &n);
        
        if (rret < OSSL_RECORD_RETURN_SUCCESS || n != more) {
            if (rl->alert != SSL_AD_NO_ALERT) {
                
                return OSSL_RECORD_RETURN_FATAL;
            }
            rr->length = 0;
            rl->packet_length = 0;
            goto again;
        }

        
    }
    
    rl->rstate = SSL_ST_READ_HEADER;

    
    bitmap = dtls_get_bitmap(rl, rr, &is_next_epoch);
    if (bitmap == NULL) {
        rr->length = 0;
        rl->packet_length = 0; 
        goto again;             
    }
#ifndef OPENSSL_NO_SCTP
    
    if (!BIO_dgram_is_sctp(rl->bio)) {
#endif
        
        if (!dtls_record_replay_check(rl, bitmap)) {
            rr->length = 0;
            rl->packet_length = 0; 
            goto again;         
        }
#ifndef OPENSSL_NO_SCTP
    }
#endif

    
    if (rr->length == 0)
        goto again;

    
    if (is_next_epoch) {
        if (rl->in_init) {
            if (dtls_rlayer_buffer_record(rl, rl->unprocessed_rcds,
                                          rr->seq_num) < 0) {
                
                return OSSL_RECORD_RETURN_FATAL;
            }
        }
        rr->length = 0;
        rl->packet_length = 0;
        goto again;
    }

    if (!dtls_process_record(rl, bitmap)) {
        if (rl->alert != SSL_AD_NO_ALERT) {
            
            return OSSL_RECORD_RETURN_FATAL;
        }
        rr->length = 0;
        rl->packet_length = 0; 
        goto again;             
    }

    if (rl->funcs->post_process_record && !rl->funcs->post_process_record(rl, rr)) {
        
        return OSSL_RECORD_RETURN_FATAL;
    }

    rl->num_recs = 1;
    return OSSL_RECORD_RETURN_SUCCESS;
}

static int dtls_free(OSSL_RECORD_LAYER *rl)
{
    TLS_BUFFER *rbuf;
    size_t left, written;
    pitem *item;
    DTLS_RLAYER_RECORD_DATA *rdata;
    int ret = 1;

    rbuf = &rl->rbuf;

    left = rbuf->left;
    if (left > 0) {
        
        ret = BIO_write_ex(rl->next, rbuf->buf + rbuf->offset, left, &written);
        rbuf->left = 0;
    }

    if (rl->unprocessed_rcds != NULL) {
        while ((item = pqueue_pop(rl->unprocessed_rcds)) != NULL) {
            rdata = (DTLS_RLAYER_RECORD_DATA *)item->data;
            
            ret &= BIO_write_ex(rl->next, rdata->packet, rdata->packet_length,
                                &written);
            OPENSSL_free(rdata->rbuf.buf);
            OPENSSL_free(item->data);
            pitem_free(item);
        }
        pqueue_free(rl->unprocessed_rcds);
    }

    if (rl->processed_rcds!= NULL) {
        while ((item = pqueue_pop(rl->processed_rcds)) != NULL) {
            rdata = (DTLS_RLAYER_RECORD_DATA *)item->data;
            OPENSSL_free(rdata->rbuf.buf);
            OPENSSL_free(item->data);
            pitem_free(item);
        }
        pqueue_free(rl->processed_rcds);
    }

    return tls_free(rl) && ret;
}

static int
dtls_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)
{
    int ret;

    ret = tls_int_new_record_layer(libctx, propq, vers, role, direction, level,
                                   ciph, taglen, md, comp, prev,
                                   transport, next, settings,
                                   options, fns, cbarg, retrl);

    if (ret != OSSL_RECORD_RETURN_SUCCESS)
        return ret;

    (*retrl)->unprocessed_rcds = pqueue_new();
    (*retrl)->processed_rcds = pqueue_new();

    if ((*retrl)->unprocessed_rcds == NULL
            || (*retrl)->processed_rcds == NULL) {
        dtls_free(*retrl);
        *retrl = NULL;
        ERR_raise(ERR_LIB_SSL, ERR_R_SSL_LIB);
        return OSSL_RECORD_RETURN_FATAL;
    }

    (*retrl)->isdtls = 1;
    (*retrl)->epoch = epoch;
    (*retrl)->in_init = 1;

    switch (vers) {
    case DTLS_ANY_VERSION:
        (*retrl)->funcs = &dtls_any_funcs;
        break;
    case DTLS1_2_VERSION:
    case DTLS1_VERSION:
    case DTLS1_BAD_VER:
        (*retrl)->funcs = &dtls_1_funcs;
        break;
    default:
        
        ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR);
        ret = OSSL_RECORD_RETURN_FATAL;
        goto err;
    }

    ret = (*retrl)->funcs->set_crypto_state(*retrl, level, key, keylen, iv,
                                            ivlen, mackey, mackeylen, ciph,
                                            taglen, mactype, md, comp);

 err:
    if (ret != OSSL_RECORD_RETURN_SUCCESS) {
        dtls_free(*retrl);
        *retrl = NULL;
    }
    return ret;
}

int dtls_prepare_record_header(OSSL_RECORD_LAYER *rl,
                               WPACKET *thispkt,
                               OSSL_RECORD_TEMPLATE *templ,
                               uint8_t rectype,
                               unsigned char **recdata)
{
    size_t maxcomplen;

    *recdata = NULL;

    maxcomplen = templ->buflen;
    if (rl->compctx != NULL)
        maxcomplen += SSL3_RT_MAX_COMPRESSED_OVERHEAD;

    if (!WPACKET_put_bytes_u8(thispkt, rectype)
            || !WPACKET_put_bytes_u16(thispkt, templ->version)
            || !WPACKET_put_bytes_u16(thispkt, rl->epoch)
            || !WPACKET_memcpy(thispkt, &(rl->sequence[2]), 6)
            || !WPACKET_start_sub_packet_u16(thispkt)
            || (rl->eivlen > 0
                && !WPACKET_allocate_bytes(thispkt, rl->eivlen, NULL))
            || (maxcomplen > 0
                && !WPACKET_reserve_bytes(thispkt, maxcomplen,
                                          recdata))) {
        RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
        return 0;
    }

    return 1;
}

int dtls_post_encryption_processing(OSSL_RECORD_LAYER *rl,
                                    size_t mac_size,
                                    OSSL_RECORD_TEMPLATE *thistempl,
                                    WPACKET *thispkt,
                                    TLS_RL_RECORD *thiswr)
{
    if (!tls_post_encryption_processing_default(rl, mac_size, thistempl,
                                                thispkt, thiswr)) {
        
        return 0;
    }

    return tls_increment_sequence_ctr(rl);
}

static size_t dtls_get_max_record_overhead(OSSL_RECORD_LAYER *rl)
{
    size_t blocksize = 0;

    if (rl->enc_ctx != NULL &&
        (EVP_CIPHER_CTX_get_mode(rl->enc_ctx) == EVP_CIPH_CBC_MODE))
        blocksize = EVP_CIPHER_CTX_get_block_size(rl->enc_ctx);

    
    assert(rl->enc_ctx == NULL  || ((blocksize == 0 || rl->eivlen > 0)
                                    && rl->taglen > 0));
    assert(rl->md == NULL || (int)rl->taglen == EVP_MD_size(rl->md));

    
    return DTLS1_RT_HEADER_LENGTH + rl->eivlen + blocksize + rl->taglen;
}

const OSSL_RECORD_METHOD ossl_dtls_record_method = {
    dtls_new_record_layer,
    dtls_free,
    tls_unprocessed_read_pending,
    tls_processed_read_pending,
    tls_app_data_pending,
    tls_get_max_records,
    tls_write_records,
    tls_retry_write_records,
    tls_read_record,
    tls_release_record,
    tls_get_alert_code,
    tls_set1_bio,
    tls_set_protocol_version,
    NULL,
    tls_set_first_handshake,
    tls_set_max_pipelines,
    dtls_set_in_init,
    tls_get_state,
    tls_set_options,
    tls_get_compression,
    tls_set_max_frag_len,
    dtls_get_max_record_overhead,
    tls_increment_sequence_ctr,
    tls_alloc_buffers,
    tls_free_buffers
};
