From e71fd827bcff720fb47e39c69cc468da9452935d Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Wed, 27 May 2020 17:20:18 +0100 Subject: [PATCH] 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 (Merged from https://github.com/openssl/openssl/pull/12288) --- include/openssl/core_names.h | 43 ++++++----- .../implementations/ciphers/ciphercommon.c | 64 ++++++++++++++++ .../ciphers/ciphercommon_block.c | 75 +++++++++++++++++++ .../ciphers/ciphercommon_local.h | 3 + .../include/prov/ciphercommon.h | 11 +++ ssl/build.info | 2 + 6 files changed, 178 insertions(+), 20 deletions(-) diff --git a/include/openssl/core_names.h b/include/openssl/core_names.h index fe126ccd7a..9ad81337c3 100644 --- a/include/openssl/core_names.h +++ b/include/openssl/core_names.h @@ -42,27 +42,30 @@ extern "C" { #define OSSL_ALG_PARAM_PROPERTIES "properties"/* utf8_string */ /* cipher parameters */ -#define OSSL_CIPHER_PARAM_PADDING "padding" /* uint */ -#define OSSL_CIPHER_PARAM_MODE "mode" /* uint */ -#define OSSL_CIPHER_PARAM_BLOCK_SIZE "blocksize" /* size_t */ -#define OSSL_CIPHER_PARAM_FLAGS "flags" /* ulong */ -#define OSSL_CIPHER_PARAM_KEYLEN "keylen" /* size_t */ -#define OSSL_CIPHER_PARAM_IVLEN "ivlen" /* size_t */ -#define OSSL_CIPHER_PARAM_IV "iv" /* octet_string OR octet_ptr */ -#define OSSL_CIPHER_PARAM_NUM "num" /* uint */ -#define OSSL_CIPHER_PARAM_ROUNDS "rounds" /* uint */ -#define OSSL_CIPHER_PARAM_AEAD_TAG "tag" /* octet_string */ -#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD "tlsaad" /* octet_string */ -#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD "tlsaadpad" /* size_t */ -#define OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED "tlsivfixed" /* octet_string */ -#define OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN "tlsivgen" /* octet_string */ -#define OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV "tlsivinv" /* octet_string */ +#define OSSL_CIPHER_PARAM_PADDING "padding" /* uint */ +#define OSSL_CIPHER_PARAM_TLS_VERSION "tls-version" /* uint */ +#define OSSL_CIPHER_PARAM_TLS_MAC "tls-mac" /* octet_ptr */ +#define OSSL_CIPHER_PARAM_TLS_MAC_SIZE "tls-mac-size" /* size_t */ +#define OSSL_CIPHER_PARAM_MODE "mode" /* uint */ +#define OSSL_CIPHER_PARAM_BLOCK_SIZE "blocksize" /* size_t */ +#define OSSL_CIPHER_PARAM_FLAGS "flags" /* ulong */ +#define OSSL_CIPHER_PARAM_KEYLEN "keylen" /* size_t */ +#define OSSL_CIPHER_PARAM_IVLEN "ivlen" /* size_t */ +#define OSSL_CIPHER_PARAM_IV "iv" /* octet_string OR octet_ptr */ +#define OSSL_CIPHER_PARAM_NUM "num" /* uint */ +#define OSSL_CIPHER_PARAM_ROUNDS "rounds" /* uint */ +#define OSSL_CIPHER_PARAM_AEAD_TAG "tag" /* octet_string */ +#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD "tlsaad" /* octet_string */ +#define OSSL_CIPHER_PARAM_AEAD_TLS1_AAD_PAD "tlsaadpad" /* size_t */ +#define OSSL_CIPHER_PARAM_AEAD_TLS1_IV_FIXED "tlsivfixed" /* octet_string */ +#define OSSL_CIPHER_PARAM_AEAD_TLS1_GET_IV_GEN "tlsivgen" /* octet_string */ +#define OSSL_CIPHER_PARAM_AEAD_TLS1_SET_IV_INV "tlsivinv" /* octet_string */ #define OSSL_CIPHER_PARAM_AEAD_IVLEN OSSL_CIPHER_PARAM_IVLEN -#define OSSL_CIPHER_PARAM_AEAD_TAGLEN "taglen" /* size_t */ -#define OSSL_CIPHER_PARAM_AEAD_MAC_KEY "mackey" /* octet_string */ -#define OSSL_CIPHER_PARAM_RANDOM_KEY "randkey" /* octet_string */ -#define OSSL_CIPHER_PARAM_RC2_KEYBITS "keybits" /* size_t */ -#define OSSL_CIPHER_PARAM_SPEED "speed" /* uint */ +#define OSSL_CIPHER_PARAM_AEAD_TAGLEN "taglen" /* size_t */ +#define OSSL_CIPHER_PARAM_AEAD_MAC_KEY "mackey" /* octet_string */ +#define OSSL_CIPHER_PARAM_RANDOM_KEY "randkey" /* octet_string */ +#define OSSL_CIPHER_PARAM_RC2_KEYBITS "keybits" /* size_t */ +#define OSSL_CIPHER_PARAM_SPEED "speed" /* uint */ /* For passing the AlgorithmIdentifier parameter in DER form */ #define OSSL_CIPHER_PARAM_ALG_ID "alg_id_param" /* octet_string */ 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 +/* For SSL3_VERSION, TLS1_VERSION etc */ +#include +#include +#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. diff --git a/ssl/build.info b/ssl/build.info index a66e0d4bdb..fd187ac7e5 100644 --- a/ssl/build.info +++ b/ssl/build.info @@ -29,3 +29,5 @@ SOURCE[../libssl]=\ record/ssl3_buffer.c record/ssl3_record.c record/dtls1_bitmap.c \ statem/statem.c record/ssl3_record_tls13.c record/tls_pad.c DEFINE[../libssl]=$AESDEF + +SOURCE[../providers/libcommon.a]=record/tls_pad.c