/** * Constant-time functions * * Copyright The Mbed TLS Contributors * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ #ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H #define MBEDTLS_CONSTANT_TIME_INTERNAL_H #include #include #include "common.h" #if defined(MBEDTLS_BIGNUM_C) #include "mbedtls/bignum.h" #endif /* The constant-time interface provides various operations that are likely * to result in constant-time code that does not branch or use conditional * instructions for secret data (for secret pointers, this also applies to * the data pointed to). * * It has three main parts: * * - boolean operations * These are all named mbedtls_ct__. * They operate over and return mbedtls_ct_condition_t. * All arguments are considered secret. * example: bool x = y | z => x = mbedtls_ct_bool_or(y, z) * example: bool x = y == z => x = mbedtls_ct_uint_eq(y, z) * * - conditional data selection * These are all named mbedtls_ct__if and mbedtls_ct__if_else_0 * All arguments are considered secret. * example: size_t a = x ? b : c => a = mbedtls_ct_size_if(x, b, c) * example: unsigned a = x ? b : 0 => a = mbedtls_ct_uint_if_else_0(x, b) * * - block memory operations * Only some arguments are considered secret, as documented for each * function. * example: if (x) memcpy(...) => mbedtls_ct_memcpy_if(x, ...) * * mbedtls_ct_condition_t must be treated as opaque and only created and * manipulated via the functions in this header. The compiler should never * be able to prove anything about its value at compile-time. * * mbedtls_ct_uint_t is an unsigned integer type over which constant time * operations may be performed via the functions in this header. It is as big * as the larger of size_t and mbedtls_mpi_uint, i.e. it is safe to cast * to/from "unsigned int", "size_t", and "mbedtls_mpi_uint" (and any other * not-larger integer types). * * For Arm (32-bit, 64-bit and Thumb), x86 and x86-64, assembly implementations * are used to ensure that the generated code is constant time. For other * architectures, it uses a plain C fallback designed to yield constant-time code * (this has been observed to be constant-time on latest gcc, clang and MSVC * as of May 2023). * * For readability, the static inline definitions are separated out into * constant_time_impl.h. */ #if (SIZE_MAX > 0xffffffffffffffffULL) /* Pointer size > 64-bit */ typedef size_t mbedtls_ct_condition_t; typedef size_t mbedtls_ct_uint_t; typedef ptrdiff_t mbedtls_ct_int_t; #define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(SIZE_MAX)) #elif (SIZE_MAX > 0xffffffff) || defined(MBEDTLS_HAVE_INT64) /* 32-bit < pointer size <= 64-bit, or 64-bit MPI */ typedef uint64_t mbedtls_ct_condition_t; typedef uint64_t mbedtls_ct_uint_t; typedef int64_t mbedtls_ct_int_t; #define MBEDTLS_CT_SIZE_64 #define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT64_MAX)) #else /* Pointer size <= 32-bit, and no 64-bit MPIs */ typedef uint32_t mbedtls_ct_condition_t; typedef uint32_t mbedtls_ct_uint_t; typedef int32_t mbedtls_ct_int_t; #define MBEDTLS_CT_SIZE_32 #define MBEDTLS_CT_TRUE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(UINT32_MAX)) #endif #define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(0)) /* ============================================================================ * Boolean operations */ /** Convert a number into a mbedtls_ct_condition_t. * * \param x Number to convert. * * \return MBEDTLS_CT_TRUE if \p x != 0, or MBEDTLS_CT_FALSE if \p x == 0 * */ static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x); /** Boolean "not equal" operation. * * Functionally equivalent to: * * \p x != \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x != \p y, otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean "equals" operation. * * Functionally equivalent to: * * \p x == \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x == \p y, otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_eq(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean "less than" operation. * * Functionally equivalent to: * * \p x < \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x < \p y, otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean "greater than" operation. * * Functionally equivalent to: * * \p x > \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x > \p y, otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_gt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean "greater or equal" operation. * * Functionally equivalent to: * * \p x >= \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x >= \p y, * otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_ge(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean "less than or equal" operation. * * Functionally equivalent to: * * \p x <= \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x <= \p y, * otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_uint_le(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y); /** Boolean not-equals operation. * * Functionally equivalent to: * * \p x != \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \note This is more efficient than mbedtls_ct_uint_ne if both arguments are * mbedtls_ct_condition_t. * * \return MBEDTLS_CT_TRUE if \p x != \p y, * otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_ne(mbedtls_ct_condition_t x, mbedtls_ct_condition_t y); /** Boolean "and" operation. * * Functionally equivalent to: * * \p x && \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x && \p y, * otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_and(mbedtls_ct_condition_t x, mbedtls_ct_condition_t y); /** Boolean "or" operation. * * Functionally equivalent to: * * \p x || \p y * * \param x The first value to analyze. * \param y The second value to analyze. * * \return MBEDTLS_CT_TRUE if \p x || \p y, * otherwise MBEDTLS_CT_FALSE. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_or(mbedtls_ct_condition_t x, mbedtls_ct_condition_t y); /** Boolean "not" operation. * * Functionally equivalent to: * * ! \p x * * \param x The value to invert * * \return MBEDTLS_CT_FALSE if \p x, otherwise MBEDTLS_CT_TRUE. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_not(mbedtls_ct_condition_t x); /* ============================================================================ * Data selection operations */ /** Choose between two size_t values. * * Functionally equivalent to: * * condition ? if1 : if0. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * \param if0 Value to use if \p condition == MBEDTLS_CT_FALSE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0. */ static inline size_t mbedtls_ct_size_if(mbedtls_ct_condition_t condition, size_t if1, size_t if0); /** Choose between two unsigned values. * * Functionally equivalent to: * * condition ? if1 : if0. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * \param if0 Value to use if \p condition == MBEDTLS_CT_FALSE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0. */ static inline unsigned mbedtls_ct_uint_if(mbedtls_ct_condition_t condition, unsigned if1, unsigned if0); /** Choose between two mbedtls_ct_condition_t values. * * Functionally equivalent to: * * condition ? if1 : if0. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * \param if0 Value to use if \p condition == MBEDTLS_CT_FALSE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_if(mbedtls_ct_condition_t condition, mbedtls_ct_condition_t if1, mbedtls_ct_condition_t if0); #if defined(MBEDTLS_BIGNUM_C) /** Choose between two mbedtls_mpi_uint values. * * Functionally equivalent to: * * condition ? if1 : if0. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * \param if0 Value to use if \p condition == MBEDTLS_CT_FALSE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0. */ static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if(mbedtls_ct_condition_t condition, \ mbedtls_mpi_uint if1, \ mbedtls_mpi_uint if0); #endif /** Choose between an unsigned value and 0. * * Functionally equivalent to: * * condition ? if1 : 0. * * Functionally equivalent to mbedtls_ct_uint_if(condition, if1, 0) but * results in smaller code size. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0. */ static inline unsigned mbedtls_ct_uint_if_else_0(mbedtls_ct_condition_t condition, unsigned if1); /** Choose between an mbedtls_ct_condition_t and 0. * * Functionally equivalent to: * * condition ? if1 : 0. * * Functionally equivalent to mbedtls_ct_bool_if(condition, if1, 0) but * results in smaller code size. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0. */ static inline mbedtls_ct_condition_t mbedtls_ct_bool_if_else_0(mbedtls_ct_condition_t condition, mbedtls_ct_condition_t if1); /** Choose between a size_t value and 0. * * Functionally equivalent to: * * condition ? if1 : 0. * * Functionally equivalent to mbedtls_ct_size_if(condition, if1, 0) but * results in smaller code size. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0. */ static inline size_t mbedtls_ct_size_if_else_0(mbedtls_ct_condition_t condition, size_t if1); #if defined(MBEDTLS_BIGNUM_C) /** Choose between an mbedtls_mpi_uint value and 0. * * Functionally equivalent to: * * condition ? if1 : 0. * * Functionally equivalent to mbedtls_ct_mpi_uint_if(condition, if1, 0) but * results in smaller code size. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0. */ static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if_else_0(mbedtls_ct_condition_t condition, mbedtls_mpi_uint if1); #endif /** Constant-flow char selection * * \param low Secret. Bottom of range * \param high Secret. Top of range * \param c Secret. Value to compare to range * \param t Secret. Value to return, if in range * * \return \p t if \p low <= \p c <= \p high, 0 otherwise. */ static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low, unsigned char high, unsigned char c, unsigned char t); /** Choose between two error values. The values must be in the range [-32767..0]. * * Functionally equivalent to: * * condition ? if1 : if0. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * \param if0 Value to use if \p condition == MBEDTLS_CT_FALSE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0. */ static inline int mbedtls_ct_error_if(mbedtls_ct_condition_t condition, int if1, int if0); /** Choose between an error value and 0. The error value must be in the range [-32767..0]. * * Functionally equivalent to: * * condition ? if1 : 0. * * Functionally equivalent to mbedtls_ct_error_if(condition, if1, 0) but * results in smaller code size. * * \param condition Condition to test. * \param if1 Value to use if \p condition == MBEDTLS_CT_TRUE. * * \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise 0. */ static inline int mbedtls_ct_error_if_else_0(mbedtls_ct_condition_t condition, int if1); /* ============================================================================ * Block memory operations */ #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) /** Conditionally set a block of memory to zero. * * Regardless of the condition, every byte will be read once and written to * once. * * \param condition Secret. Condition to test. * \param buf Secret. Pointer to the start of the buffer. * \param len Number of bytes to set to zero. * * \warning Unlike mbedtls_platform_zeroize, this does not have the same guarantees * about not being optimised away if the memory is never read again. */ void mbedtls_ct_zeroize_if(mbedtls_ct_condition_t condition, void *buf, size_t len); /** Shift some data towards the left inside a buffer. * * Functionally equivalent to: * * memmove(start, start + offset, total - offset); * memset(start + (total - offset), 0, offset); * * Timing independence comes at the expense of performance. * * \param start Secret. Pointer to the start of the buffer. * \param total Total size of the buffer. * \param offset Secret. Offset from which to copy \p total - \p offset bytes. */ void mbedtls_ct_memmove_left(void *start, size_t total, size_t offset); #endif /* defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) */ /** Conditional memcpy. * * Functionally equivalent to: * * if (condition) { * memcpy(dest, src1, len); * } else { * if (src2 != NULL) * memcpy(dest, src2, len); * } * * It will always read len bytes from src1. * If src2 != NULL, it will always read len bytes from src2. * If src2 == NULL, it will instead read len bytes from dest (as if src2 == dest). * * \param condition The condition * \param dest Secret. Destination pointer. * \param src1 Secret. Pointer to copy from (if \p condition == MBEDTLS_CT_TRUE). * This may be equal to \p dest, but may not overlap in other ways. * \param src2 Secret (contents only - may branch to determine if this parameter is NULL). * Pointer to copy from (if \p condition == MBEDTLS_CT_FALSE and \p src2 is not NULL). May be NULL. * This may be equal to \p dest, but may not overlap it in other ways. It may overlap with \p src1. * \param len Number of bytes to copy. */ void mbedtls_ct_memcpy_if(mbedtls_ct_condition_t condition, unsigned char *dest, const unsigned char *src1, const unsigned char *src2, size_t len ); /** Copy data from a secret position. * * Functionally equivalent to: * * memcpy(dst, src + offset, len) * * This function copies \p len bytes from \p src + \p offset to * \p dst, with a code flow and memory access pattern that does not depend on * \p offset, but only on \p offset_min, \p offset_max and \p len. * * \note This function reads from \p dest, but the value that * is read does not influence the result and this * function's behavior is well-defined regardless of the * contents of the buffers. This may result in false * positives from static or dynamic analyzers, especially * if \p dest is not initialized. * * \param dest Secret. The destination buffer. This must point to a writable * buffer of at least \p len bytes. * \param src Secret. The base of the source buffer. This must point to a * readable buffer of at least \p offset_max + \p len * bytes. Shouldn't overlap with \p dest * \param offset Secret. The offset in the source buffer from which to copy. * This must be no less than \p offset_min and no greater * than \p offset_max. * \param offset_min The minimal value of \p offset. * \param offset_max The maximal value of \p offset. * \param len The number of bytes to copy. */ void mbedtls_ct_memcpy_offset(unsigned char *dest, const unsigned char *src, size_t offset, size_t offset_min, size_t offset_max, size_t len); /* Documented in include/mbedtls/constant_time.h. a and b are secret. int mbedtls_ct_memcmp(const void *a, const void *b, size_t n); */ #if defined(MBEDTLS_NIST_KW_C) /** Constant-time buffer comparison without branches. * * Similar to mbedtls_ct_memcmp, except that the result only depends on part of * the input data - differences in the head or tail are ignored. Functionally equivalent to: * * memcmp(a + skip_head, b + skip_head, size - skip_head - skip_tail) * * Time taken depends on \p n, but not on \p skip_head or \p skip_tail . * * Behaviour is undefined if ( \p skip_head + \p skip_tail) > \p n. * * \param a Secret. Pointer to the first buffer, containing at least \p n bytes. May not be NULL. * \param b Secret. Pointer to the second buffer, containing at least \p n bytes. May not be NULL. * \param n The number of bytes to examine (total size of the buffers). * \param skip_head Secret. The number of bytes to treat as non-significant at the start of the buffer. * These bytes will still be read. * \param skip_tail Secret. The number of bytes to treat as non-significant at the end of the buffer. * These bytes will still be read. * * \return Zero if the contents of the two buffers are the same, otherwise non-zero. */ int mbedtls_ct_memcmp_partial(const void *a, const void *b, size_t n, size_t skip_head, size_t skip_tail); #endif /* Include the implementation of static inline functions above. */ #include "constant_time_impl.h" #endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */