From 19e8cd06febe82340cf6b74fd4f467ab098f6fd1 Mon Sep 17 00:00:00 2001 From: Dave Rodgman Date: Tue, 9 May 2023 11:10:21 +0100 Subject: [PATCH] Move mbedtls_ct_rsaes_pkcs1_v15_unpadding into rsa.c Signed-off-by: Dave Rodgman --- library/constant_time.c | 135 +------------------------- library/constant_time_internal.h | 40 +------- library/rsa.c | 158 +++++++++++++++++++++++++++++++ 3 files changed, 162 insertions(+), 171 deletions(-) diff --git a/library/constant_time.c b/library/constant_time.c index 093ae28726..16d7a1fc41 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -334,8 +334,8 @@ void mbedtls_ct_mpi_uint_cond_assign(size_t n, #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) void mbedtls_ct_mem_move_to_left(void *start, - size_t total, - size_t offset) + size_t total, + size_t offset) { volatile unsigned char *buf = start; size_t i, n; @@ -590,134 +590,3 @@ int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X, } #endif /* MBEDTLS_BIGNUM_C */ - -#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) - -int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input, - size_t ilen, - unsigned char *output, - size_t output_max_len, - size_t *olen) -{ - int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; - size_t i, plaintext_max_size; - - /* The following variables take sensitive values: their value must - * not leak into the observable behavior of the function other than - * the designated outputs (output, olen, return value). Otherwise - * this would open the execution of the function to - * side-channel-based variants of the Bleichenbacher padding oracle - * attack. Potential side channels include overall timing, memory - * access patterns (especially visible to an adversary who has access - * to a shared memory cache), and branches (especially visible to - * an adversary who has access to a shared code cache or to a shared - * branch predictor). */ - size_t pad_count = 0; - unsigned bad = 0; - unsigned char pad_done = 0; - size_t plaintext_size = 0; - unsigned output_too_large; - - plaintext_max_size = (output_max_len > ilen - 11) ? ilen - 11 - : output_max_len; - - /* Check and get padding length in constant time and constant - * memory trace. The first byte must be 0. */ - bad |= input[0]; - - - /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00 - * where PS must be at least 8 nonzero bytes. */ - bad |= input[1] ^ MBEDTLS_RSA_CRYPT; - - /* Read the whole buffer. Set pad_done to nonzero if we find - * the 0x00 byte and remember the padding length in pad_count. */ - for (i = 2; i < ilen; i++) { - pad_done |= ((input[i] | (unsigned char) -input[i]) >> 7) ^ 1; - pad_count += ((pad_done | (unsigned char) -pad_done) >> 7) ^ 1; - } - - - /* If pad_done is still zero, there's no data, only unfinished padding. */ - bad |= mbedtls_ct_uint_if(pad_done, 0, 1); - - /* There must be at least 8 bytes of padding. */ - bad |= mbedtls_ct_size_gt(8, pad_count); - - /* If the padding is valid, set plaintext_size to the number of - * remaining bytes after stripping the padding. If the padding - * is invalid, avoid leaking this fact through the size of the - * output: use the maximum message size that fits in the output - * buffer. Do it without branches to avoid leaking the padding - * validity through timing. RSA keys are small enough that all the - * size_t values involved fit in unsigned int. */ - plaintext_size = mbedtls_ct_uint_if( - bad, (unsigned) plaintext_max_size, - (unsigned) (ilen - pad_count - 3)); - - /* Set output_too_large to 0 if the plaintext fits in the output - * buffer and to 1 otherwise. */ - output_too_large = mbedtls_ct_size_gt(plaintext_size, - plaintext_max_size); - - /* Set ret without branches to avoid timing attacks. Return: - * - INVALID_PADDING if the padding is bad (bad != 0). - * - OUTPUT_TOO_LARGE if the padding is good but the decrypted - * plaintext does not fit in the output buffer. - * - 0 if the padding is correct. */ - ret = -(int) mbedtls_ct_uint_if( - bad, -MBEDTLS_ERR_RSA_INVALID_PADDING, - mbedtls_ct_uint_if(output_too_large, - -MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE, - 0)); - - /* If the padding is bad or the plaintext is too large, zero the - * data that we're about to copy to the output buffer. - * We need to copy the same amount of data - * from the same buffer whether the padding is good or not to - * avoid leaking the padding validity through overall timing or - * through memory or cache access patterns. */ - bad = mbedtls_ct_uint_mask(bad | output_too_large); - for (i = 11; i < ilen; i++) { - input[i] &= ~bad; - } - - /* If the plaintext is too large, truncate it to the buffer size. - * Copy anyway to avoid revealing the length through timing, because - * revealing the length is as bad as revealing the padding validity - * for a Bleichenbacher attack. */ - plaintext_size = mbedtls_ct_uint_if(output_too_large, - (unsigned) plaintext_max_size, - (unsigned) plaintext_size); - - /* Move the plaintext to the leftmost position where it can start in - * the working buffer, i.e. make it start plaintext_max_size from - * the end of the buffer. Do this with a memory access trace that - * does not depend on the plaintext size. After this move, the - * starting location of the plaintext is no longer sensitive - * information. */ - mbedtls_ct_mem_move_to_left(input + ilen - plaintext_max_size, - plaintext_max_size, - plaintext_max_size - plaintext_size); - - /* Finally copy the decrypted plaintext plus trailing zeros into the output - * buffer. If output_max_len is 0, then output may be an invalid pointer - * and the result of memcpy() would be undefined; prevent undefined - * behavior making sure to depend only on output_max_len (the size of the - * user-provided output buffer), which is independent from plaintext - * length, validity of padding, success of the decryption, and other - * secrets. */ - if (output_max_len != 0) { - memcpy(output, input + ilen - plaintext_max_size, plaintext_max_size); - } - - /* Report the amount of data we copied to the output buffer. In case - * of errors (bad padding or output too large), the value of *olen - * when this function returns is not specified. Making it equivalent - * to the good case limits the risks of leaking the padding validity. */ - *olen = plaintext_size; - - return ret; -} - -#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h index 84ca96233f..bfeb8c55ec 100644 --- a/library/constant_time_internal.h +++ b/library/constant_time_internal.h @@ -238,42 +238,6 @@ void mbedtls_ct_memcpy_offset(unsigned char *dest, #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ -#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) - -/** This function performs the unpadding part of a PKCS#1 v1.5 decryption - * operation (EME-PKCS1-v1_5 decoding). - * - * \note The return value from this function is a sensitive value - * (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen - * in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING - * is often a situation that an attacker can provoke and leaking which - * one is the result is precisely the information the attacker wants. - * - * \param input The input buffer which is the payload inside PKCS#1v1.5 - * encryption padding, called the "encoded message EM" - * by the terminology. - * \param ilen The length of the payload in the \p input buffer. - * \param output The buffer for the payload, called "message M" by the - * PKCS#1 terminology. This must be a writable buffer of - * length \p output_max_len bytes. - * \param olen The address at which to store the length of - * the payload. This must not be \c NULL. - * \param output_max_len The length in bytes of the output buffer \p output. - * - * \return \c 0 on success. - * \return #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE - * The output buffer is too small for the unpadded payload. - * \return #MBEDTLS_ERR_RSA_INVALID_PADDING - * The input doesn't contain properly formatted padding. - */ -int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input, - size_t ilen, - unsigned char *output, - size_t output_max_len, - size_t *olen); - -#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ - #if defined(MBEDTLS_BASE64_C) /** Constant-flow char selection @@ -333,8 +297,8 @@ unsigned mbedtls_ct_size_gt(size_t x, size_t y); * \param offset Offset from which to copy \p total - \p offset bytes. */ void mbedtls_ct_mem_move_to_left(void *start, - size_t total, - size_t offset); + size_t total, + size_t offset); #endif /* defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) */ diff --git a/library/rsa.c b/library/rsa.c index 87b3311899..e4a45b37c1 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -56,6 +56,164 @@ #include "mbedtls/platform.h" + +#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) + +/** This function performs the unpadding part of a PKCS#1 v1.5 decryption + * operation (EME-PKCS1-v1_5 decoding). + * + * \note The return value from this function is a sensitive value + * (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen + * in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING + * is often a situation that an attacker can provoke and leaking which + * one is the result is precisely the information the attacker wants. + * + * \param input The input buffer which is the payload inside PKCS#1v1.5 + * encryption padding, called the "encoded message EM" + * by the terminology. + * \param ilen The length of the payload in the \p input buffer. + * \param output The buffer for the payload, called "message M" by the + * PKCS#1 terminology. This must be a writable buffer of + * length \p output_max_len bytes. + * \param olen The address at which to store the length of + * the payload. This must not be \c NULL. + * \param output_max_len The length in bytes of the output buffer \p output. + * + * \return \c 0 on success. + * \return #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE + * The output buffer is too small for the unpadded payload. + * \return #MBEDTLS_ERR_RSA_INVALID_PADDING + * The input doesn't contain properly formatted padding. + */ +static int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input, + size_t ilen, + unsigned char *output, + size_t output_max_len, + size_t *olen) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + size_t i, plaintext_max_size; + + /* The following variables take sensitive values: their value must + * not leak into the observable behavior of the function other than + * the designated outputs (output, olen, return value). Otherwise + * this would open the execution of the function to + * side-channel-based variants of the Bleichenbacher padding oracle + * attack. Potential side channels include overall timing, memory + * access patterns (especially visible to an adversary who has access + * to a shared memory cache), and branches (especially visible to + * an adversary who has access to a shared code cache or to a shared + * branch predictor). */ + size_t pad_count = 0; + unsigned bad = 0; + unsigned char pad_done = 0; + size_t plaintext_size = 0; + unsigned output_too_large; + + plaintext_max_size = (output_max_len > ilen - 11) ? ilen - 11 + : output_max_len; + + /* Check and get padding length in constant time and constant + * memory trace. The first byte must be 0. */ + bad |= input[0]; + + + /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00 + * where PS must be at least 8 nonzero bytes. */ + bad |= input[1] ^ MBEDTLS_RSA_CRYPT; + + /* Read the whole buffer. Set pad_done to nonzero if we find + * the 0x00 byte and remember the padding length in pad_count. */ + for (i = 2; i < ilen; i++) { + pad_done |= ((input[i] | (unsigned char) -input[i]) >> 7) ^ 1; + pad_count += ((pad_done | (unsigned char) -pad_done) >> 7) ^ 1; + } + + + /* If pad_done is still zero, there's no data, only unfinished padding. */ + bad |= mbedtls_ct_uint_if(pad_done, 0, 1); + + /* There must be at least 8 bytes of padding. */ + bad |= mbedtls_ct_size_gt(8, pad_count); + + /* If the padding is valid, set plaintext_size to the number of + * remaining bytes after stripping the padding. If the padding + * is invalid, avoid leaking this fact through the size of the + * output: use the maximum message size that fits in the output + * buffer. Do it without branches to avoid leaking the padding + * validity through timing. RSA keys are small enough that all the + * size_t values involved fit in unsigned int. */ + plaintext_size = mbedtls_ct_uint_if( + bad, (unsigned) plaintext_max_size, + (unsigned) (ilen - pad_count - 3)); + + /* Set output_too_large to 0 if the plaintext fits in the output + * buffer and to 1 otherwise. */ + output_too_large = mbedtls_ct_size_gt(plaintext_size, + plaintext_max_size); + + /* Set ret without branches to avoid timing attacks. Return: + * - INVALID_PADDING if the padding is bad (bad != 0). + * - OUTPUT_TOO_LARGE if the padding is good but the decrypted + * plaintext does not fit in the output buffer. + * - 0 if the padding is correct. */ + ret = -(int) mbedtls_ct_uint_if( + bad, -MBEDTLS_ERR_RSA_INVALID_PADDING, + mbedtls_ct_uint_if(output_too_large, + -MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE, + 0)); + + /* If the padding is bad or the plaintext is too large, zero the + * data that we're about to copy to the output buffer. + * We need to copy the same amount of data + * from the same buffer whether the padding is good or not to + * avoid leaking the padding validity through overall timing or + * through memory or cache access patterns. */ + bad = mbedtls_ct_uint_mask(bad | output_too_large); + for (i = 11; i < ilen; i++) { + input[i] &= ~bad; + } + + /* If the plaintext is too large, truncate it to the buffer size. + * Copy anyway to avoid revealing the length through timing, because + * revealing the length is as bad as revealing the padding validity + * for a Bleichenbacher attack. */ + plaintext_size = mbedtls_ct_uint_if(output_too_large, + (unsigned) plaintext_max_size, + (unsigned) plaintext_size); + + /* Move the plaintext to the leftmost position where it can start in + * the working buffer, i.e. make it start plaintext_max_size from + * the end of the buffer. Do this with a memory access trace that + * does not depend on the plaintext size. After this move, the + * starting location of the plaintext is no longer sensitive + * information. */ + mbedtls_ct_mem_move_to_left(input + ilen - plaintext_max_size, + plaintext_max_size, + plaintext_max_size - plaintext_size); + + /* Finally copy the decrypted plaintext plus trailing zeros into the output + * buffer. If output_max_len is 0, then output may be an invalid pointer + * and the result of memcpy() would be undefined; prevent undefined + * behavior making sure to depend only on output_max_len (the size of the + * user-provided output buffer), which is independent from plaintext + * length, validity of padding, success of the decryption, and other + * secrets. */ + if (output_max_len != 0) { + memcpy(output, input + ilen - plaintext_max_size, plaintext_max_size); + } + + /* Report the amount of data we copied to the output buffer. In case + * of errors (bad padding or output too large), the value of *olen + * when this function returns is not specified. Making it equivalent + * to the good case limits the risks of leaking the padding validity. */ + *olen = plaintext_size; + + return ret; +} + +#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */ + #if !defined(MBEDTLS_RSA_ALT) int mbedtls_rsa_import(mbedtls_rsa_context *ctx,