diff options
author | Matt Caswell <matt@openssl.org> | 2020-05-27 17:20:18 +0100 |
---|---|---|
committer | Matt Caswell <matt@openssl.org> | 2020-07-06 09:26:09 +0100 |
commit | e71fd827bcff720fb47e39c69cc468da9452935d (patch) | |
tree | 893f948c36b8236e59f8060fceb86253461d25b9 /providers | |
parent | f0237a6c6266535e105d6778ca7c34a080b88e92 (diff) |
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)
Diffstat (limited to 'providers')
4 files changed, 153 insertions, 0 deletions
diff --git a/providers/implementations/ciphers/ciphercommon.c b/providers/implementations/ciphers/ciphercommon.c index 93bee0dc0f..0b0219c7ad 100644 --- a/providers/implementations/ciphers/ciphercommon.c +++ b/providers/implementations/ciphers/ciphercommon.c @@ -24,6 +24,7 @@ static const OSSL_PARAM cipher_known_gettable_params[] = { OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_IVLEN, NULL), OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_BLOCK_SIZE, NULL), OSSL_PARAM_ulong(OSSL_CIPHER_PARAM_FLAGS, NULL), + { OSSL_CIPHER_PARAM_TLS_MAC, OSSL_PARAM_OCTET_PTR, NULL, 0, OSSL_PARAM_UNMODIFIED }, OSSL_PARAM_END }; const OSSL_PARAM *cipher_generic_gettable_params(void) @@ -69,6 +70,8 @@ CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_START(cipher_generic) CIPHER_DEFAULT_GETTABLE_CTX_PARAMS_END(cipher_generic) CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_START(cipher_generic) +OSSL_PARAM_uint(OSSL_CIPHER_PARAM_TLS_VERSION, NULL), +OSSL_PARAM_size_t(OSSL_CIPHER_PARAM_TLS_MAC_SIZE, NULL), CIPHER_DEFAULT_SETTABLE_CTX_PARAMS_END(cipher_generic) /* @@ -178,6 +181,41 @@ int cipher_generic_block_update(void *vctx, unsigned char *out, size_t *outl, size_t blksz = ctx->blocksize; size_t nextblocks; + if (ctx->tlsversion > 0) { + /* + * Each update call corresponds to a TLS record and is individually + * padded + */ + + /* Sanity check inputs */ + if (in == 0 + || (inl % blksz) != 0 + || in != out + || outsize < inl + || !ctx->pad) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + /* Shouldn't normally fail */ + if (!ctx->hw->cipher(ctx, out, in, inl)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + + /* This only fails if padding is publicly invalid */ + /* TODO(3.0): FIX ME FIX ME - Figure out aead */ + *outl = inl; + if (!ctx->enc + && !tlsunpadblock(ctx->libctx, ctx->tlsversion, out, outl, + blksz, &ctx->tlsmac, &ctx->alloced, + ctx->tlsmacsize, 0)) { + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + return 1; + } + if (ctx->bufsz != 0) nextblocks = fillblock(ctx->buf, &ctx->bufsz, blksz, &in, &inl); else @@ -238,6 +276,12 @@ int cipher_generic_block_final(void *vctx, unsigned char *out, size_t *outl, PROV_CIPHER_CTX *ctx = (PROV_CIPHER_CTX *)vctx; size_t blksz = ctx->blocksize; + if (ctx->tlsversion > 0) { + /* We never finalize TLS, so this is an error */ + ERR_raise(ERR_LIB_PROV, PROV_R_CIPHER_OPERATION_FAILED); + return 0; + } + if (ctx->enc) { if (ctx->pad) { padblock(ctx->buf, &ctx->bufsz, blksz); @@ -375,6 +419,12 @@ int cipher_generic_get_ctx_params(void *vctx, OSSL_PARAM params[]) ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); return 0; } + p = OSSL_PARAM_locate(params, OSSL_CIPHER_PARAM_TLS_MAC); + if (p != NULL + && !OSSL_PARAM_set_octet_ptr(p, ctx->tlsmac, ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_SET_PARAMETER); + return 0; + } return 1; } @@ -393,6 +443,20 @@ int cipher_generic_set_ctx_params(void *vctx, const OSSL_PARAM params[]) } ctx->pad = pad ? 1 : 0; } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_VERSION); + if (p != NULL) { + if (!OSSL_PARAM_get_uint(p, &ctx->tlsversion)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } + p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_TLS_MAC_SIZE); + if (p != NULL) { + if (!OSSL_PARAM_get_size_t(p, &ctx->tlsmacsize)) { + ERR_raise(ERR_LIB_PROV, PROV_R_FAILED_TO_GET_PARAMETER); + return 0; + } + } p = OSSL_PARAM_locate_const(params, OSSL_CIPHER_PARAM_NUM); if (p != NULL) { unsigned int num; diff --git a/providers/implementations/ciphers/ciphercommon_block.c b/providers/implementations/ciphers/ciphercommon_block.c index ac792d68d7..ba6f68eeff 100644 --- a/providers/implementations/ciphers/ciphercommon_block.c +++ b/providers/implementations/ciphers/ciphercommon_block.c @@ -8,9 +8,31 @@ */ #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 @@ -110,3 +132,56 @@ int unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize) *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; + } +} diff --git a/providers/implementations/ciphers/ciphercommon_local.h b/providers/implementations/ciphers/ciphercommon_local.h index 1c4716f357..43c1c192af 100644 --- a/providers/implementations/ciphers/ciphercommon_local.h +++ b/providers/implementations/ciphers/ciphercommon_local.h @@ -11,3 +11,6 @@ void padblock(unsigned char *buf, size_t *buflen, size_t blocksize); int unpadblock(unsigned char *buf, size_t *buflen, size_t blocksize); +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); diff --git a/providers/implementations/include/prov/ciphercommon.h b/providers/implementations/include/prov/ciphercommon.h index fa6eec6a27..18bb579026 100644 --- a/providers/implementations/include/prov/ciphercommon.h +++ b/providers/implementations/include/prov/ciphercommon.h @@ -31,6 +31,9 @@ typedef struct prov_cipher_ctx_st PROV_CIPHER_CTX; typedef int (PROV_CIPHER_HW_FN)(PROV_CIPHER_CTX *dat, unsigned char *out, const unsigned char *in, size_t len); +/* TODO(3.0): VERIFY ME */ +#define MAX_TLS_MAC_SIZE 48 + struct prov_cipher_ctx_st { block128_f block; union { @@ -48,6 +51,14 @@ struct prov_cipher_ctx_st { unsigned int enc : 1; /* Set to 1 for encrypt, or 0 otherwise */ unsigned int iv_set : 1; /* Set when the iv is copied to the iv/oiv buffers */ + unsigned int tlsversion; /* If TLS padding is in use the TLS version number */ + unsigned char *tlsmac; /* tls MAC extracted from the last record */ + int alloced; /* + * Whether the tlsmac data has been allocated or + * points into the user buffer. + */ + size_t tlsmacsize; /* Size of the TLS MAC */ + /* * num contains the number of bytes of |iv| which are valid for modes that * manage partial blocks themselves. |