

#include <openssl/rand.h>
#include <openssl/evp.h>

#include "internal/constant_time.h"
#include "internal/cryptlib.h"
#include "internal/ssl3_cbc.h"



static int ssl3_cbc_copy_mac(size_t *reclen,
                             size_t origreclen,
                             unsigned char *recdata,
                             unsigned char **mac,
                             int *alloced,
                             size_t block_size,
                             size_t mac_size,
                             size_t good,
                             OSSL_LIB_CTX *libctx);


int ssl3_cbc_remove_padding_and_mac(size_t *reclen,
                                    size_t origreclen,
                                    unsigned char *recdata,
                                    unsigned char **mac,
                                    int *alloced,
                                    size_t block_size, size_t mac_size,
                                    OSSL_LIB_CTX *libctx)
{
    size_t padding_length;
    size_t good;
    const size_t overhead = 1   + mac_size;

    
    if (overhead > *reclen)
        return 0;

    padding_length = recdata[*reclen - 1];
    good = constant_time_ge_s(*reclen, padding_length + overhead);
    
    good &= constant_time_ge_s(block_size, padding_length + 1);
    *reclen -= good & (padding_length + 1);

    return ssl3_cbc_copy_mac(reclen, origreclen, recdata, mac, alloced,
                             block_size, mac_size, good, libctx);
}


int tls1_cbc_remove_padding_and_mac(size_t *reclen,
                                    size_t origreclen,
                                    unsigned char *recdata,
                                    unsigned char **mac,
                                    int *alloced,
                                    size_t block_size, size_t mac_size,
                                    int aead,
                                    OSSL_LIB_CTX *libctx)
{
    size_t good = -1;
    size_t padding_length, to_check, i;
    size_t overhead = ((block_size == 1) ? 0 : 1) 
                      + mac_size;

    
    if (overhead > *reclen)
        return 0;

    if (block_size != 1) {

        padding_length = recdata[*reclen - 1];

        if (aead) {
            
            *reclen -= padding_length + 1 + mac_size;
            return 1;
        }

        good = constant_time_ge_s(*reclen, overhead + padding_length);
        
        to_check = 256;        
        if (to_check > *reclen)
            to_check = *reclen;

        for (i = 0; i < to_check; i++) {
            unsigned char mask = constant_time_ge_8_s(padding_length, i);
            unsigned char b = recdata[*reclen - 1 - i];
            
            good &= ~(mask & (padding_length ^ b));
        }

        
        good = constant_time_eq_s(0xff, good & 0xff);
        *reclen -= good & (padding_length + 1);
    }

    return ssl3_cbc_copy_mac(reclen, origreclen, recdata, mac, alloced,
                             block_size, mac_size, good, libctx);
}


#define CBC_MAC_ROTATE_IN_PLACE

static int ssl3_cbc_copy_mac(size_t *reclen,
                             size_t origreclen,
                             unsigned char *recdata,
                             unsigned char **mac,
                             int *alloced,
                             size_t block_size,
                             size_t mac_size,
                             size_t good,
                             OSSL_LIB_CTX *libctx)
{
#if defined(CBC_MAC_ROTATE_IN_PLACE)
    unsigned char rotated_mac_buf[64 + EVP_MAX_MD_SIZE];
    unsigned char *rotated_mac;
    char aux1, aux2, aux3, mask;
#else
    unsigned char rotated_mac[EVP_MAX_MD_SIZE];
#endif
    unsigned char randmac[EVP_MAX_MD_SIZE];
    unsigned char *out;

    
    size_t mac_end = *reclen;
    size_t mac_start = mac_end - mac_size;
    size_t in_mac;
    
    size_t scan_start = 0;
    size_t i, j;
    size_t rotate_offset;

    if (!ossl_assert(origreclen >= mac_size
                     && mac_size <= EVP_MAX_MD_SIZE))
        return 0;

    
    if (mac_size == 0) {
        
        if (good == 0)
            return 0;
        return 1;
    }

    *reclen -= mac_size;

    if (block_size == 1) {
        
        if (mac != NULL)
            *mac = &recdata[*reclen];
        if (alloced != NULL)
            *alloced = 0;
        return 1;
    }

    
    if (RAND_bytes_ex(libctx, randmac, mac_size, 0) <= 0)
        return 0;

    if (!ossl_assert(mac != NULL && alloced != NULL))
        return 0;
    *mac = out = OPENSSL_malloc(mac_size);
    if (*mac == NULL)
        return 0;
    *alloced = 1;

#if defined(CBC_MAC_ROTATE_IN_PLACE)
    rotated_mac = rotated_mac_buf + ((0 - (size_t)rotated_mac_buf) & 63);
#endif

    
    if (origreclen > mac_size + 255 + 1)
        scan_start = origreclen - (mac_size + 255 + 1);

    in_mac = 0;
    rotate_offset = 0;
    memset(rotated_mac, 0, mac_size);
    for (i = scan_start, j = 0; i < origreclen; i++) {
        size_t mac_started = constant_time_eq_s(i, mac_start);
        size_t mac_ended = constant_time_lt_s(i, mac_end);
        unsigned char b = recdata[i];

        in_mac |= mac_started;
        in_mac &= mac_ended;
        rotate_offset |= j & mac_started;
        rotated_mac[j++] |= b & in_mac;
        j &= constant_time_lt_s(j, mac_size);
    }

    
#if defined(CBC_MAC_ROTATE_IN_PLACE)
    j = 0;
    for (i = 0; i < mac_size; i++) {
        
        aux1 = rotated_mac[rotate_offset & ~32];
        aux2 = rotated_mac[rotate_offset | 32];
        mask = constant_time_eq_8(rotate_offset & ~32, rotate_offset);
        aux3 = constant_time_select_8(mask, aux1, aux2);
        rotate_offset++;

        
        out[j++] = constant_time_select_8((unsigned char)(good & 0xff),
                                          aux3,
                                          randmac[i]);
        rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);
    }
#else
    memset(out, 0, mac_size);
    rotate_offset = mac_size - rotate_offset;
    rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);
    for (i = 0; i < mac_size; i++) {
        for (j = 0; j < mac_size; j++)
            out[j] |= rotated_mac[i] & constant_time_eq_8_s(j, rotate_offset);
        rotate_offset++;
        rotate_offset &= constant_time_lt_s(rotate_offset, mac_size);

        
        out[i] = constant_time_select_8((unsigned char)(good & 0xff), out[i],
                                        randmac[i]);
    }
#endif

    return 1;
}
