From c89c21f8f59d4973b7ccda6d6aaa2756636e2121 Mon Sep 17 00:00:00 2001 From: Matt Caswell Date: Tue, 1 Nov 2022 15:57:56 +0000 Subject: Move tls_pad.c into ssl/record/methods This file is used by libssl record layer methods and therefore should now be in the methods subdir Reviewed-by: Hugo Landau Reviewed-by: Tomas Mraz (Merged from https://github.com/openssl/openssl/pull/19586) --- ssl/record/build.info | 6 - ssl/record/methods/build.info | 7 + ssl/record/methods/tls_pad.c | 325 ++++++++++++++++++++++++++++++++++++++++++ ssl/record/tls_pad.c | 325 ------------------------------------------ 4 files changed, 332 insertions(+), 331 deletions(-) create mode 100644 ssl/record/methods/tls_pad.c delete mode 100644 ssl/record/tls_pad.c (limited to 'ssl') diff --git a/ssl/record/build.info b/ssl/record/build.info index 13238863a6..42bc3f3611 100644 --- a/ssl/record/build.info +++ b/ssl/record/build.info @@ -13,11 +13,5 @@ ENDIF SOURCE[../../libssl]=\ rec_layer_s3.c rec_layer_d1.c ssl3_record.c -# For shared builds we need to include the sources needed in providers -# (tls_pad.c) in libssl as well. -SHARED_SOURCE[../../libssl]=tls_pad.c - -SOURCE[../../providers/libcommon.a]=tls_pad.c - DEFINE[../../libssl]=$AESDEF diff --git a/ssl/record/methods/build.info b/ssl/record/methods/build.info index f31acd10b9..8b1af5dd5d 100644 --- a/ssl/record/methods/build.info +++ b/ssl/record/methods/build.info @@ -14,3 +14,10 @@ IF[{- !$disabled{'deprecated-3.0'} -}] ENDIF SOURCE[../../../providers/libdefault.a ../../../providers/libfips.a]=ssl3_cbc.c + +# For shared builds we need to include the sources needed in providers +# (tls_pad.c) in libssl as well. +SHARED_SOURCE[../../../libssl]=tls_pad.c + +SOURCE[../../../providers/libcommon.a]=tls_pad.c + diff --git a/ssl/record/methods/tls_pad.c b/ssl/record/methods/tls_pad.c new file mode 100644 index 0000000000..7311c8266a --- /dev/null +++ b/ssl/record/methods/tls_pad.c @@ -0,0 +1,325 @@ +/* + * Copyright 1995-2021 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 +#include +#include "internal/constant_time.h" +#include "internal/cryptlib.h" + +/* + * This file has no dependencies on the rest of libssl because it is shared + * with the providers. It contains functions for low level CBC TLS padding + * removal. Responsibility for this lies with the cipher implementations in the + * providers. However there are legacy code paths in libssl which also need to + * do this. In time those legacy code paths can be removed and this file can be + * moved out of libssl. + */ + +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); + +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); + +/*- + * ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC + * record in |recdata| by updating |reclen| in constant time. It also extracts + * the MAC from the underlying record and places a pointer to it in |mac|. The + * MAC data can either be newly allocated memory, or a pointer inside the + * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is + * set to 0. + * + * origreclen: the original record length before any changes were made + * block_size: the block size of the cipher used to encrypt the record. + * mac_size: the size of the MAC to be extracted + * aead: 1 if an AEAD cipher is in use, or 0 otherwise + * returns: + * 0: if the record is publicly invalid. + * 1: if the record is publicly valid. If the padding removal fails then the + * MAC returned is random. + */ +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 /* padding length byte */ + mac_size; + + /* + * These lengths are all public so we can test them in non-constant time. + */ + if (overhead > *reclen) + return 0; + + padding_length = recdata[*reclen - 1]; + good = constant_time_ge_s(*reclen, padding_length + overhead); + /* SSLv3 requires that the padding is minimal. */ + 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); +} + +/*- + * tls1_cbc_remove_padding_and_mac removes padding from the decrypted, TLS, CBC + * record in |recdata| by updating |reclen| in constant time. It also extracts + * the MAC from the underlying record and places a pointer to it in |mac|. The + * MAC data can either be newly allocated memory, or a pointer inside the + * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is + * set to 0. + * + * origreclen: the original record length before any changes were made + * block_size: the block size of the cipher used to encrypt the record. + * mac_size: the size of the MAC to be extracted + * aead: 1 if an AEAD cipher is in use, or 0 otherwise + * returns: + * 0: if the record is publicly invalid. + * 1: if the record is publicly valid. If the padding removal fails then the + * MAC returned is random. + */ +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) /* padding length byte */ + + mac_size; + + /* + * These lengths are all public so we can test them in non-constant + * time. + */ + if (overhead > *reclen) + return 0; + + if (block_size != 1) { + + padding_length = recdata[*reclen - 1]; + + if (aead) { + /* padding is already verified and we don't need to check the MAC */ + *reclen -= padding_length + 1 + mac_size; + return 1; + } + + good = constant_time_ge_s(*reclen, overhead + padding_length); + /* + * The padding consists of a length byte at the end of the record and + * then that many bytes of padding, all with the same value as the + * length byte. Thus, with the length byte included, there are i+1 bytes + * of padding. We can't check just |padding_length+1| bytes because that + * leaks decrypted information. Therefore we always have to check the + * maximum amount of padding possible. (Again, the length of the record + * is public information so we can use it.) + */ + to_check = 256; /* maximum amount of padding, inc length byte. */ + 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]; + /* + * The final |padding_length+1| bytes should all have the value + * |padding_length|. Therefore the XOR should be zero. + */ + good &= ~(mask & (padding_length ^ b)); + } + + /* + * If any of the final |padding_length+1| bytes had the wrong value, one + * or more of the lower eight bits of |good| will be cleared. + */ + 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); +} + +/*- + * ssl3_cbc_copy_mac copies |md_size| bytes from the end of the record in + * |recdata| to |*mac| in constant time (independent of the concrete value of + * the record length |reclen|, which may vary within a 256-byte window). + * + * On entry: + * origreclen >= mac_size + * mac_size <= EVP_MAX_MD_SIZE + * + * If CBC_MAC_ROTATE_IN_PLACE is defined then the rotation is performed with + * variable accesses in a 64-byte-aligned buffer. Assuming that this fits into + * a single or pair of cache-lines, then the variable memory accesses don't + * actually affect the timing. CPUs with smaller cache-lines [if any] are + * not multi-core and are not considered vulnerable to cache-timing attacks. + */ +#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; + + /* + * mac_end is the index of |recdata| just after the end of the MAC. + */ + size_t mac_end = *reclen; + size_t mac_start = mac_end - mac_size; + size_t in_mac; + /* + * scan_start contains the number of bytes that we can ignore because the + * MAC's position can only vary by 255 bytes. + */ + 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 no MAC then nothing to be done */ + if (mac_size == 0) { + /* No MAC so we can do this in non-constant time */ + if (good == 0) + return 0; + return 1; + } + + *reclen -= mac_size; + + if (block_size == 1) { + /* There's no padding so the position of the MAC is fixed */ + if (mac != NULL) + *mac = &recdata[*reclen]; + if (alloced != NULL) + *alloced = 0; + return 1; + } + + /* Create the random MAC we will emit if padding is bad */ + 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 + + /* This information is public so it's safe to branch based on it. */ + 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); + } + + /* Now rotate the MAC */ +#if defined(CBC_MAC_ROTATE_IN_PLACE) + j = 0; + for (i = 0; i < mac_size; i++) { + /* + * in case cache-line is 32 bytes, + * load from both lines and select appropriately + */ + 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++; + + /* If the padding wasn't good we emit a random MAC */ + 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); + + /* If the padding wasn't good we emit a random MAC */ + out[i] = constant_time_select_8((unsigned char)(good & 0xff), out[i], + randmac[i]); + } +#endif + + return 1; +} diff --git a/ssl/record/tls_pad.c b/ssl/record/tls_pad.c deleted file mode 100644 index 7311c8266a..0000000000 --- a/ssl/record/tls_pad.c +++ /dev/null @@ -1,325 +0,0 @@ -/* - * Copyright 1995-2021 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 -#include -#include "internal/constant_time.h" -#include "internal/cryptlib.h" - -/* - * This file has no dependencies on the rest of libssl because it is shared - * with the providers. It contains functions for low level CBC TLS padding - * removal. Responsibility for this lies with the cipher implementations in the - * providers. However there are legacy code paths in libssl which also need to - * do this. In time those legacy code paths can be removed and this file can be - * moved out of libssl. - */ - -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); - -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); - -/*- - * ssl3_cbc_remove_padding removes padding from the decrypted, SSLv3, CBC - * record in |recdata| by updating |reclen| in constant time. It also extracts - * the MAC from the underlying record and places a pointer to it in |mac|. The - * MAC data can either be newly allocated memory, or a pointer inside the - * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is - * set to 0. - * - * origreclen: the original record length before any changes were made - * block_size: the block size of the cipher used to encrypt the record. - * mac_size: the size of the MAC to be extracted - * aead: 1 if an AEAD cipher is in use, or 0 otherwise - * returns: - * 0: if the record is publicly invalid. - * 1: if the record is publicly valid. If the padding removal fails then the - * MAC returned is random. - */ -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 /* padding length byte */ + mac_size; - - /* - * These lengths are all public so we can test them in non-constant time. - */ - if (overhead > *reclen) - return 0; - - padding_length = recdata[*reclen - 1]; - good = constant_time_ge_s(*reclen, padding_length + overhead); - /* SSLv3 requires that the padding is minimal. */ - 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); -} - -/*- - * tls1_cbc_remove_padding_and_mac removes padding from the decrypted, TLS, CBC - * record in |recdata| by updating |reclen| in constant time. It also extracts - * the MAC from the underlying record and places a pointer to it in |mac|. The - * MAC data can either be newly allocated memory, or a pointer inside the - * |recdata| buffer. If allocated then |*alloced| is set to 1, otherwise it is - * set to 0. - * - * origreclen: the original record length before any changes were made - * block_size: the block size of the cipher used to encrypt the record. - * mac_size: the size of the MAC to be extracted - * aead: 1 if an AEAD cipher is in use, or 0 otherwise - * returns: - * 0: if the record is publicly invalid. - * 1: if the record is publicly valid. If the padding removal fails then the - * MAC returned is random. - */ -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) /* padding length byte */ - + mac_size; - - /* - * These lengths are all public so we can test them in non-constant - * time. - */ - if (overhead > *reclen) - return 0; - - if (block_size != 1) { - - padding_length = recdata[*reclen - 1]; - - if (aead) { - /* padding is already verified and we don't need to check the MAC */ - *reclen -= padding_length + 1 + mac_size; - return 1; - } - - good = constant_time_ge_s(*reclen, overhead + padding_length); - /* - * The padding consists of a length byte at the end of the record and - * then that many bytes of padding, all with the same value as the - * length byte. Thus, with the length byte included, there are i+1 bytes - * of padding. We can't check just |padding_length+1| bytes because that - * leaks decrypted information. Therefore we always have to check the - * maximum amount of padding possible. (Again, the length of the record - * is public information so we can use it.) - */ - to_check = 256; /* maximum amount of padding, inc length byte. */ - 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]; - /* - * The final |padding_length+1| bytes should all have the value - * |padding_length|. Therefore the XOR should be zero. - */ - good &= ~(mask & (padding_length ^ b)); - } - - /* - * If any of the final |padding_length+1| bytes had the wrong value, one - * or more of the lower eight bits of |good| will be cleared. - */ - 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); -} - -/*- - * ssl3_cbc_copy_mac copies |md_size| bytes from the end of the record in - * |recdata| to |*mac| in constant time (independent of the concrete value of - * the record length |reclen|, which may vary within a 256-byte window). - * - * On entry: - * origreclen >= mac_size - * mac_size <= EVP_MAX_MD_SIZE - * - * If CBC_MAC_ROTATE_IN_PLACE is defined then the rotation is performed with - * variable accesses in a 64-byte-aligned buffer. Assuming that this fits into - * a single or pair of cache-lines, then the variable memory accesses don't - * actually affect the timing. CPUs with smaller cache-lines [if any] are - * not multi-core and are not considered vulnerable to cache-timing attacks. - */ -#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; - - /* - * mac_end is the index of |recdata| just after the end of the MAC. - */ - size_t mac_end = *reclen; - size_t mac_start = mac_end - mac_size; - size_t in_mac; - /* - * scan_start contains the number of bytes that we can ignore because the - * MAC's position can only vary by 255 bytes. - */ - 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 no MAC then nothing to be done */ - if (mac_size == 0) { - /* No MAC so we can do this in non-constant time */ - if (good == 0) - return 0; - return 1; - } - - *reclen -= mac_size; - - if (block_size == 1) { - /* There's no padding so the position of the MAC is fixed */ - if (mac != NULL) - *mac = &recdata[*reclen]; - if (alloced != NULL) - *alloced = 0; - return 1; - } - - /* Create the random MAC we will emit if padding is bad */ - 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 - - /* This information is public so it's safe to branch based on it. */ - 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); - } - - /* Now rotate the MAC */ -#if defined(CBC_MAC_ROTATE_IN_PLACE) - j = 0; - for (i = 0; i < mac_size; i++) { - /* - * in case cache-line is 32 bytes, - * load from both lines and select appropriately - */ - 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++; - - /* If the padding wasn't good we emit a random MAC */ - 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); - - /* If the padding wasn't good we emit a random MAC */ - out[i] = constant_time_select_8((unsigned char)(good & 0xff), out[i], - randmac[i]); - } -#endif - - return 1; -} -- cgit v1.2.3