openssl/providers/implementations/ciphers/ciphercommon_block.c
Matt Caswell e71fd827bc Add provider support for TLS CBC padding and MAC removal
The previous commits separated out the TLS CBC padding code in libssl.
Now we can use that code to directly support TLS CBC padding and MAC
removal in provided ciphers.

Reviewed-by: Shane Lontis <shane.lontis@oracle.com>
(Merged from https://github.com/openssl/openssl/pull/12288)
2020-07-06 09:26:09 +01:00

188 lines
6.3 KiB
C

/*
* Copyright 2019-2020 The OpenSSL Project Authors. All Rights Reserved.
*
* Licensed under the Apache License 2.0 (the "License"). You may not use
* this file except in compliance with the License. You can obtain a copy
* in the file LICENSE in the source distribution or at
* https://www.openssl.org/source/license.html
*/
#include <assert.h>
/* For SSL3_VERSION, TLS1_VERSION etc */
#include <openssl/ssl.h>
#include <openssl/rand.h>
#include "internal/constant_time.h"
#include "ciphercommon_local.h"
#include "prov/providercommonerr.h"
/* Functions defined in ssl/tls_pad.c */
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,
OPENSSL_CTX *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,
OPENSSL_CTX *libctx);
/*
* Fills a single block of buffered data from the input, and returns the amount
* of data remaining in the input that is a multiple of the blocksize. The buffer
* is only filled if it already has some data in it, isn't full already or we
* don't have at least one block in the input.
*
* buf: a buffer of blocksize bytes
* buflen: contains the amount of data already in buf on entry. Updated with the
* amount of data in buf at the end. On entry *buflen must always be
* less than the blocksize
* blocksize: size of a block. Must be greater than 0 and a power of 2
* in: pointer to a pointer containing the input data
* inlen: amount of input data available
*
* On return buf is filled with as much data as possible up to a full block,
* *buflen is updated containing the amount of data in buf. *in is updated to
* the new location where input data should be read from, *inlen is updated with
* the remaining amount of data in *in. Returns the largest value <= *inlen
* which is a multiple of the blocksize.
*/
size_t 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;
}
/*
* Fills the buffer with trailing data from an encryption/decryption that didn't
* fit into a full block.
*/
int 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;
}
/* Pad the final block for encryption */
void 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 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;
}
/*
* The following assumes that the ciphertext has been authenticated.
* Otherwise it provides a padding oracle.
*/
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;
}
/*-
* tlsunpadblock removes the CBC padding from the decrypted, TLS, CBC
* record in constant time. Also removes the MAC from the record in constant
* time.
*
* libctx: Our library context
* tlsversion: The TLS version in use, e.g. SSL3_VERSION, TLS1_VERSION, etc
* buf: The decrypted TLS record data
* buflen: The length of the decrypted TLS record data. Updated with the new
* length after the padding is removed
* block_size: the block size of the cipher used to encrypt the record.
* mac: Location to store the pointer to the MAC
* alloced: Whether the MAC is stored in a newly allocated buffer, or whether
* *mac points into *buf
* macsize: the size of the MAC inside the record (or 0 if there isn't one)
* aead: whether this is an aead cipher
* returns:
* 0: (in non-constant time) if the record is publicly invalid.
* 1: (in constant time) Record is publicly valid. If padding is invalid then
* the mac is random
*/
int tlsunpadblock(OPENSSL_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:
/* Remove the explicit IV */
buf += blocksize;
*buflen -= blocksize;
/* Fall through */
case TLS1_VERSION:
ret = tls1_cbc_remove_padding_and_mac(buflen, *buflen, buf, mac,
alloced, blocksize, macsize,
aead, libctx);
return ret;
default:
return 0;
}
}