diff --git a/library/constant_time.c b/library/constant_time.c index 135a6ece3..cc12c9c7c 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -22,17 +22,14 @@ * might be translated to branches by some compilers on some platforms. */ +#include + #include "common.h" #include "constant_time_internal.h" #include "mbedtls/constant_time.h" #include "mbedtls/error.h" #include "mbedtls/platform_util.h" -#if defined(MBEDTLS_BIGNUM_C) -#include "mbedtls/bignum.h" -#include "bignum_core.h" -#endif - #if defined(MBEDTLS_SSL_TLS_C) #include "ssl_misc.h" #endif @@ -41,10 +38,6 @@ #include "mbedtls/rsa.h" #endif -#if defined(MBEDTLS_BASE64_C) -#include "constant_time_invasive.h" -#endif - #include #if defined(MBEDTLS_USE_PSA_CRYPTO) #define PSA_TO_MBEDTLS_ERR(status) PSA_TO_MBEDTLS_ERR_LIST(status, \ @@ -62,13 +55,11 @@ * Some of these definitions could be moved into alignment.h but for now they are * only used here. */ -#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && defined(MBEDTLS_HAVE_ASM) -#if defined(__arm__) || defined(__thumb__) || defined(__thumb2__) || defined(__aarch64__) -#define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS -#endif -#endif +#if defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && \ + (defined(MBEDTLS_CT_ARM_ASM) || defined(MBEDTLS_CT_AARCH64_ASM)) + +#define MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS -#if defined(MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS) static inline uint32_t mbedtls_get_unaligned_volatile_uint32(volatile const unsigned char *p) { /* This is UB, even where it's safe: @@ -76,14 +67,17 @@ static inline uint32_t mbedtls_get_unaligned_volatile_uint32(volatile const unsi * so instead the same thing is expressed in assembly below. */ uint32_t r; -#if defined(__arm__) || defined(__thumb__) || defined(__thumb2__) +#if defined(MBEDTLS_CT_ARM_ASM) asm volatile ("ldr %0, [%1]" : "=r" (r) : "r" (p) :); -#elif defined(__aarch64__) +#elif defined(MBEDTLS_CT_AARCH64_ASM) asm volatile ("ldr %w0, [%1]" : "=r" (r) : "r" (p) :); +#else +#error No assembly defined for mbedtls_get_unaligned_volatile_uint32 #endif return r; } -#endif /* MBEDTLS_EFFICIENT_UNALIGNED_VOLATILE_ACCESS */ +#endif /* defined(MBEDTLS_EFFICIENT_UNALIGNED_ACCESS) && + (defined(MBEDTLS_CT_ARM_ASM) || defined(MBEDTLS_CT_AARCH64_ASM)) */ int mbedtls_ct_memcmp(const void *a, const void *b, diff --git a/library/constant_time_impl.h b/library/constant_time_impl.h new file mode 100644 index 000000000..218a4a614 --- /dev/null +++ b/library/constant_time_impl.h @@ -0,0 +1,276 @@ +/** + * Constant-time functions + * + * For readability, the static inline definitions are here, and + * constant_time_internal.h has only the declarations. + * + * This results in duplicate declarations of the form: + * static inline void f() { ... } + * static inline void f(); + * when constant_time_internal.h is included. This appears to behave + * exactly as if the declaration-without-definition was not present. + * + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef MBEDTLS_CONSTANT_TIME_IMPL_H +#define MBEDTLS_CONSTANT_TIME_IMPL_H + +#include + +#include "common.h" + +#if defined(MBEDTLS_BIGNUM_C) +#include "mbedtls/bignum.h" +#endif + + +/* Disable asm under Memsan because it confuses Memsan and generates false errors */ +#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN) +#define MBEDTLS_CT_NO_ASM +#elif defined(__has_feature) +#if __has_feature(memory_sanitizer) +#define MBEDTLS_CT_NO_ASM +#endif +#endif + +/* armcc5 --gnu defines __GNUC__ but doesn't support GNU's extended asm */ +#if defined(MBEDTLS_HAVE_ASM) && defined(__GNUC__) && (!defined(__ARMCC_VERSION) || \ + __ARMCC_VERSION >= 6000000) && !defined(MBEDTLS_CT_NO_ASM) +#define MBEDTLS_CT_ASM +#if (defined(__arm__) || defined(__thumb__) || defined(__thumb2__)) +#define MBEDTLS_CT_ARM_ASM +#elif defined(__aarch64__) +#define MBEDTLS_CT_AARCH64_ASM +#endif +#endif + +#define MBEDTLS_CT_SIZE (sizeof(mbedtls_ct_uint_t) * 8) + + +/* ============================================================================ + * Core const-time primitives + */ + +/** Ensure that the compiler cannot know the value of x (i.e., cannot optimise + * based on its value) after this function is called. + * + * If we are not using assembly, this will be fairly inefficient, so its use + * should be minimised. + */ +static inline mbedtls_ct_uint_t mbedtls_ct_compiler_opaque(mbedtls_ct_uint_t x) +{ +#if defined(MBEDTLS_CT_ASM) + asm volatile ("" : [x] "+r" (x) :); + return x; +#else + volatile mbedtls_ct_uint_t result = x; + return result; +#endif +} + +/* Convert a number into a condition in constant time. */ +static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x) +{ + /* + * Define mask-generation code that, as far as possible, will not use branches or conditional instructions. + * + * For some platforms / type sizes, we define assembly to assure this. + * + * Otherwise, we define a plain C fallback which (in May 2023) does not get optimised into + * conditional instructions or branches by trunk clang, gcc, or MSVC v19. + */ + const mbedtls_ct_uint_t xo = mbedtls_ct_compiler_opaque(x); +#if defined(_MSC_VER) + /* MSVC has a warning about unary minus on unsigned, but this is + * well-defined and precisely what we want to do here */ +#pragma warning( push ) +#pragma warning( disable : 4146 ) +#endif + return (mbedtls_ct_condition_t) (((mbedtls_ct_int_t) ((-xo) | -(xo >> 1))) >> + (MBEDTLS_CT_SIZE - 1)); +#if defined(_MSC_VER) +#pragma warning( pop ) +#endif +} + +static inline mbedtls_ct_uint_t mbedtls_ct_if(mbedtls_ct_condition_t condition, + mbedtls_ct_uint_t if1, + mbedtls_ct_uint_t if0) +{ + mbedtls_ct_condition_t not_mask = + (mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(condition)); + mbedtls_ct_condition_t mask = + (mbedtls_ct_condition_t) mbedtls_ct_compiler_opaque(condition); + return (mbedtls_ct_uint_t) ((mask & if1) | (not_mask & if0)); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y) +{ + /* Ensure that the compiler cannot optimise the following operations over x and y, + * even if it knows the value of x and y. + */ + const mbedtls_ct_uint_t yo = mbedtls_ct_compiler_opaque(y); + /* + * Check if the most significant bits (MSB) of the operands are different. + * cond is true iff the MSBs differ. + */ + mbedtls_ct_condition_t cond = mbedtls_ct_bool((x ^ yo) >> (MBEDTLS_CT_SIZE - 1)); + + /* + * If the MSB are the same then the difference x-y will be negative (and + * have its MSB set to 1 during conversion to unsigned) if and only if x> (MBEDTLS_CT_SIZE - 1); + + // Convert to a condition (i.e., all bits set iff non-zero) + return mbedtls_ct_bool(ret); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y) +{ + /* diff = 0 if x == y, non-zero otherwise */ + const mbedtls_ct_uint_t diff = mbedtls_ct_compiler_opaque(x) ^ y; + + /* all ones if x != y, 0 otherwise */ + return mbedtls_ct_bool(diff); +} + +static inline unsigned char mbedtls_ct_uchar_in_range_if(unsigned char low, + unsigned char high, + unsigned char c, + unsigned char t) +{ + const unsigned char co = (const unsigned char) mbedtls_ct_compiler_opaque(c); + const unsigned char to = (const unsigned char) mbedtls_ct_compiler_opaque(t); + + /* low_mask is: 0 if low <= c, 0x...ff if low > c */ + unsigned low_mask = ((unsigned) co - low) >> 8; + /* high_mask is: 0 if c <= high, 0x...ff if c > high */ + unsigned high_mask = ((unsigned) high - co) >> 8; + + return (unsigned char) (~(low_mask | high_mask)) & to; +} + + +/* ============================================================================ + * Everything below here is trivial wrapper functions + */ + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_eq(mbedtls_ct_uint_t x, + mbedtls_ct_uint_t y) +{ + return ~mbedtls_ct_bool_ne(x, y); +} + +static inline size_t mbedtls_ct_size_if(mbedtls_ct_condition_t condition, + size_t if1, + size_t if0) +{ + return (size_t) mbedtls_ct_if(condition, (mbedtls_ct_uint_t) if1, (mbedtls_ct_uint_t) if0); +} + +static inline unsigned mbedtls_ct_uint_if_new(mbedtls_ct_condition_t condition, + unsigned if1, + unsigned if0) +{ + return (unsigned) mbedtls_ct_if(condition, (mbedtls_ct_uint_t) if1, (mbedtls_ct_uint_t) if0); +} + +#if defined(MBEDTLS_BIGNUM_C) + +static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if(mbedtls_ct_condition_t condition, \ + mbedtls_mpi_uint if1, \ + mbedtls_mpi_uint if0) +{ + return (mbedtls_mpi_uint) mbedtls_ct_if(condition, + (mbedtls_ct_uint_t) if1, + (mbedtls_ct_uint_t) if0); +} + +#endif + +static inline size_t mbedtls_ct_size_if0(mbedtls_ct_condition_t condition, size_t if1) +{ + return (size_t) (mbedtls_ct_compiler_opaque(condition) & if1); +} + +static inline unsigned mbedtls_ct_uint_if0(mbedtls_ct_condition_t condition, unsigned if1) +{ + return (unsigned) (mbedtls_ct_compiler_opaque(condition) & if1); +} + +#if defined(MBEDTLS_BIGNUM_C) + +static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if0(mbedtls_ct_condition_t condition, + mbedtls_mpi_uint if1) +{ + return (mbedtls_mpi_uint) (mbedtls_ct_compiler_opaque(condition) & if1); +} + +#endif /* MBEDTLS_BIGNUM_C */ + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_gt(mbedtls_ct_uint_t x, + mbedtls_ct_uint_t y) +{ + return mbedtls_ct_bool_lt(y, x); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_ge(mbedtls_ct_uint_t x, + mbedtls_ct_uint_t y) +{ + return ~mbedtls_ct_bool_lt(x, y); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_le(mbedtls_ct_uint_t x, + mbedtls_ct_uint_t y) +{ + return ~mbedtls_ct_bool_gt(x, y); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_xor(mbedtls_ct_condition_t x, + mbedtls_ct_condition_t y) +{ + return (mbedtls_ct_condition_t) (mbedtls_ct_compiler_opaque(x) ^ mbedtls_ct_compiler_opaque(y)); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_and(mbedtls_ct_condition_t x, + mbedtls_ct_condition_t y) +{ + return (mbedtls_ct_condition_t) (mbedtls_ct_compiler_opaque(x) & mbedtls_ct_compiler_opaque(y)); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_or(mbedtls_ct_condition_t x, + mbedtls_ct_condition_t y) +{ + return (mbedtls_ct_condition_t) (mbedtls_ct_compiler_opaque(x) | mbedtls_ct_compiler_opaque(y)); +} + +static inline mbedtls_ct_condition_t mbedtls_ct_bool_not(mbedtls_ct_condition_t x) +{ + return (mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(x)); +} + +#endif /* MBEDTLS_CONSTANT_TIME_IMPL_H */ diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h index e085478d6..d1e3755d2 100644 --- a/library/constant_time_internal.h +++ b/library/constant_time_internal.h @@ -20,6 +20,9 @@ #ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H #define MBEDTLS_CONSTANT_TIME_INTERNAL_H +#include +#include + #include "common.h" #if defined(MBEDTLS_BIGNUM_C) @@ -30,8 +33,6 @@ #include "ssl_misc.h" #endif -#include - /** Turn a value into a mask: * - if \p value == 0, return the all-bits 0 mask, aka 0 @@ -220,33 +221,6 @@ void mbedtls_ct_memcpy_offset(unsigned char *dest, #endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */ -#if defined(MBEDTLS_BASE64_C) - -/** Constant-flow char selection - * - * \param low Bottom of range - * \param high Top of range - * \param c Value to compare to range - * \param t 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) -{ - /* low_mask is: 0 if low <= c, 0x...ff if low > c */ - unsigned low_mask = ((unsigned) c - low) >> 8; - /* high_mask is: 0 if c <= high, 0x...ff if c > high */ - unsigned high_mask = ((unsigned) high - c) >> 8; - return (unsigned char) - mbedtls_ct_uint_if(~mbedtls_ct_mpi_uint_mask(low_mask | high_mask), t, 0); -} - -#endif /* MBEDTLS_BASE64_C */ - - #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) /** Constant-flow "greater than" comparison: @@ -284,4 +258,447 @@ void mbedtls_ct_mem_move_to_left(void *start, #endif /* defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) */ + + +/* 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 (and a few non-boolean operations) + * These are all named mbedtls_ct_bool_, and operate over + * mbedtls_ct_condition_t. + * All arguments to these operations are considered secret. + * example: bool x = y | z => x = mbedtls_ct_bool_or(y, z) + * + * - conditional data selection + * These are all named mbedtls_ct__if and mbedtls_ct__if0 + * 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_if0(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 should be treated as opaque and only manipulated + * via the functions in this header. + * + * 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), assembly implementations are used + * to ensure that the generated code is constant time. For other architectures, + * 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). + */ + +#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) 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_TRUE ((mbedtls_ct_condition_t) 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_TRUE ((mbedtls_ct_condition_t) UINT32_MAX) +#endif +#define MBEDTLS_CT_FALSE ((mbedtls_ct_condition_t) 0) + +/* constant_time_impl.h contains all the static inline implementations, + * so that constant_time_internal.h is more readable. + */ +#include "constant_time_impl.h" + + +/* ============================================================================ + * 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_bool_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_bool_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_bool_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_bool_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_bool_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_bool_le(mbedtls_ct_uint_t x, + mbedtls_ct_uint_t y); + +/** Boolean "xor" 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_bool_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_xor(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_new(mbedtls_ct_condition_t condition, + unsigned if1, + unsigned 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 tombedtls_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_if0(mbedtls_ct_condition_t condition, unsigned if1); + +#if defined(MBEDTLS_BIGNUM_C) + +/** Choose between an mbedtls_mpi_uint value and 0. + * + * Functionally equivalent to: + * + * condition ? if1 : 0. + * + * Functionally equivalent tombedtls_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_if0(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); + + +/* ============================================================================ + * 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). Shouldn't overlap with \p dest. + * \param src2 Secret (contents only - may branch to test if src2 == NULL). + * Pointer to copy from (if \p condition == MBEDTLS_CT_FALSE and \p src2 is not NULL). Shouldn't overlap with \p dest. May be NULL. + * \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_base + \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); + #endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */ diff --git a/library/constant_time_invasive.h b/library/constant_time_invasive.h deleted file mode 100644 index c176b28ff..000000000 --- a/library/constant_time_invasive.h +++ /dev/null @@ -1,51 +0,0 @@ -/** - * \file constant_time_invasive.h - * - * \brief Constant-time module: interfaces for invasive testing only. - * - * The interfaces in this file are intended for testing purposes only. - * They SHOULD NOT be made available in library integrations except when - * building the library for testing. - */ -/* - * Copyright The Mbed TLS Contributors - * SPDX-License-Identifier: Apache-2.0 - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may - * not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT - * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef MBEDTLS_CONSTANT_TIME_INVASIVE_H -#define MBEDTLS_CONSTANT_TIME_INVASIVE_H - -#include "common.h" - -#if defined(MBEDTLS_TEST_HOOKS) - -/** Turn a value into a mask: - * - if \p low <= \p c <= \p high, - * return the all-bits 1 mask, aka (unsigned) -1 - * - otherwise, return the all-bits 0 mask, aka 0 - * - * \param low The value to analyze. - * \param high The value to analyze. - * \param c The value to analyze. - * - * \return All-bits-one if \p low <= \p c <= \p high, otherwise zero. - */ -unsigned char mbedtls_ct_uchar_mask_of_range(unsigned char low, - unsigned char high, - unsigned char c); - -#endif /* MBEDTLS_TEST_HOOKS */ - -#endif /* MBEDTLS_CONSTANT_TIME_INVASIVE_H */