Move mbedtls_cf_rsaes_pkcs1_v15_unpadding function to the constant-time module
Signed-off-by: Gabor Mezei <gabor.mezei@arm.com>
diff --git a/library/constant_time.c b/library/constant_time.c
index aa291d7..712008a 100644
--- a/library/constant_time.c
+++ b/library/constant_time.c
@@ -29,6 +29,7 @@
#include "mbedtls/ssl_internal.h"
#endif
+#include <string.h>
/* constant-time buffer comparison */
int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n )
@@ -710,3 +711,152 @@
}
#endif /* MBEDTLS_BIGNUM_C */
+
+#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
+
+int mbedtls_cf_rsaes_pkcs1_v15_unpadding( int mode,
+ size_t ilen,
+ size_t *olen,
+ unsigned char *output,
+ size_t output_max_len,
+ unsigned char *buf )
+{
+ 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 = mbedtls_cf_size_if( 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 |= buf[0];
+
+ if( mode == MBEDTLS_RSA_PRIVATE )
+ {
+ /* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
+ * where PS must be at least 8 nonzero bytes. */
+ bad |= buf[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 |= ((buf[i] | (unsigned char)-buf[i]) >> 7) ^ 1;
+ pad_count += ((pad_done | (unsigned char)-pad_done) >> 7) ^ 1;
+ }
+ }
+ else
+ {
+ /* Decode EMSA-PKCS1-v1_5 padding: 0x00 || 0x01 || PS || 0x00
+ * where PS must be at least 8 bytes with the value 0xFF. */
+ bad |= buf[1] ^ MBEDTLS_RSA_SIGN;
+
+ /* Read the whole buffer. Set pad_done to nonzero if we find
+ * the 0x00 byte and remember the padding length in pad_count.
+ * If there's a non-0xff byte in the padding, the padding is bad. */
+ for( i = 2; i < ilen; i++ )
+ {
+ pad_done |= mbedtls_cf_uint_if( buf[i], 0, 1 );
+ pad_count += mbedtls_cf_uint_if( pad_done, 0, 1 );
+ bad |= mbedtls_cf_uint_if( pad_done, 0, buf[i] ^ 0xFF );
+ }
+ }
+
+ /* If pad_done is still zero, there's no data, only unfinished padding. */
+ bad |= mbedtls_cf_uint_if( pad_done, 0, 1 );
+
+ /* There must be at least 8 bytes of padding. */
+ bad |= mbedtls_cf_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_cf_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_cf_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_cf_uint_if(
+ bad, - MBEDTLS_ERR_RSA_INVALID_PADDING,
+ mbedtls_cf_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_cf_uint_mask( bad | output_too_large );
+ for( i = 11; i < ilen; i++ )
+ buf[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_cf_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_cf_mem_move_to_left( buf + 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, buf + 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 */