

#include <assert.h>

#include <openssl/prov_ssl.h>
#include <openssl/rand.h>
#include <openssl/proverr.h>
#include "internal/constant_time.h"
#include "internal/ssl3_cbc.h"
#include "ciphercommon_local.h"


size_t ossl_cipher_fillblock(unsigned char *buf, size_t *buflen,
                             size_t blocksize,
                             const unsigned char **in, size_t *inlen)
{
    size_t blockmask = ~(blocksize - 1);
    size_t bufremain = blocksize - *buflen;

    assert(*buflen <= blocksize);
    assert(blocksize > 0 && (blocksize & (blocksize - 1)) == 0);

    if (*inlen < bufremain)
        bufremain = *inlen;
    memcpy(buf + *buflen, *in, bufremain);
    *in += bufremain;
    *inlen -= bufremain;
    *buflen += bufremain;

    return *inlen & blockmask;
}


int ossl_cipher_trailingdata(unsigned char *buf, size_t *buflen, size_t blocksize,
                             const unsigned char **in, size_t *inlen)
{
    if (*inlen == 0)
        return 1;

    if (*buflen + *inlen > blocksize) {
        ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
        return 0;
    }

    memcpy(buf + *buflen, *in, *inlen);
    *buflen += *inlen;
    *inlen = 0;

    return 1;
}


void ossl_cipher_padblock(unsigned char *buf, size_t *buflen, size_t blocksize)
{
    size_t i;
    unsigned char pad = (unsigned char)(blocksize - *buflen);

    for (i = *buflen; i < blocksize; i++)
        buf[i] = pad;
}

int ossl_cipher_unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize)
{
    size_t pad, i;
    size_t len = *buflen;

    if (len != blocksize) {
        ERR_raise(ERR_LIB_PROV, ERR_R_INTERNAL_ERROR);
        return 0;
    }

    
    pad = buf[blocksize - 1];
    if (pad == 0 || pad > blocksize) {
        ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT);
        return 0;
    }
    for (i = 0; i < pad; i++) {
        if (buf[--len] != pad) {
            ERR_raise(ERR_LIB_PROV, PROV_R_BAD_DECRYPT);
            return 0;
        }
    }
    *buflen = len;
    return 1;
}


int ossl_cipher_tlsunpadblock(OSSL_LIB_CTX *libctx, unsigned int tlsversion,
                              unsigned char *buf, size_t *buflen,
                              size_t blocksize,
                              unsigned char **mac, int *alloced, size_t macsize,
                              int aead)
{
    int ret;

    switch (tlsversion) {
    case SSL3_VERSION:
        return ssl3_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
                                               alloced, blocksize, macsize,
                                               libctx);

    case TLS1_2_VERSION:
    case DTLS1_2_VERSION:
    case TLS1_1_VERSION:
    case DTLS1_VERSION:
    case DTLS1_BAD_VER:
        
        buf += blocksize;
        *buflen -= blocksize;
        
    case TLS1_VERSION:
        ret = tls1_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
                                              alloced, blocksize, macsize,
                                              aead, libctx);
        return ret;

    default:
        return 0;
    }
}
