Merge pull request #7578 from daverodgman/safer-ct5

Improve constant-time interface
This commit is contained in:
Manuel Pégourié-Gonnard 2023-08-10 16:57:39 +00:00 committed by GitHub
commit 54da1a69a2
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
24 changed files with 2412 additions and 1369 deletions

View file

@ -23,20 +23,22 @@
#include <stddef.h>
/** Constant-time buffer comparison without branches.
*
* This is equivalent to the standard memcmp function, but is likely to be
* compiled to code using bitwise operation rather than a branch.
* compiled to code using bitwise operations rather than a branch, such that
* the time taken is constant w.r.t. the data pointed to by \p a and \p b,
* and w.r.t. whether \p a and \p b are equal or not. It is not constant-time
* w.r.t. \p n .
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
*
* \param a Pointer to the first buffer.
* \param b Pointer to the second buffer.
* \param n The number of bytes to compare in the buffer.
* \param a Pointer to the first buffer, containing at least \p n bytes. May not be NULL.
* \param b Pointer to the second buffer, containing at least \p n bytes. May not be NULL.
* \param n The number of bytes to compare.
*
* \return Zero if the content of the two buffer is the same,
* \return Zero if the contents of the two buffers are the same,
* otherwise non-zero.
*/
int mbedtls_ct_memcmp(const void *a,

View file

@ -24,6 +24,7 @@
#if defined(MBEDTLS_BASE64_C)
#include "mbedtls/base64.h"
#include "base64_internal.h"
#include "constant_time_internal.h"
#include <stdint.h>
@ -33,6 +34,39 @@
#include "mbedtls/platform.h"
#endif /* MBEDTLS_SELF_TEST */
MBEDTLS_STATIC_TESTABLE
unsigned char mbedtls_ct_base64_enc_char(unsigned char value)
{
unsigned char digit = 0;
/* For each range of values, if value is in that range, mask digit with
* the corresponding value. Since value can only be in a single range,
* only at most one masking will change digit. */
digit |= mbedtls_ct_uchar_in_range_if(0, 25, value, 'A' + value);
digit |= mbedtls_ct_uchar_in_range_if(26, 51, value, 'a' + value - 26);
digit |= mbedtls_ct_uchar_in_range_if(52, 61, value, '0' + value - 52);
digit |= mbedtls_ct_uchar_in_range_if(62, 62, value, '+');
digit |= mbedtls_ct_uchar_in_range_if(63, 63, value, '/');
return digit;
}
MBEDTLS_STATIC_TESTABLE
signed char mbedtls_ct_base64_dec_value(unsigned char c)
{
unsigned char val = 0;
/* For each range of digits, if c is in that range, mask val with
* the corresponding value. Since c can only be in a single range,
* only at most one masking will change val. Set val to one plus
* the desired value so that it stays 0 if c is in none of the ranges. */
val |= mbedtls_ct_uchar_in_range_if('A', 'Z', c, c - 'A' + 0 + 1);
val |= mbedtls_ct_uchar_in_range_if('a', 'z', c, c - 'a' + 26 + 1);
val |= mbedtls_ct_uchar_in_range_if('0', '9', c, c - '0' + 52 + 1);
val |= mbedtls_ct_uchar_in_range_if('+', '+', c, c - '+' + 62 + 1);
val |= mbedtls_ct_uchar_in_range_if('/', '/', c, c - '/' + 63 + 1);
/* At this point, val is 0 if c is an invalid digit and v+1 if c is
* a digit with the value v. */
return val - 1;
}
/*
* Encode a buffer into base64 format
*/

57
library/base64_internal.h Normal file
View file

@ -0,0 +1,57 @@
/**
* \file base64_internal.h
*
* \brief RFC 1521 base64 encoding/decoding: interfaces for invasive 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_BASE64_INTERNAL
#define MBEDTLS_BASE64_INTERNAL
#include "common.h"
#if defined(MBEDTLS_TEST_HOOKS)
/** Given a value in the range 0..63, return the corresponding Base64 digit.
*
* The implementation assumes that letters are consecutive (e.g. ASCII
* but not EBCDIC).
*
* \param value A value in the range 0..63.
*
* \return A base64 digit converted from \p value.
*/
unsigned char mbedtls_ct_base64_enc_char(unsigned char value);
/** Given a Base64 digit, return its value.
*
* If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
* return -1.
*
* The implementation assumes that letters are consecutive (e.g. ASCII
* but not EBCDIC).
*
* \param c A base64 digit.
*
* \return The value of the base64 digit \p c.
*/
signed char mbedtls_ct_base64_dec_value(unsigned char c);
#endif /* MBEDTLS_TEST_HOOKS */
#endif /* MBEDTLS_BASE64_INTERNAL */

View file

@ -54,6 +54,132 @@
#define MPI_VALIDATE(cond) \
MBEDTLS_INTERNAL_VALIDATE(cond)
/*
* Compare signed values in constant time
*/
int mbedtls_mpi_lt_mpi_ct(const mbedtls_mpi *X,
const mbedtls_mpi *Y,
unsigned *ret)
{
mbedtls_ct_condition_t different_sign, X_is_negative, Y_is_negative, result;
MPI_VALIDATE_RET(X != NULL);
MPI_VALIDATE_RET(Y != NULL);
MPI_VALIDATE_RET(ret != NULL);
if (X->n != Y->n) {
return MBEDTLS_ERR_MPI_BAD_INPUT_DATA;
}
/*
* Set sign_N to 1 if N >= 0, 0 if N < 0.
* We know that N->s == 1 if N >= 0 and N->s == -1 if N < 0.
*/
X_is_negative = mbedtls_ct_bool((X->s & 2) >> 1);
Y_is_negative = mbedtls_ct_bool((Y->s & 2) >> 1);
/*
* If the signs are different, then the positive operand is the bigger.
* That is if X is negative (X_is_negative == 1), then X < Y is true and it
* is false if X is positive (X_is_negative == 0).
*/
different_sign = mbedtls_ct_bool_xor(X_is_negative, Y_is_negative); // non-zero if different sign
result = mbedtls_ct_bool_and(different_sign, X_is_negative);
/*
* Assuming signs are the same, compare X and Y. We switch the comparison
* order if they are negative so that we get the right result, regardles of
* sign.
*/
/* This array is used to conditionally swap the pointers in const time */
void * const p[2] = { X->p, Y->p };
size_t i = mbedtls_ct_size_if_else_0(X_is_negative, 1);
mbedtls_ct_condition_t lt = mbedtls_mpi_core_lt_ct(p[i], p[i ^ 1], X->n);
/*
* Store in result iff the signs are the same (i.e., iff different_sign == false). If
* the signs differ, result has already been set, so we don't change it.
*/
result = mbedtls_ct_bool_or(result,
mbedtls_ct_bool_and(mbedtls_ct_bool_not(different_sign), lt));
*ret = mbedtls_ct_uint_if_else_0(result, 1);
return 0;
}
/*
* Conditionally assign X = Y, without leaking information
* about whether the assignment was made or not.
* (Leaking information about the respective sizes of X and Y is ok however.)
*/
#if defined(_MSC_VER) && defined(_M_ARM64) && (_MSC_FULL_VER < 193131103)
/*
* MSVC miscompiles this function if it's inlined prior to Visual Studio 2022 version 17.1. See:
* https://developercommunity.visualstudio.com/t/c-compiler-miscompiles-part-of-mbedtls-library-on/1646989
*/
__declspec(noinline)
#endif
int mbedtls_mpi_safe_cond_assign(mbedtls_mpi *X,
const mbedtls_mpi *Y,
unsigned char assign)
{
int ret = 0;
MPI_VALIDATE_RET(X != NULL);
MPI_VALIDATE_RET(Y != NULL);
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
mbedtls_ct_condition_t do_assign = mbedtls_ct_bool(assign);
X->s = (int) mbedtls_ct_uint_if(do_assign, Y->s, X->s);
mbedtls_mpi_core_cond_assign(X->p, Y->p, Y->n, do_assign);
mbedtls_ct_condition_t do_not_assign = mbedtls_ct_bool_not(do_assign);
for (size_t i = Y->n; i < X->n; i++) {
X->p[i] = mbedtls_ct_mpi_uint_if_else_0(do_not_assign, X->p[i]);
}
cleanup:
return ret;
}
/*
* Conditionally swap X and Y, without leaking information
* about whether the swap was made or not.
* Here it is not ok to simply swap the pointers, which would lead to
* different memory access patterns when X and Y are used afterwards.
*/
int mbedtls_mpi_safe_cond_swap(mbedtls_mpi *X,
mbedtls_mpi *Y,
unsigned char swap)
{
int ret = 0;
int s;
MPI_VALIDATE_RET(X != NULL);
MPI_VALIDATE_RET(Y != NULL);
if (X == Y) {
return 0;
}
mbedtls_ct_condition_t do_swap = mbedtls_ct_bool(swap);
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(X, Y->n));
MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Y, X->n));
s = X->s;
X->s = (int) mbedtls_ct_uint_if(do_swap, Y->s, X->s);
Y->s = (int) mbedtls_ct_uint_if(do_swap, s, Y->s);
mbedtls_mpi_core_cond_swap(X->p, Y->p, X->n, do_swap);
cleanup:
return ret;
}
/* Implementation that should never be optimized out by the compiler */
#define mbedtls_mpi_zeroize_and_free(v, n) mbedtls_zeroize_and_free(v, ciL * (n))
@ -1624,10 +1750,8 @@ static int mpi_select(mbedtls_mpi *R, const mbedtls_mpi *T, size_t T_size, size_
for (size_t i = 0; i < T_size; i++) {
MBEDTLS_MPI_CHK(mbedtls_mpi_safe_cond_assign(R, &T[i],
(unsigned char) mbedtls_ct_size_bool_eq(i,
idx)));
(unsigned char) mbedtls_ct_uint_eq(i, idx)));
}
cleanup:
return ret;
}

View file

@ -144,54 +144,92 @@ void mbedtls_mpi_core_bigendian_to_host(mbedtls_mpi_uint *A,
/* Whether min <= A, in constant time.
* A_limbs must be at least 1. */
unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
const mbedtls_mpi_uint *A,
size_t A_limbs)
mbedtls_ct_condition_t mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
const mbedtls_mpi_uint *A,
size_t A_limbs)
{
/* min <= least significant limb? */
unsigned min_le_lsl = 1 ^ mbedtls_ct_mpi_uint_lt(A[0], min);
mbedtls_ct_condition_t min_le_lsl = mbedtls_ct_uint_ge(A[0], min);
/* limbs other than the least significant one are all zero? */
mbedtls_mpi_uint msll_mask = 0;
mbedtls_ct_condition_t msll_mask = MBEDTLS_CT_FALSE;
for (size_t i = 1; i < A_limbs; i++) {
msll_mask |= A[i];
msll_mask = mbedtls_ct_bool_or(msll_mask, mbedtls_ct_bool(A[i]));
}
/* The most significant limbs of A are not all zero iff msll_mask != 0. */
unsigned msll_nonzero = mbedtls_ct_mpi_uint_mask(msll_mask) & 1;
/* min <= A iff the lowest limb of A is >= min or the other limbs
* are not all zero. */
return min_le_lsl | msll_nonzero;
return mbedtls_ct_bool_or(msll_mask, min_le_lsl);
}
mbedtls_ct_condition_t mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
const mbedtls_mpi_uint *B,
size_t limbs)
{
mbedtls_ct_condition_t ret = MBEDTLS_CT_FALSE, cond = MBEDTLS_CT_FALSE, done = MBEDTLS_CT_FALSE;
for (size_t i = limbs; i > 0; i--) {
/*
* If B[i - 1] < A[i - 1] then A < B is false and the result must
* remain 0.
*
* Again even if we can make a decision, we just mark the result and
* the fact that we are done and continue looping.
*/
cond = mbedtls_ct_uint_lt(B[i - 1], A[i - 1]);
done = mbedtls_ct_bool_or(done, cond);
/*
* If A[i - 1] < B[i - 1] then A < B is true.
*
* Again even if we can make a decision, we just mark the result and
* the fact that we are done and continue looping.
*/
cond = mbedtls_ct_uint_lt(A[i - 1], B[i - 1]);
ret = mbedtls_ct_bool_or(ret, mbedtls_ct_bool_and(cond, mbedtls_ct_bool_not(done)));
done = mbedtls_ct_bool_or(done, cond);
}
/*
* If all the limbs were equal, then the numbers are equal, A < B is false
* and leaving the result 0 is correct.
*/
return ret;
}
void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X,
const mbedtls_mpi_uint *A,
size_t limbs,
unsigned char assign)
mbedtls_ct_condition_t assign)
{
if (X == A) {
return;
}
mbedtls_ct_mpi_uint_cond_assign(limbs, X, A, assign);
/* This function is very performance-sensitive for RSA. For this reason
* we have the loop below, instead of calling mbedtls_ct_memcpy_if
* (this is more optimal since here we don't have to handle the case where
* we copy awkwardly sized data).
*/
for (size_t i = 0; i < limbs; i++) {
X[i] = mbedtls_ct_mpi_uint_if(assign, A[i], X[i]);
}
}
void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X,
mbedtls_mpi_uint *Y,
size_t limbs,
unsigned char swap)
mbedtls_ct_condition_t swap)
{
if (X == Y) {
return;
}
/* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */
mbedtls_mpi_uint limb_mask = mbedtls_ct_mpi_uint_mask(swap);
for (size_t i = 0; i < limbs; i++) {
mbedtls_mpi_uint tmp = X[i];
X[i] = (X[i] & ~limb_mask) | (Y[i] & limb_mask);
Y[i] = (Y[i] & ~limb_mask) | (tmp & limb_mask);
X[i] = mbedtls_ct_mpi_uint_if(swap, Y[i], X[i]);
Y[i] = mbedtls_ct_mpi_uint_if(swap, tmp, Y[i]);
}
}
@ -422,11 +460,10 @@ mbedtls_mpi_uint mbedtls_mpi_core_add_if(mbedtls_mpi_uint *X,
{
mbedtls_mpi_uint c = 0;
/* all-bits 0 if cond is 0, all-bits 1 if cond is non-0 */
const mbedtls_mpi_uint mask = mbedtls_ct_mpi_uint_mask(cond);
mbedtls_ct_condition_t do_add = mbedtls_ct_bool(cond);
for (size_t i = 0; i < limbs; i++) {
mbedtls_mpi_uint add = mask & A[i];
mbedtls_mpi_uint add = mbedtls_ct_mpi_uint_if_else_0(do_add, A[i]);
mbedtls_mpi_uint t = c + X[i];
c = (t < X[i]);
t += add;
@ -568,7 +605,11 @@ void mbedtls_mpi_core_montmul(mbedtls_mpi_uint *X,
* So the correct return value is already in X if (carry ^ borrow) = 0,
* but is in (the lower AN_limbs limbs of) T if (carry ^ borrow) = 1.
*/
mbedtls_ct_mpi_uint_cond_assign(AN_limbs, X, T, (unsigned char) (carry ^ borrow));
mbedtls_ct_memcpy_if(mbedtls_ct_bool(carry ^ borrow),
(unsigned char *) X,
(unsigned char *) T,
NULL,
AN_limbs * sizeof(mbedtls_mpi_uint));
}
int mbedtls_mpi_core_get_mont_r2_unsafe(mbedtls_mpi *X,
@ -593,7 +634,7 @@ void mbedtls_mpi_core_ct_uint_table_lookup(mbedtls_mpi_uint *dest,
size_t index)
{
for (size_t i = 0; i < count; i++, table += limbs) {
unsigned char assign = mbedtls_ct_size_bool_eq(i, index);
mbedtls_ct_condition_t assign = mbedtls_ct_uint_eq(i, index);
mbedtls_mpi_core_cond_assign(dest, table, limbs, assign);
}
}
@ -633,7 +674,7 @@ int mbedtls_mpi_core_random(mbedtls_mpi_uint *X,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng)
{
unsigned ge_lower = 1, lt_upper = 0;
mbedtls_ct_condition_t ge_lower = MBEDTLS_CT_TRUE, lt_upper = MBEDTLS_CT_FALSE;
size_t n_bits = mbedtls_mpi_core_bitlen(N, limbs);
size_t n_bytes = (n_bits + 7) / 8;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
@ -678,7 +719,7 @@ int mbedtls_mpi_core_random(mbedtls_mpi_uint *X,
ge_lower = mbedtls_mpi_core_uint_le_mpi(min, X, limbs);
lt_upper = mbedtls_mpi_core_lt_ct(X, N, limbs);
} while (ge_lower == 0 || lt_upper == 0);
} while (mbedtls_ct_bool_and(ge_lower, lt_upper) == MBEDTLS_CT_FALSE);
cleanup:
return ret;

View file

@ -86,6 +86,8 @@
#include "mbedtls/bignum.h"
#endif
#include "constant_time_internal.h"
#define ciL (sizeof(mbedtls_mpi_uint)) /** chars in limb */
#define biL (ciL << 3) /** bits in limb */
#define biH (ciL << 2) /** half limb size */
@ -142,11 +144,29 @@ void mbedtls_mpi_core_bigendian_to_host(mbedtls_mpi_uint *A,
* \param A_limbs The number of limbs of \p A.
* This must be at least 1.
*
* \return 1 if \p min is less than or equal to \p A, otherwise 0.
* \return MBEDTLS_CT_TRUE if \p min is less than or equal to \p A, otherwise MBEDTLS_CT_FALSE.
*/
unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
const mbedtls_mpi_uint *A,
size_t A_limbs);
mbedtls_ct_condition_t mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
const mbedtls_mpi_uint *A,
size_t A_limbs);
/**
* \brief Check if one unsigned MPI is less than another in constant
* time.
*
* \param A The left-hand MPI. This must point to an array of limbs
* with the same allocated length as \p B.
* \param B The right-hand MPI. This must point to an array of limbs
* with the same allocated length as \p A.
* \param limbs The number of limbs in \p A and \p B.
* This must not be 0.
*
* \return MBEDTLS_CT_TRUE if \p A is less than \p B.
* MBEDTLS_CT_FALSE if \p A is greater than or equal to \p B.
*/
mbedtls_ct_condition_t mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
const mbedtls_mpi_uint *B,
size_t limbs);
/**
* \brief Perform a safe conditional copy of an MPI which doesn't reveal
@ -158,21 +178,17 @@ unsigned mbedtls_mpi_core_uint_le_mpi(mbedtls_mpi_uint min,
* \param[in] A The address of the source MPI. This must be initialized.
* \param limbs The number of limbs of \p A.
* \param assign The condition deciding whether to perform the
* assignment or not. Must be either 0 or 1:
* * \c 1: Perform the assignment `X = A`.
* * \c 0: Keep the original value of \p X.
* assignment or not. Callers will need to use
* the constant time interface (e.g. `mbedtls_ct_bool()`)
* to construct this argument.
*
* \note This function avoids leaking any information about whether
* the assignment was done or not.
*
* \warning If \p assign is neither 0 nor 1, the result of this function
* is indeterminate, and the resulting value in \p X might be
* neither its original value nor the value in \p A.
*/
void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X,
const mbedtls_mpi_uint *A,
size_t limbs,
unsigned char assign);
mbedtls_ct_condition_t assign);
/**
* \brief Perform a safe conditional swap of two MPIs which doesn't reveal
@ -184,21 +200,15 @@ void mbedtls_mpi_core_cond_assign(mbedtls_mpi_uint *X,
* This must be initialized.
* \param limbs The number of limbs of \p X and \p Y.
* \param swap The condition deciding whether to perform
* the swap or not. Must be either 0 or 1:
* * \c 1: Swap the values of \p X and \p Y.
* * \c 0: Keep the original values of \p X and \p Y.
* the swap or not.
*
* \note This function avoids leaking any information about whether
* the swap was done or not.
*
* \warning If \p swap is neither 0 nor 1, the result of this function
* is indeterminate, and both \p X and \p Y might end up with
* values different to either of the original ones.
*/
void mbedtls_mpi_core_cond_swap(mbedtls_mpi_uint *X,
mbedtls_mpi_uint *Y,
size_t limbs,
unsigned char swap);
mbedtls_ct_condition_t swap);
/** Import X from unsigned binary data, little-endian.
*

View file

@ -40,7 +40,7 @@ void mbedtls_mpi_mod_raw_cond_assign(mbedtls_mpi_uint *X,
const mbedtls_mpi_mod_modulus *N,
unsigned char assign)
{
mbedtls_mpi_core_cond_assign(X, A, N->limbs, assign);
mbedtls_mpi_core_cond_assign(X, A, N->limbs, mbedtls_ct_bool(assign));
}
void mbedtls_mpi_mod_raw_cond_swap(mbedtls_mpi_uint *X,
@ -48,7 +48,7 @@ void mbedtls_mpi_mod_raw_cond_swap(mbedtls_mpi_uint *X,
const mbedtls_mpi_mod_modulus *N,
unsigned char swap)
{
mbedtls_mpi_core_cond_swap(X, Y, N->limbs, swap);
mbedtls_mpi_core_cond_swap(X, Y, N->limbs, mbedtls_ct_bool(swap));
}
int mbedtls_mpi_mod_raw_read(mbedtls_mpi_uint *X,

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,306 @@
/**
* 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 <stddef.h>
#include "common.h"
#if defined(MBEDTLS_BIGNUM_C)
#include "mbedtls/bignum.h"
#endif
/* constant_time_impl.h contains all the static inline implementations,
* so that constant_time_internal.h is more readable.
*
* gcc generates warnings about duplicate declarations, so disable this
* warning.
*/
#ifdef __GNUC__
#pragma GCC diagnostic push
#pragma GCC diagnostic ignored "-Wredundant-decls"
#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.
*/
#if !defined(MBEDTLS_CT_ASM)
extern volatile mbedtls_ct_uint_t mbedtls_ct_zero;
#endif
/**
* \brief Ensure that a value cannot be known at compile time.
*
* \param x The value to hide from the compiler.
* \return The same value that was passed in, such that the compiler
* cannot prove its value (even for calls of the form
* x = mbedtls_ct_compiler_opaque(1), x will be unknown).
*
* \note This is mainly used in constructing mbedtls_ct_condition_t
* values and performing operations over them, to ensure that
* there is no way for the compiler to ever know anything about
* the value of an mbedtls_ct_condition_t.
*/
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
return x ^ mbedtls_ct_zero;
#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_cond =
(mbedtls_ct_condition_t) (~mbedtls_ct_compiler_opaque(condition));
return (mbedtls_ct_uint_t) ((condition & if1) | (not_cond & if0));
}
static inline mbedtls_ct_condition_t mbedtls_ct_uint_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 xo = mbedtls_ct_compiler_opaque(x);
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((xo ^ 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<y.
*
* If the MSB are different, then the operand with the MSB of 1 is the
* bigger. (That is if y has MSB of 1, then x<y is true and it is false if
* the MSB of y is 0.)
*/
// Select either y, or x - y
mbedtls_ct_uint_t ret = mbedtls_ct_if(cond, yo, (mbedtls_ct_uint_t) (xo - yo));
// Extract only the MSB of ret
ret = ret >> (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_uint_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) ^ mbedtls_ct_compiler_opaque(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 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(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_if_else_0(mbedtls_ct_condition_t condition, size_t if1)
{
return (size_t) (condition & if1);
}
static inline unsigned mbedtls_ct_uint_if_else_0(mbedtls_ct_condition_t condition, unsigned if1)
{
return (unsigned) (condition & if1);
}
#if defined(MBEDTLS_BIGNUM_C)
static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if_else_0(mbedtls_ct_condition_t condition,
mbedtls_mpi_uint if1)
{
return (mbedtls_mpi_uint) (condition & if1);
}
#endif /* MBEDTLS_BIGNUM_C */
static inline mbedtls_ct_condition_t mbedtls_ct_uint_eq(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y)
{
return ~mbedtls_ct_uint_ne(x, y);
}
static inline mbedtls_ct_condition_t mbedtls_ct_uint_gt(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y)
{
return mbedtls_ct_uint_lt(y, x);
}
static inline mbedtls_ct_condition_t mbedtls_ct_uint_ge(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y)
{
return ~mbedtls_ct_uint_lt(x, y);
}
static inline mbedtls_ct_condition_t mbedtls_ct_uint_le(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y)
{
return ~mbedtls_ct_uint_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) (x ^ 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) (x & 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) (x | y);
}
static inline mbedtls_ct_condition_t mbedtls_ct_bool_not(mbedtls_ct_condition_t x)
{
return (mbedtls_ct_condition_t) (~x);
}
#ifdef __GNUC__
#pragma GCC diagnostic pop
#endif
#endif /* MBEDTLS_CONSTANT_TIME_IMPL_H */

View file

@ -20,224 +20,442 @@
#ifndef MBEDTLS_CONSTANT_TIME_INTERNAL_H
#define MBEDTLS_CONSTANT_TIME_INTERNAL_H
#include <stdint.h>
#include <stddef.h>
#include "common.h"
#if defined(MBEDTLS_BIGNUM_C)
#include "mbedtls/bignum.h"
#endif
#if defined(MBEDTLS_SSL_TLS_C)
#include "ssl_misc.h"
/* 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_<type>_<operation>.
* They operate over <type> 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_<type>_if and mbedtls_ct_<type>_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_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_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))
#include <stddef.h>
/** Turn a value into a mask:
* - if \p value == 0, return the all-bits 0 mask, aka 0
* - otherwise, return the all-bits 1 mask, aka (unsigned) -1
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
*
* \param value The value to analyze.
*
* \return Zero if \p value is zero, otherwise all-bits-one.
/* ============================================================================
* Boolean operations
*/
unsigned mbedtls_ct_uint_mask(unsigned value);
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
/** Turn a value into a mask:
* - if \p value == 0, return the all-bits 0 mask, aka 0
* - otherwise, return the all-bits 1 mask, aka (size_t) -1
/** Convert a number into a mbedtls_ct_condition_t.
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
* \param x Number to convert.
*
* \param value The value to analyze.
* \return MBEDTLS_CT_TRUE if \p x != 0, or MBEDTLS_CT_FALSE if \p x == 0
*
* \return Zero if \p value is zero, otherwise all-bits-one.
*/
size_t mbedtls_ct_size_mask(size_t value);
static inline mbedtls_ct_condition_t mbedtls_ct_bool(mbedtls_ct_uint_t x);
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
#if defined(MBEDTLS_BIGNUM_C)
/** Turn a value into a mask:
* - if \p value == 0, return the all-bits 0 mask, aka 0
* - otherwise, return the all-bits 1 mask, aka (mbedtls_mpi_uint) -1
/** Boolean "not equal" operation.
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
* Functionally equivalent to:
*
* \param value The value to analyze.
*
* \return Zero if \p value is zero, otherwise all-bits-one.
*/
mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask(mbedtls_mpi_uint value);
#endif /* MBEDTLS_BIGNUM_C */
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/** Constant-flow mask generation for "greater or equal" comparison:
* - if \p x >= \p y, return all-bits 1, that is (size_t) -1
* - otherwise, return all bits 0, that is 0
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
* \p x != \p y
*
* \param x The first value to analyze.
* \param y The second value to analyze.
*
* \return All-bits-one if \p x is greater or equal than \p y,
* otherwise zero.
* \return MBEDTLS_CT_TRUE if \p x != \p y, otherwise MBEDTLS_CT_FALSE.
*/
size_t mbedtls_ct_size_mask_ge(size_t x,
size_t y);
static inline mbedtls_ct_condition_t mbedtls_ct_uint_ne(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
/** Constant-flow boolean "equal" comparison:
* return x == y
/** Boolean "equals" operation.
*
* This is equivalent to \p x == \p y, but is likely to be compiled
* to code using bitwise operation rather than a branch.
* Functionally equivalent to:
*
* \p x == \p y
*
* \param x The first value to analyze.
* \param y The second value to analyze.
*
* \return 1 if \p x equals to \p y, otherwise 0.
* \return MBEDTLS_CT_TRUE if \p x == \p y, otherwise MBEDTLS_CT_FALSE.
*/
unsigned mbedtls_ct_size_bool_eq(size_t x,
size_t y);
static inline mbedtls_ct_condition_t mbedtls_ct_uint_eq(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y);
#if defined(MBEDTLS_BIGNUM_C)
/** Decide if an integer is less than the other, without branches.
/** Boolean "less than" operation.
*
* This is equivalent to \p x < \p y, but is likely to be compiled
* to code using bitwise operation rather than a branch.
* Functionally equivalent to:
*
* \p x < \p y
*
* \param x The first value to analyze.
* \param y The second value to analyze.
*
* \return 1 if \p x is less than \p y, otherwise 0.
* \return MBEDTLS_CT_TRUE if \p x < \p y, otherwise MBEDTLS_CT_FALSE.
*/
unsigned mbedtls_ct_mpi_uint_lt(const mbedtls_mpi_uint x,
const mbedtls_mpi_uint y);
static inline mbedtls_ct_condition_t mbedtls_ct_uint_lt(mbedtls_ct_uint_t x, mbedtls_ct_uint_t y);
/**
* \brief Check if one unsigned MPI is less than another in constant
* time.
/** Boolean "greater than" operation.
*
* \param A The left-hand MPI. This must point to an array of limbs
* with the same allocated length as \p B.
* \param B The right-hand MPI. This must point to an array of limbs
* with the same allocated length as \p A.
* \param limbs The number of limbs in \p A and \p B.
* This must not be 0.
* Functionally equivalent to:
*
* \return The result of the comparison:
* \c 1 if \p A is less than \p B.
* \c 0 if \p A is greater than or equal to \p B.
* \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.
*/
unsigned mbedtls_mpi_core_lt_ct(const mbedtls_mpi_uint *A,
const mbedtls_mpi_uint *B,
size_t limbs);
#endif /* MBEDTLS_BIGNUM_C */
static inline mbedtls_ct_condition_t mbedtls_ct_uint_gt(mbedtls_ct_uint_t x,
mbedtls_ct_uint_t y);
/** Choose between two integer values without branches.
/** Boolean "greater or equal" operation.
*
* This is equivalent to `condition ? if1 : if0`, but is likely to be compiled
* to code using bitwise operation rather than a branch.
* 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 "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_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_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 is nonzero.
* \param if0 Value to use if \p condition is zero.
* \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 is nonzero, otherwise \c if0.
* \return \c if1 if \p condition == MBEDTLS_CT_TRUE, otherwise \c if0.
*/
unsigned mbedtls_ct_uint_if(unsigned condition,
unsigned if1,
unsigned 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);
#if defined(MBEDTLS_BIGNUM_C)
/** Conditionally assign a value without branches.
/** Choose between two mbedtls_mpi_uint values.
*
* This is equivalent to `if ( condition ) dest = src`, but is likely
* to be compiled to code using bitwise operation rather than a branch.
* Functionally equivalent to:
*
* \param n \p dest and \p src must be arrays of limbs of size n.
* \param dest The MPI to conditionally assign to. This must point
* to an initialized MPI.
* \param src The MPI to be assigned from. This must point to an
* initialized MPI.
* \param condition Condition to test, must be 0 or 1.
* 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.
*/
void mbedtls_ct_mpi_uint_cond_assign(size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
unsigned char condition);
static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if(mbedtls_ct_condition_t condition, \
mbedtls_mpi_uint if1, \
mbedtls_mpi_uint if0);
#endif /* MBEDTLS_BIGNUM_C */
#endif
#if defined(MBEDTLS_BASE64_C)
/** Given a value in the range 0..63, return the corresponding Base64 digit.
/** Choose between an unsigned value and 0.
*
* The implementation assumes that letters are consecutive (e.g. ASCII
* but not EBCDIC).
* Functionally equivalent to:
*
* \param value A value in the range 0..63.
* condition ? if1 : 0.
*
* \return A base64 digit converted from \p value.
* 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.
*/
unsigned char mbedtls_ct_base64_enc_char(unsigned char value);
static inline unsigned mbedtls_ct_uint_if_else_0(mbedtls_ct_condition_t condition, unsigned if1);
/** Given a Base64 digit, return its value.
/** Choose between a size_t value and 0.
*
* If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'),
* return -1.
* Functionally equivalent to:
*
* The implementation assumes that letters are consecutive (e.g. ASCII
* but not EBCDIC).
* condition ? if1 : 0.
*
* \param c A base64 digit.
* Functionally equivalent to mbedtls_ct_size_if(condition, if1, 0) but
* results in smaller code size.
*
* \return The value of the base64 digit \p c.
* \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.
*/
signed char mbedtls_ct_base64_dec_value(unsigned char c);
static inline size_t mbedtls_ct_size_if_else_0(mbedtls_ct_condition_t condition, size_t if1);
#endif /* MBEDTLS_BASE64_C */
#if defined(MBEDTLS_BIGNUM_C)
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
/** Conditional memcpy without branches.
/** Choose between an mbedtls_mpi_uint value and 0.
*
* This is equivalent to `if ( c1 == c2 ) memcpy(dest, src, len)`, but is likely
* to be compiled to code using bitwise operation rather than a branch.
* Functionally equivalent to:
*
* \param dest The pointer to conditionally copy to.
* \param src The pointer to copy from. Shouldn't overlap with \p dest.
* \param len The number of bytes to copy.
* \param c1 The first value to analyze in the condition.
* \param c2 The second value to analyze in the condition.
* 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.
*/
void mbedtls_ct_memcpy_if_eq(unsigned char *dest,
const unsigned char *src,
size_t len,
size_t c1, size_t c2);
static inline mbedtls_mpi_uint mbedtls_ct_mpi_uint_if_else_0(mbedtls_ct_condition_t condition,
mbedtls_mpi_uint if1);
/** Copy data from a secret position with constant flow.
#endif
/** Constant-flow char selection
*
* This function copies \p len bytes from \p src_base + \p offset_secret to \p
* dst, with a code flow and memory access pattern that does not depend on \p
* offset_secret, but only on \p offset_min, \p offset_max and \p len.
* Functionally equivalent to `memcpy(dst, src + offset_secret, len)`.
* \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).
* 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_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
@ -246,12 +464,12 @@ void mbedtls_ct_memcpy_if_eq(unsigned char *dest,
* positives from static or dynamic analyzers, especially
* if \p dest is not initialized.
*
* \param dest The destination buffer. This must point to a writable
* \param dest Secret. The destination buffer. This must point to a writable
* buffer of at least \p len bytes.
* \param src The base of the source buffer. This must point to a
* \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 The offset in the source buffer from which to copy.
* 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.
@ -265,99 +483,14 @@ void mbedtls_ct_memcpy_offset(unsigned char *dest,
size_t offset_max,
size_t len);
/** Compute the HMAC of variable-length data with constant flow.
*
* This function computes the HMAC of the concatenation of \p add_data and \p
* data, and does with a code flow and memory access pattern that does not
* depend on \p data_len_secret, but only on \p min_data_len and \p
* max_data_len. In particular, this function always reads exactly \p
* max_data_len bytes from \p data.
*
* \param ctx The HMAC context. It must have keys configured
* with mbedtls_md_hmac_starts() and use one of the
* following hashes: SHA-384, SHA-256, SHA-1 or MD-5.
* It is reset using mbedtls_md_hmac_reset() after
* the computation is complete to prepare for the
* next computation.
* \param add_data The first part of the message whose HMAC is being
* calculated. This must point to a readable buffer
* of \p add_data_len bytes.
* \param add_data_len The length of \p add_data in bytes.
* \param data The buffer containing the second part of the
* message. This must point to a readable buffer
* of \p max_data_len bytes.
* \param data_len_secret The length of the data to process in \p data.
* This must be no less than \p min_data_len and no
* greater than \p max_data_len.
* \param min_data_len The minimal length of the second part of the
* message, read from \p data.
* \param max_data_len The maximal length of the second part of the
* message, read from \p data.
* \param output The HMAC will be written here. This must point to
* a writable buffer of sufficient size to hold the
* HMAC value.
*
* \retval 0 on success.
* \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
* The hardware accelerator failed.
/* 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_USE_PSA_CRYPTO)
int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
psa_algorithm_t alg,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output);
#else
int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output);
#endif /* MBEDTLS_USE_PSA_CRYPTO */
#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 */
/* Include the implementation of static inline functions above. */
#include "constant_time_impl.h"
#endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */

View file

@ -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 */

View file

@ -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;
mbedtls_ct_condition_t bad;
mbedtls_ct_condition_t pad_done;
size_t plaintext_size = 0;
mbedtls_ct_condition_t 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 = mbedtls_ct_bool(input[0]);
/* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
* where PS must be at least 8 nonzero bytes. */
bad = mbedtls_ct_bool_or(bad, mbedtls_ct_uint_ne(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. */
pad_done = MBEDTLS_CT_FALSE;
for (i = 2; i < ilen; i++) {
mbedtls_ct_condition_t found = mbedtls_ct_uint_eq(input[i], 0);
pad_done = mbedtls_ct_bool_or(pad_done, found);
pad_count += mbedtls_ct_uint_if_else_0(mbedtls_ct_bool_not(pad_done), 1);
}
/* If pad_done is still zero, there's no data, only unfinished padding. */
bad = mbedtls_ct_bool_or(bad, mbedtls_ct_bool_not(pad_done));
/* There must be at least 8 bytes of padding. */
bad = mbedtls_ct_bool_or(bad, mbedtls_ct_uint_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_uint_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,
(unsigned) (-(MBEDTLS_ERR_RSA_INVALID_PADDING)),
mbedtls_ct_uint_if_else_0(
output_too_large,
(unsigned) (-(MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE)))
);
/* 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. */
mbedtls_ct_zeroize_if(mbedtls_ct_bool_or(bad, output_too_large), input + 11, ilen - 11);
/* 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_memmove_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,

View file

@ -2796,4 +2796,64 @@ static inline void mbedtls_ssl_session_clear_ticket_flags(
int mbedtls_ssl_tls13_finalize_client_hello(mbedtls_ssl_context *ssl);
#endif
#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
/** Compute the HMAC of variable-length data with constant flow.
*
* This function computes the HMAC of the concatenation of \p add_data and \p
* data, and does with a code flow and memory access pattern that does not
* depend on \p data_len_secret, but only on \p min_data_len and \p
* max_data_len. In particular, this function always reads exactly \p
* max_data_len bytes from \p data.
*
* \param ctx The HMAC context. It must have keys configured
* with mbedtls_md_hmac_starts() and use one of the
* following hashes: SHA-384, SHA-256, SHA-1 or MD-5.
* It is reset using mbedtls_md_hmac_reset() after
* the computation is complete to prepare for the
* next computation.
* \param add_data The first part of the message whose HMAC is being
* calculated. This must point to a readable buffer
* of \p add_data_len bytes.
* \param add_data_len The length of \p add_data in bytes.
* \param data The buffer containing the second part of the
* message. This must point to a readable buffer
* of \p max_data_len bytes.
* \param data_len_secret The length of the data to process in \p data.
* This must be no less than \p min_data_len and no
* greater than \p max_data_len.
* \param min_data_len The minimal length of the second part of the
* message, read from \p data.
* \param max_data_len The maximal length of the second part of the
* message, read from \p data.
* \param output The HMAC will be written here. This must point to
* a writable buffer of sufficient size to hold the
* HMAC value.
*
* \retval 0 on success.
* \retval #MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
* The hardware accelerator failed.
*/
#if defined(MBEDTLS_USE_PSA_CRYPTO)
int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
psa_algorithm_t mac_alg,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output);
#else
int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output);
#endif /* defined(MBEDTLS_USE_PSA_CRYPTO) */
#endif /* MBEDTLS_TEST_HOOKS && defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC) */
#endif /* ssl_misc.h */

View file

@ -60,6 +60,234 @@ static int local_err_translation(psa_status_t status)
#define PSA_TO_MBEDTLS_ERR(status) local_err_translation(status)
#endif
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_MAC)
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#if defined(PSA_WANT_ALG_SHA_384)
#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_384)
#elif defined(PSA_WANT_ALG_SHA_256)
#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_256)
#else /* See check_config.h */
#define MAX_HASH_BLOCK_LENGTH PSA_HASH_BLOCK_LENGTH(PSA_ALG_SHA_1)
#endif
MBEDTLS_STATIC_TESTABLE
int mbedtls_ct_hmac(mbedtls_svc_key_id_t key,
psa_algorithm_t mac_alg,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output)
{
/*
* This function breaks the HMAC abstraction and uses psa_hash_clone()
* extension in order to get constant-flow behaviour.
*
* HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
* concatenation, and okey/ikey are the XOR of the key with some fixed bit
* patterns (see RFC 2104, sec. 2).
*
* We'll first compute ikey/okey, then inner_hash = HASH(ikey + msg) by
* hashing up to minlen, then cloning the context, and for each byte up
* to maxlen finishing up the hash computation, keeping only the
* correct result.
*
* Then we only need to compute HASH(okey + inner_hash) and we're done.
*/
psa_algorithm_t hash_alg = PSA_ALG_HMAC_GET_HASH(mac_alg);
const size_t block_size = PSA_HASH_BLOCK_LENGTH(hash_alg);
unsigned char key_buf[MAX_HASH_BLOCK_LENGTH];
const size_t hash_size = PSA_HASH_LENGTH(hash_alg);
psa_hash_operation_t operation = PSA_HASH_OPERATION_INIT;
size_t hash_length;
unsigned char aux_out[PSA_HASH_MAX_SIZE];
psa_hash_operation_t aux_operation = PSA_HASH_OPERATION_INIT;
size_t offset;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t mac_key_length;
size_t i;
#define PSA_CHK(func_call) \
do { \
status = (func_call); \
if (status != PSA_SUCCESS) \
goto cleanup; \
} while (0)
/* Export MAC key
* We assume key length is always exactly the output size
* which is never more than the block size, thus we use block_size
* as the key buffer size.
*/
PSA_CHK(psa_export_key(key, key_buf, block_size, &mac_key_length));
/* Calculate ikey */
for (i = 0; i < mac_key_length; i++) {
key_buf[i] = (unsigned char) (key_buf[i] ^ 0x36);
}
for (; i < block_size; ++i) {
key_buf[i] = 0x36;
}
PSA_CHK(psa_hash_setup(&operation, hash_alg));
/* Now compute inner_hash = HASH(ikey + msg) */
PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
PSA_CHK(psa_hash_update(&operation, add_data, add_data_len));
PSA_CHK(psa_hash_update(&operation, data, min_data_len));
/* Fill the hash buffer in advance with something that is
* not a valid hash (barring an attack on the hash and
* deliberately-crafted input), in case the caller doesn't
* check the return status properly. */
memset(output, '!', hash_size);
/* For each possible length, compute the hash up to that point */
for (offset = min_data_len; offset <= max_data_len; offset++) {
PSA_CHK(psa_hash_clone(&operation, &aux_operation));
PSA_CHK(psa_hash_finish(&aux_operation, aux_out,
PSA_HASH_MAX_SIZE, &hash_length));
/* Keep only the correct inner_hash in the output buffer */
mbedtls_ct_memcpy_if(mbedtls_ct_uint_eq(offset, data_len_secret),
output, aux_out, NULL, hash_size);
if (offset < max_data_len) {
PSA_CHK(psa_hash_update(&operation, data + offset, 1));
}
}
/* Abort current operation to prepare for final operation */
PSA_CHK(psa_hash_abort(&operation));
/* Calculate okey */
for (i = 0; i < mac_key_length; i++) {
key_buf[i] = (unsigned char) ((key_buf[i] ^ 0x36) ^ 0x5C);
}
for (; i < block_size; ++i) {
key_buf[i] = 0x5C;
}
/* Now compute HASH(okey + inner_hash) */
PSA_CHK(psa_hash_setup(&operation, hash_alg));
PSA_CHK(psa_hash_update(&operation, key_buf, block_size));
PSA_CHK(psa_hash_update(&operation, output, hash_size));
PSA_CHK(psa_hash_finish(&operation, output, hash_size, &hash_length));
#undef PSA_CHK
cleanup:
mbedtls_platform_zeroize(key_buf, MAX_HASH_BLOCK_LENGTH);
mbedtls_platform_zeroize(aux_out, PSA_HASH_MAX_SIZE);
psa_hash_abort(&operation);
psa_hash_abort(&aux_operation);
return PSA_TO_MBEDTLS_ERR(status);
}
#undef MAX_HASH_BLOCK_LENGTH
#else
MBEDTLS_STATIC_TESTABLE
int mbedtls_ct_hmac(mbedtls_md_context_t *ctx,
const unsigned char *add_data,
size_t add_data_len,
const unsigned char *data,
size_t data_len_secret,
size_t min_data_len,
size_t max_data_len,
unsigned char *output)
{
/*
* This function breaks the HMAC abstraction and uses the md_clone()
* extension to the MD API in order to get constant-flow behaviour.
*
* HMAC(msg) is defined as HASH(okey + HASH(ikey + msg)) where + means
* concatenation, and okey/ikey are the XOR of the key with some fixed bit
* patterns (see RFC 2104, sec. 2), which are stored in ctx->hmac_ctx.
*
* We'll first compute inner_hash = HASH(ikey + msg) by hashing up to
* minlen, then cloning the context, and for each byte up to maxlen
* finishing up the hash computation, keeping only the correct result.
*
* Then we only need to compute HASH(okey + inner_hash) and we're done.
*/
const mbedtls_md_type_t md_alg = mbedtls_md_get_type(ctx->md_info);
/* TLS 1.2 only supports SHA-384, SHA-256, SHA-1, MD-5,
* all of which have the same block size except SHA-384. */
const size_t block_size = md_alg == MBEDTLS_MD_SHA384 ? 128 : 64;
const unsigned char * const ikey = ctx->hmac_ctx;
const unsigned char * const okey = ikey + block_size;
const size_t hash_size = mbedtls_md_get_size(ctx->md_info);
unsigned char aux_out[MBEDTLS_MD_MAX_SIZE];
mbedtls_md_context_t aux;
size_t offset;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
mbedtls_md_init(&aux);
#define MD_CHK(func_call) \
do { \
ret = (func_call); \
if (ret != 0) \
goto cleanup; \
} while (0)
MD_CHK(mbedtls_md_setup(&aux, ctx->md_info, 0));
/* After hmac_start() of hmac_reset(), ikey has already been hashed,
* so we can start directly with the message */
MD_CHK(mbedtls_md_update(ctx, add_data, add_data_len));
MD_CHK(mbedtls_md_update(ctx, data, min_data_len));
/* Fill the hash buffer in advance with something that is
* not a valid hash (barring an attack on the hash and
* deliberately-crafted input), in case the caller doesn't
* check the return status properly. */
memset(output, '!', hash_size);
/* For each possible length, compute the hash up to that point */
for (offset = min_data_len; offset <= max_data_len; offset++) {
MD_CHK(mbedtls_md_clone(&aux, ctx));
MD_CHK(mbedtls_md_finish(&aux, aux_out));
/* Keep only the correct inner_hash in the output buffer */
mbedtls_ct_memcpy_if(mbedtls_ct_uint_eq(offset, data_len_secret),
output, aux_out, NULL, hash_size);
if (offset < max_data_len) {
MD_CHK(mbedtls_md_update(ctx, data + offset, 1));
}
}
/* The context needs to finish() before it starts() again */
MD_CHK(mbedtls_md_finish(ctx, aux_out));
/* Now compute HASH(okey + inner_hash) */
MD_CHK(mbedtls_md_starts(ctx));
MD_CHK(mbedtls_md_update(ctx, okey, block_size));
MD_CHK(mbedtls_md_update(ctx, output, hash_size));
MD_CHK(mbedtls_md_finish(ctx, output));
/* Done, get ready for next time */
MD_CHK(mbedtls_md_hmac_reset(ctx));
#undef MD_CHK
cleanup:
mbedtls_md_free(&aux);
return ret;
}
#endif /* MBEDTLS_USE_PSA_CRYPTO */
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_MAC */
static uint32_t ssl_get_hs_total_len(mbedtls_ssl_context const *ssl);
/*
@ -1690,11 +1918,11 @@ hmac_failed_etm_enabled:
padlen = data[rec->data_len - 1];
if (auth_done == 1) {
const size_t mask = mbedtls_ct_size_mask_ge(
const mbedtls_ct_condition_t ge = mbedtls_ct_uint_ge(
rec->data_len,
padlen + 1);
correct &= mask;
padlen &= mask;
correct = mbedtls_ct_size_if_else_0(ge, correct);
padlen = mbedtls_ct_size_if_else_0(ge, padlen);
} else {
#if defined(MBEDTLS_SSL_DEBUG_ALL)
if (rec->data_len < transform->maclen + padlen + 1) {
@ -1706,12 +1934,11 @@ hmac_failed_etm_enabled:
padlen + 1));
}
#endif
const size_t mask = mbedtls_ct_size_mask_ge(
const mbedtls_ct_condition_t ge = mbedtls_ct_uint_ge(
rec->data_len,
transform->maclen + padlen + 1);
correct &= mask;
padlen &= mask;
correct = mbedtls_ct_size_if_else_0(ge, correct);
padlen = mbedtls_ct_size_if_else_0(ge, padlen);
}
padlen++;
@ -1740,19 +1967,20 @@ hmac_failed_etm_enabled:
/* pad_count += (idx >= padding_idx) &&
* (check[idx] == padlen - 1);
*/
const size_t mask = mbedtls_ct_size_mask_ge(idx, padding_idx);
const size_t equal = mbedtls_ct_size_bool_eq(check[idx],
padlen - 1);
pad_count += mask & equal;
const mbedtls_ct_condition_t a = mbedtls_ct_uint_ge(idx, padding_idx);
size_t increment = mbedtls_ct_size_if_else_0(a, 1);
const mbedtls_ct_condition_t b = mbedtls_ct_uint_eq(check[idx], padlen - 1);
increment = mbedtls_ct_size_if_else_0(b, increment);
pad_count += increment;
}
correct &= mbedtls_ct_size_bool_eq(pad_count, padlen);
correct = mbedtls_ct_size_if_else_0(mbedtls_ct_uint_eq(pad_count, padlen), padlen);
#if defined(MBEDTLS_SSL_DEBUG_ALL)
if (padlen > 0 && correct == 0) {
MBEDTLS_SSL_DEBUG_MSG(1, ("bad padding byte detected"));
}
#endif
padlen &= mbedtls_ct_size_mask(correct);
padlen = mbedtls_ct_size_if_else_0(mbedtls_ct_bool(correct), padlen);
#endif /* MBEDTLS_SSL_PROTO_TLS1_2 */

View file

@ -3506,9 +3506,8 @@ static int ssl_parse_encrypted_pms(mbedtls_ssl_context *ssl,
unsigned char *pms = ssl->handshake->premaster + pms_offset;
unsigned char ver[2];
unsigned char fake_pms[48], peer_pms[48];
unsigned char mask;
size_t i, peer_pmslen;
unsigned int diff;
size_t peer_pmslen;
mbedtls_ct_condition_t diff;
/* In case of a failure in decryption, the decryption may write less than
* 2 bytes of output, but we always read the first two bytes. It doesn't
@ -3537,13 +3536,10 @@ static int ssl_parse_encrypted_pms(mbedtls_ssl_context *ssl,
/* Avoid data-dependent branches while checking for invalid
* padding, to protect against timing-based Bleichenbacher-type
* attacks. */
diff = (unsigned int) ret;
diff |= peer_pmslen ^ 48;
diff |= peer_pms[0] ^ ver[0];
diff |= peer_pms[1] ^ ver[1];
/* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */
mask = mbedtls_ct_uint_mask(diff);
diff = mbedtls_ct_bool(ret);
diff = mbedtls_ct_bool_or(diff, mbedtls_ct_uint_ne(peer_pmslen, 48));
diff = mbedtls_ct_bool_or(diff, mbedtls_ct_uint_ne(peer_pms[0], ver[0]));
diff = mbedtls_ct_bool_or(diff, mbedtls_ct_uint_ne(peer_pms[1], ver[1]));
/*
* Protection against Bleichenbacher's attack: invalid PKCS#1 v1.5 padding
@ -3562,7 +3558,7 @@ static int ssl_parse_encrypted_pms(mbedtls_ssl_context *ssl,
}
#if defined(MBEDTLS_SSL_DEBUG_ALL)
if (diff != 0) {
if (diff != MBEDTLS_CT_FALSE) {
MBEDTLS_SSL_DEBUG_MSG(1, ("bad client key exchange message"));
}
#endif
@ -3576,9 +3572,7 @@ static int ssl_parse_encrypted_pms(mbedtls_ssl_context *ssl,
/* Set pms to either the true or the fake PMS, without
* data-dependent branches. */
for (i = 0; i < ssl->handshake->pmslen; i++) {
pms[i] = (mask & fake_pms[i]) | ((~mask) & peer_pms[i]);
}
mbedtls_ct_memcpy_if(diff, pms, fake_pms, peer_pms, ssl->handshake->pmslen);
return 0;
}

View file

@ -1,27 +1,3 @@
mask_of_range empty (1..0)
mask_of_range:1:0
mask_of_range empty (255..0)
mask_of_range:255:0
mask_of_range empty (42..7)
mask_of_range:42:7
mask_of_range 0..0
mask_of_range:0:0
mask_of_range 42..42
mask_of_range:42:42
mask_of_range 255..255
mask_of_range:255:255
mask_of_range 0..255
mask_of_range:0:255
mask_of_range 'A'..'Z'
mask_of_range:65:90
enc_char (all digits)
enc_chars:

View file

@ -1,7 +1,7 @@
/* BEGIN_HEADER */
#include "mbedtls/base64.h"
#include "base64_internal.h"
#include "constant_time_internal.h"
#include "constant_time_invasive.h"
#include <test/constant_flow.h>
#if defined(MBEDTLS_TEST_HOOKS)
@ -16,26 +16,6 @@ static const char base64_digits[] =
* END_DEPENDENCIES
*/
/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
void mask_of_range(int low_arg, int high_arg)
{
unsigned char low = low_arg, high = high_arg;
unsigned c;
for (c = 0; c <= 0xff; c++) {
mbedtls_test_set_step(c);
TEST_CF_SECRET(&c, sizeof(c));
unsigned char m = mbedtls_ct_uchar_mask_of_range(low, high, c);
TEST_CF_PUBLIC(&c, sizeof(c));
TEST_CF_PUBLIC(&m, sizeof(m));
if (low <= c && c <= high) {
TEST_EQUAL(m, 0xff);
} else {
TEST_EQUAL(m, 0);
}
}
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_TEST_HOOKS */
void enc_chars()
{

View file

@ -438,7 +438,7 @@ void mpi_lt_mpi_ct(int size_X, char *input_X,
TEST_ASSERT(mbedtls_mpi_lt_mpi_ct(&X, &Y, &ret) == input_err);
if (input_err == 0) {
TEST_ASSERT(ret == input_uret);
TEST_EQUAL(ret, input_uret);
}
exit:

View file

@ -358,7 +358,7 @@ void mpi_core_lt_ct(char *input_X, char *input_Y, int exp_ret)
TEST_CF_SECRET(Y, X_limbs * sizeof(mbedtls_mpi_uint));
ret = mbedtls_mpi_core_lt_ct(X, Y, X_limbs);
TEST_EQUAL(ret, exp_ret);
TEST_EQUAL(!!ret, exp_ret);
exit:
mbedtls_free(X);
@ -384,25 +384,25 @@ void mpi_core_uint_le_mpi(char *input_A)
TEST_CF_SECRET(A, A_limbs * sizeof(*A));
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(0, A, A_limbs), 1);
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0], A, A_limbs), 1);
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(0, A, A_limbs), 1);
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0], A, A_limbs), 1);
if (is_large) {
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
A, A_limbs), 1);
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
A, A_limbs), 1);
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
A, A_limbs), 1);
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
A, A_limbs), 1);
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
A, A_limbs), 1);
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
A, A_limbs), 1);
} else {
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
A, A_limbs),
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi(A[0] + 1,
A, A_limbs),
A[0] + 1 <= A[0]);
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
A, A_limbs),
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1) >> 1,
A, A_limbs),
(mbedtls_mpi_uint) (-1) >> 1 <= A[0]);
TEST_EQUAL(mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
A, A_limbs),
TEST_EQUAL(!!mbedtls_mpi_core_uint_le_mpi((mbedtls_mpi_uint) (-1),
A, A_limbs),
(mbedtls_mpi_uint) (-1) <= A[0]);
}
@ -447,7 +447,7 @@ void mpi_core_cond_assign(char *input_X,
TEST_CF_SECRET(X, bytes);
TEST_CF_SECRET(Y, bytes);
mbedtls_mpi_core_cond_assign(X, Y, copy_limbs, 1);
mbedtls_mpi_core_cond_assign(X, Y, copy_limbs, mbedtls_ct_bool(1));
TEST_CF_PUBLIC(X, bytes);
TEST_CF_PUBLIC(Y, bytes);
@ -515,7 +515,7 @@ void mpi_core_cond_swap(char *input_X,
TEST_CF_SECRET(X, bytes);
TEST_CF_SECRET(Y, bytes);
mbedtls_mpi_core_cond_swap(X, Y, copy_limbs, 1);
mbedtls_mpi_core_cond_swap(X, Y, copy_limbs, mbedtls_ct_bool(1));
TEST_CF_PUBLIC(X, bytes);
TEST_CF_PUBLIC(Y, bytes);

View file

@ -134,7 +134,7 @@ void mpi_core_random_basic(int min, char *bound_bytes, int expected_ret)
if (expected_ret == 0) {
TEST_EQUAL(0, mbedtls_mpi_core_lt_ct(result, lower_bound, limbs));
TEST_EQUAL(1, mbedtls_mpi_core_lt_ct(result, upper_bound, limbs));
TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result, upper_bound, limbs));
}
exit:
@ -429,8 +429,7 @@ void mpi_mod_random_validation(int min, char *bound_hex,
* size as the modulus, otherwise it's a mistake in the test data. */
TEST_EQUAL(result_limbs, N.limbs);
/* Sanity check: check that the result is in range */
TEST_EQUAL(mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs),
1);
TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs));
/* Check result >= min (changes result) */
TEST_EQUAL(mbedtls_mpi_core_sub_int(result_digits, result_digits, min,
result_limbs),
@ -444,8 +443,7 @@ void mpi_mod_random_validation(int min, char *bound_hex,
mbedtls_test_rnd_std_rand, NULL),
expected_ret);
if (expected_ret == 0) {
TEST_EQUAL(mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs),
1);
TEST_ASSERT(0 != mbedtls_mpi_core_lt_ct(result_digits, N.p, N.limbs));
TEST_EQUAL(mbedtls_mpi_core_sub_int(result_digits, result.p, min,
result_limbs),
0);

View file

@ -1,14 +1,14 @@
# these are the numbers we'd get with an empty plaintext and truncated HMAC
Constant-flow memcpy from offset: small
ssl_cf_memcpy_offset:0:5:10
mbedtls_ct_memcpy_offset:0:5:10
# we could get this with 255-bytes plaintext and untruncated SHA-256
Constant-flow memcpy from offset: medium
ssl_cf_memcpy_offset:0:255:32
mbedtls_ct_memcpy_offset:0:255:32
# we could get this with 255-bytes plaintext and untruncated SHA-384
Constant-flow memcpy from offset: large
ssl_cf_memcpy_offset:100:339:48
mbedtls_ct_memcpy_offset:100:339:48
mbedtls_ct_memcmp NULL
mbedtls_ct_memcmp_null
@ -91,47 +91,611 @@ mbedtls_ct_memcmp:-1:17:2
mbedtls_ct_memcmp len 17 offset 3
mbedtls_ct_memcmp:-1:17:3
mbedtls_ct_memcpy_if_eq len 1 offset 0
mbedtls_ct_memcpy_if_eq:1:1:0
mbedtls_ct_memcpy_if len 1 offset 0
mbedtls_ct_memcpy_if:1:1:0
mbedtls_ct_memcpy_if_eq len 1 offset 1
mbedtls_ct_memcpy_if_eq:1:1:1
mbedtls_ct_memcpy_if len 1 offset 1
mbedtls_ct_memcpy_if:1:1:1
mbedtls_ct_memcpy_if_eq len 4 offset 0
mbedtls_ct_memcpy_if_eq:1:1:0
mbedtls_ct_memcpy_if len 4 offset 0
mbedtls_ct_memcpy_if:1:1:0
mbedtls_ct_memcpy_if_eq len 4 offset 1
mbedtls_ct_memcpy_if_eq:1:1:1
mbedtls_ct_memcpy_if len 4 offset 1
mbedtls_ct_memcpy_if:1:1:1
mbedtls_ct_memcpy_if_eq len 4 offset 2
mbedtls_ct_memcpy_if_eq:1:1:2
mbedtls_ct_memcpy_if len 4 offset 2
mbedtls_ct_memcpy_if:1:1:2
mbedtls_ct_memcpy_if_eq len 4 offset 3
mbedtls_ct_memcpy_if_eq:1:1:3
mbedtls_ct_memcpy_if len 4 offset 3
mbedtls_ct_memcpy_if:1:1:3
mbedtls_ct_memcpy_if_eq len 15 offset 0
mbedtls_ct_memcpy_if_eq:1:15:0
mbedtls_ct_memcpy_if len 15 offset 0
mbedtls_ct_memcpy_if:1:15:0
mbedtls_ct_memcpy_if_eq len 15 offset 1
mbedtls_ct_memcpy_if_eq:1:15:1
mbedtls_ct_memcpy_if len 15 offset 1
mbedtls_ct_memcpy_if:1:15:1
mbedtls_ct_memcpy_if_eq len 16 offset 0
mbedtls_ct_memcpy_if_eq:1:16:0
mbedtls_ct_memcpy_if len 16 offset 0
mbedtls_ct_memcpy_if:1:16:0
mbedtls_ct_memcpy_if_eq len 16 offset 1
mbedtls_ct_memcpy_if_eq:1:16:1
mbedtls_ct_memcpy_if len 16 offset 1
mbedtls_ct_memcpy_if:1:16:1
mbedtls_ct_memcpy_if_eq len 17 offset 0
mbedtls_ct_memcpy_if_eq:1:17:0
mbedtls_ct_memcpy_if len 17 offset 0
mbedtls_ct_memcpy_if:1:17:0
mbedtls_ct_memcpy_if_eq len 17 offset 1
mbedtls_ct_memcpy_if_eq:1:17:1
mbedtls_ct_memcpy_if len 17 offset 1
mbedtls_ct_memcpy_if:1:17:1
mbedtls_ct_memcpy_if_eq len 0 not eq
mbedtls_ct_memcpy_if_eq:0:17:0
mbedtls_ct_memcpy_if len 0 not eq
mbedtls_ct_memcpy_if:0:17:0
mbedtls_ct_memcpy_if_eq len 5 offset 1 not eq
mbedtls_ct_memcpy_if_eq:0:5:1
mbedtls_ct_memcpy_if len 5 offset 1 not eq
mbedtls_ct_memcpy_if:0:5:1
mbedtls_ct_memcpy_if_eq len 17 offset 3 not eq
mbedtls_ct_memcpy_if_eq:0:17:3
mbedtls_ct_memcpy_if len 17 offset 3 not eq
mbedtls_ct_memcpy_if:0:17:3
mbedtls_ct_bool 0
mbedtls_ct_bool:"0x0"
mbedtls_ct_bool 1
mbedtls_ct_bool:"0x1"
mbedtls_ct_bool 4
mbedtls_ct_bool:"0x4"
mbedtls_ct_bool 0xfffffff
mbedtls_ct_bool:"0xfffffff"
mbedtls_ct_bool 0x7fffffff
mbedtls_ct_bool:"0x7fffffff"
mbedtls_ct_bool 0xfffffffe
mbedtls_ct_bool:"0xfffffffe"
mbedtls_ct_bool 0xffffffff
mbedtls_ct_bool:"0xffffffff"
mbedtls_ct_bool 0x0fffffffffffffff
mbedtls_ct_bool:"0x0fffffffffffffff"
mbedtls_ct_bool 0x7fffffffffffffff
mbedtls_ct_bool:"0x7fffffffffffffff"
mbedtls_ct_bool 0xffffffffffffffff
mbedtls_ct_bool:"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0x0 0x0
mbedtls_ct_bool_xxx:"0x0":"0x0"
mbedtls_ct_bool_xxx 0x0 0x1
mbedtls_ct_bool_xxx:"0x0":"0x1"
mbedtls_ct_bool_xxx 0x0 0x7fffffff
mbedtls_ct_bool_xxx:"0x0":"0x7fffffff"
mbedtls_ct_bool_xxx 0x0 0xffffffff
mbedtls_ct_bool_xxx:"0x0":"0xffffffff"
mbedtls_ct_bool_xxx 0x0 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0x0":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0x0 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0x0":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0x1 0x0
mbedtls_ct_bool_xxx:"0x1":"0x0"
mbedtls_ct_bool_xxx 0x1 0x1
mbedtls_ct_bool_xxx:"0x1":"0x1"
mbedtls_ct_bool_xxx 0x1 0x7fffffff
mbedtls_ct_bool_xxx:"0x1":"0x7fffffff"
mbedtls_ct_bool_xxx 0x1 0xffffffff
mbedtls_ct_bool_xxx:"0x1":"0xffffffff"
mbedtls_ct_bool_xxx 0x1 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0x1":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0x1 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0x1":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0x7fffffff 0x0
mbedtls_ct_bool_xxx:"0x7fffffff":"0x0"
mbedtls_ct_bool_xxx 0x7fffffff 0x1
mbedtls_ct_bool_xxx:"0x7fffffff":"0x1"
mbedtls_ct_bool_xxx 0x7fffffff 0x7fffffff
mbedtls_ct_bool_xxx:"0x7fffffff":"0x7fffffff"
mbedtls_ct_bool_xxx 0x7fffffff 0xffffffff
mbedtls_ct_bool_xxx:"0x7fffffff":"0xffffffff"
mbedtls_ct_bool_xxx 0x7fffffff 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0x7fffffff":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0x7fffffff 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0x7fffffff":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0xffffffff 0x0
mbedtls_ct_bool_xxx:"0xffffffff":"0x0"
mbedtls_ct_bool_xxx 0xffffffff 0x1
mbedtls_ct_bool_xxx:"0xffffffff":"0x1"
mbedtls_ct_bool_xxx 0xffffffff 0x7fffffff
mbedtls_ct_bool_xxx:"0xffffffff":"0x7fffffff"
mbedtls_ct_bool_xxx 0xffffffff 0xffffffff
mbedtls_ct_bool_xxx:"0xffffffff":"0xffffffff"
mbedtls_ct_bool_xxx 0xffffffff 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0xffffffff":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0xffffffff 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0xffffffff":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x0
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x0"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x1
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x1"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x7fffffff
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x7fffffff"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0xffffffff
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0xffffffff"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0x7fffffffffffffff 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0x7fffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0x0
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x0"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0x1
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x1"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0x7fffffff
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x7fffffff"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0xffffffff
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0xffffffff"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0x7fffffffffffffff
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_bool_xxx 0xffffffffffffffff 0xffffffffffffffff
mbedtls_ct_bool_xxx:"0xffffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_bool_xxx 138 256
mbedtls_ct_bool_xxx:"138":"256"
mbedtls_ct_bool_xxx 256 138
mbedtls_ct_bool_xxx:"256":"138"
mbedtls_ct_bool_xxx 6 6
mbedtls_ct_bool_xxx:"0x6":"0x6"
mbedtls_ct_uchar_in_range_if 0 0 0
mbedtls_ct_uchar_in_range_if:0:0:0
mbedtls_ct_uchar_in_range_if 0 0 100
mbedtls_ct_uchar_in_range_if:0:0:100
mbedtls_ct_uchar_in_range_if 0 0 255
mbedtls_ct_uchar_in_range_if:0:0:255
mbedtls_ct_uchar_in_range_if 0 65 0
mbedtls_ct_uchar_in_range_if:0:65:0
mbedtls_ct_uchar_in_range_if 0 65 100
mbedtls_ct_uchar_in_range_if:0:65:100
mbedtls_ct_uchar_in_range_if 0 65 255
mbedtls_ct_uchar_in_range_if:0:65:255
mbedtls_ct_uchar_in_range_if 0 90 0
mbedtls_ct_uchar_in_range_if:0:90:0
mbedtls_ct_uchar_in_range_if 0 90 100
mbedtls_ct_uchar_in_range_if:0:90:100
mbedtls_ct_uchar_in_range_if 0 90 255
mbedtls_ct_uchar_in_range_if:0:90:255
mbedtls_ct_uchar_in_range_if 0 255 0
mbedtls_ct_uchar_in_range_if:0:255:0
mbedtls_ct_uchar_in_range_if 0 255 100
mbedtls_ct_uchar_in_range_if:0:255:100
mbedtls_ct_uchar_in_range_if 0 255 255
mbedtls_ct_uchar_in_range_if:0:255:255
mbedtls_ct_uchar_in_range_if 65 0 0
mbedtls_ct_uchar_in_range_if:65:0:0
mbedtls_ct_uchar_in_range_if 65 0 100
mbedtls_ct_uchar_in_range_if:65:0:100
mbedtls_ct_uchar_in_range_if 65 0 255
mbedtls_ct_uchar_in_range_if:65:0:255
mbedtls_ct_uchar_in_range_if 65 65 0
mbedtls_ct_uchar_in_range_if:65:65:0
mbedtls_ct_uchar_in_range_if 65 65 100
mbedtls_ct_uchar_in_range_if:65:65:100
mbedtls_ct_uchar_in_range_if 65 65 255
mbedtls_ct_uchar_in_range_if:65:65:255
mbedtls_ct_uchar_in_range_if 65 90 0
mbedtls_ct_uchar_in_range_if:65:90:0
mbedtls_ct_uchar_in_range_if 65 90 100
mbedtls_ct_uchar_in_range_if:65:90:100
mbedtls_ct_uchar_in_range_if 65 90 255
mbedtls_ct_uchar_in_range_if:65:90:255
mbedtls_ct_uchar_in_range_if 65 255 0
mbedtls_ct_uchar_in_range_if:65:255:0
mbedtls_ct_uchar_in_range_if 65 255 100
mbedtls_ct_uchar_in_range_if:65:255:100
mbedtls_ct_uchar_in_range_if 65 255 255
mbedtls_ct_uchar_in_range_if:65:255:255
mbedtls_ct_uchar_in_range_if 90 0 0
mbedtls_ct_uchar_in_range_if:90:0:0
mbedtls_ct_uchar_in_range_if 90 0 100
mbedtls_ct_uchar_in_range_if:90:0:100
mbedtls_ct_uchar_in_range_if 90 0 255
mbedtls_ct_uchar_in_range_if:90:0:255
mbedtls_ct_uchar_in_range_if 90 65 0
mbedtls_ct_uchar_in_range_if:90:65:0
mbedtls_ct_uchar_in_range_if 90 65 100
mbedtls_ct_uchar_in_range_if:90:65:100
mbedtls_ct_uchar_in_range_if 90 65 255
mbedtls_ct_uchar_in_range_if:90:65:255
mbedtls_ct_uchar_in_range_if 90 90 0
mbedtls_ct_uchar_in_range_if:90:90:0
mbedtls_ct_uchar_in_range_if 90 90 100
mbedtls_ct_uchar_in_range_if:90:90:100
mbedtls_ct_uchar_in_range_if 90 90 255
mbedtls_ct_uchar_in_range_if:90:90:255
mbedtls_ct_uchar_in_range_if 90 255 0
mbedtls_ct_uchar_in_range_if:90:255:0
mbedtls_ct_uchar_in_range_if 90 255 100
mbedtls_ct_uchar_in_range_if:90:255:100
mbedtls_ct_uchar_in_range_if 90 255 255
mbedtls_ct_uchar_in_range_if:90:255:255
mbedtls_ct_uchar_in_range_if 255 0 0
mbedtls_ct_uchar_in_range_if:255:0:0
mbedtls_ct_uchar_in_range_if 255 0 100
mbedtls_ct_uchar_in_range_if:255:0:100
mbedtls_ct_uchar_in_range_if 255 0 255
mbedtls_ct_uchar_in_range_if:255:0:255
mbedtls_ct_uchar_in_range_if 255 65 0
mbedtls_ct_uchar_in_range_if:255:65:0
mbedtls_ct_uchar_in_range_if 255 65 100
mbedtls_ct_uchar_in_range_if:255:65:100
mbedtls_ct_uchar_in_range_if 255 65 255
mbedtls_ct_uchar_in_range_if:255:65:255
mbedtls_ct_uchar_in_range_if 255 90 0
mbedtls_ct_uchar_in_range_if:255:90:0
mbedtls_ct_uchar_in_range_if 255 90 100
mbedtls_ct_uchar_in_range_if:255:90:100
mbedtls_ct_uchar_in_range_if 255 90 255
mbedtls_ct_uchar_in_range_if:255:90:255
mbedtls_ct_uchar_in_range_if 255 255 0
mbedtls_ct_uchar_in_range_if:255:255:0
mbedtls_ct_uchar_in_range_if 255 255 100
mbedtls_ct_uchar_in_range_if:255:255:100
mbedtls_ct_uchar_in_range_if 255 255 255
mbedtls_ct_uchar_in_range_if:255:255:255
mbedtls_ct_if 0x0 0x0 0x0
mbedtls_ct_if:"0x0":"0x0":"0x0"
mbedtls_ct_if 0x0 0x0 0x1
mbedtls_ct_if:"0x0":"0x0":"0x1"
mbedtls_ct_if 0x0 0x0 0x7fffffff
mbedtls_ct_if:"0x0":"0x0":"0x7fffffff"
mbedtls_ct_if 0x0 0x0 0xffffffff
mbedtls_ct_if:"0x0":"0x0":"0xffffffff"
mbedtls_ct_if 0x0 0x0 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0x0":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0x0 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0x0":"0xffffffffffffffff"
mbedtls_ct_if 0x0 0x1 0x0
mbedtls_ct_if:"0x0":"0x1":"0x0"
mbedtls_ct_if 0x0 0x1 0x1
mbedtls_ct_if:"0x0":"0x1":"0x1"
mbedtls_ct_if 0x0 0x1 0x7fffffff
mbedtls_ct_if:"0x0":"0x1":"0x7fffffff"
mbedtls_ct_if 0x0 0x1 0xffffffff
mbedtls_ct_if:"0x0":"0x1":"0xffffffff"
mbedtls_ct_if 0x0 0x1 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0x1":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0x1 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0x1":"0xffffffffffffffff"
mbedtls_ct_if 0x0 0x7fffffff 0x0
mbedtls_ct_if:"0x0":"0x7fffffff":"0x0"
mbedtls_ct_if 0x0 0x7fffffff 0x1
mbedtls_ct_if:"0x0":"0x7fffffff":"0x1"
mbedtls_ct_if 0x0 0x7fffffff 0x7fffffff
mbedtls_ct_if:"0x0":"0x7fffffff":"0x7fffffff"
mbedtls_ct_if 0x0 0x7fffffff 0xffffffff
mbedtls_ct_if:"0x0":"0x7fffffff":"0xffffffff"
mbedtls_ct_if 0x0 0x7fffffff 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0x7fffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0x7fffffff 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0x7fffffff":"0xffffffffffffffff"
mbedtls_ct_if 0x0 0xffffffff 0x0
mbedtls_ct_if:"0x0":"0xffffffff":"0x0"
mbedtls_ct_if 0x0 0xffffffff 0x1
mbedtls_ct_if:"0x0":"0xffffffff":"0x1"
mbedtls_ct_if 0x0 0xffffffff 0x7fffffff
mbedtls_ct_if:"0x0":"0xffffffff":"0x7fffffff"
mbedtls_ct_if 0x0 0xffffffff 0xffffffff
mbedtls_ct_if:"0x0":"0xffffffff":"0xffffffff"
mbedtls_ct_if 0x0 0xffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0xffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0xffffffff 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0xffffffff":"0xffffffffffffffff"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0x0
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x0"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0x1
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x1"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0x7fffffff
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x7fffffff"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0xffffffff
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0xffffffff"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0x7fffffffffffffff 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0x7fffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_if 0x0 0xffffffffffffffff 0x0
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x0"
mbedtls_ct_if 0x0 0xffffffffffffffff 0x1
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x1"
mbedtls_ct_if 0x0 0xffffffffffffffff 0x7fffffff
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x7fffffff"
mbedtls_ct_if 0x0 0xffffffffffffffff 0xffffffff
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0xffffffff"
mbedtls_ct_if 0x0 0xffffffffffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0x0 0xffffffffffffffff 0xffffffffffffffff
mbedtls_ct_if:"0x0":"0xffffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x0 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0x0 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0x0 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0x0 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x0 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x0 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x0":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x1 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0x1 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0x1 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0x1 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x1 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x1 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x1":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffff 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffff":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffff 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffff":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0x7fffffffffffffff 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0x7fffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x0
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x0"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x1
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x1"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x7fffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x7fffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0xffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0xffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0x7fffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0x7fffffffffffffff"
mbedtls_ct_if 0xffffffffffffffff 0xffffffffffffffff 0xffffffffffffffff
mbedtls_ct_if:"0xffffffffffffffff":"0xffffffffffffffff":"0xffffffffffffffff"
mbedtls_ct_zeroize_if 0x0 0
mbedtls_ct_zeroize_if:"0x0":0
mbedtls_ct_zeroize_if 0x0 1
mbedtls_ct_zeroize_if:"0x0":1
mbedtls_ct_zeroize_if 0x0 1024
mbedtls_ct_zeroize_if:"0x0":1024
mbedtls_ct_zeroize_if 0xffffffffffffffff 0
mbedtls_ct_zeroize_if:"0xffffffffffffffff":0
mbedtls_ct_zeroize_if 0xffffffffffffffff 1
mbedtls_ct_zeroize_if:"0xffffffffffffffff":1
mbedtls_ct_zeroize_if 0xffffffffffffffff 4
mbedtls_ct_zeroize_if:"0xffffffffffffffff":4
mbedtls_ct_zeroize_if 0xffffffffffffffff 5
mbedtls_ct_zeroize_if:"0xffffffffffffffff":5
mbedtls_ct_zeroize_if 0xffffffffffffffff 7
mbedtls_ct_zeroize_if:"0xffffffffffffffff":7
mbedtls_ct_zeroize_if 0xffffffffffffffff 8
mbedtls_ct_zeroize_if:"0xffffffffffffffff":8
mbedtls_ct_zeroize_if 0xffffffffffffffff 9
mbedtls_ct_zeroize_if:"0xffffffffffffffff":9
mbedtls_ct_zeroize_if 0xffffffffffffffff 1024
mbedtls_ct_zeroize_if:"0xffffffffffffffff":1024
mbedtls_ct_memmove_left 0 0
mbedtls_ct_memmove_left:0:0
mbedtls_ct_memmove_left 1 0
mbedtls_ct_memmove_left:1:0
mbedtls_ct_memmove_left 1 1
mbedtls_ct_memmove_left:1:1
mbedtls_ct_memmove_left 16 0
mbedtls_ct_memmove_left:16:0
mbedtls_ct_memmove_left 16 1
mbedtls_ct_memmove_left:16:1
mbedtls_ct_memmove_left 16 4
mbedtls_ct_memmove_left:16:4
mbedtls_ct_memmove_left 16 15
mbedtls_ct_memmove_left:16:15
mbedtls_ct_memmove_left 16 16
mbedtls_ct_memmove_left:16:16

View file

@ -8,9 +8,15 @@
* under MSan or Valgrind will detect a non-constant-time implementation.
*/
#include <stdio.h>
#include <limits.h>
#include <stdlib.h>
#include <errno.h>
#include <mbedtls/bignum.h>
#include <mbedtls/constant_time.h>
#include <constant_time_internal.h>
#include <constant_time_invasive.h>
#include <test/constant_flow.h>
/* END_HEADER */
@ -25,6 +31,147 @@ void mbedtls_ct_memcmp_null()
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_ct_bool(char *input)
{
mbedtls_ct_uint_t v = (mbedtls_ct_uint_t) strtoull(input, NULL, 16);
TEST_ASSERT(errno == 0);
mbedtls_ct_condition_t expected = (v != 0) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_CF_SECRET(&v, sizeof(v));
TEST_EQUAL(mbedtls_ct_bool(v), expected);
TEST_CF_PUBLIC(&v, sizeof(v));
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_ct_bool_xxx(char *x_str, char *y_str)
{
mbedtls_ct_uint_t x = strtoull(x_str, NULL, 0);
mbedtls_ct_uint_t y = strtoull(y_str, NULL, 0);
mbedtls_ct_uint_t x1 = x;
mbedtls_ct_uint_t y1 = y;
TEST_CF_SECRET(&x, sizeof(x));
TEST_CF_SECRET(&y, sizeof(y));
mbedtls_ct_condition_t expected = x1 ? MBEDTLS_CT_FALSE : MBEDTLS_CT_TRUE;
TEST_EQUAL(mbedtls_ct_bool_not(mbedtls_ct_bool(x)), expected);
expected = x1 != y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_ne(x, y), expected);
expected = x1 == y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_eq(x, y), expected);
expected = x1 > y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_gt(x, y), expected);
expected = x1 < y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_lt(x, y), expected);
expected = x1 >= y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_ge(x, y), expected);
expected = x1 <= y1 ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_uint_le(x, y), expected);
expected = (!!x1) ^ (!!y1) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_bool_xor(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
expected = (!!x1) && (!!y1) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_bool_and(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
expected = (!!x1) || (!!y1) ? MBEDTLS_CT_TRUE : MBEDTLS_CT_FALSE;
TEST_EQUAL(mbedtls_ct_bool_or(mbedtls_ct_bool(x), mbedtls_ct_bool(y)), expected);
TEST_CF_PUBLIC(&x, sizeof(x));
TEST_CF_PUBLIC(&y, sizeof(y));
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_BASE64_C */
void mbedtls_ct_uchar_in_range_if(int li, int hi, int ti)
{
unsigned char l = li, h = hi, t = ti;
for (unsigned x = 0; x <= 255; x++) {
unsigned char expected = (x >= l) && (x <= h) ? t : 0;
TEST_CF_SECRET(&x, sizeof(x));
TEST_CF_SECRET(&l, sizeof(l));
TEST_CF_SECRET(&h, sizeof(h));
TEST_CF_SECRET(&t, sizeof(t));
TEST_EQUAL(mbedtls_ct_uchar_in_range_if(l, h, (unsigned char) x, t), expected);
TEST_CF_PUBLIC(&x, sizeof(x));
TEST_CF_PUBLIC(&l, sizeof(l));
TEST_CF_PUBLIC(&h, sizeof(h));
TEST_CF_PUBLIC(&t, sizeof(t));
}
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_ct_if(char *c_str, char *t_str, char *f_str)
{
mbedtls_ct_condition_t c = mbedtls_ct_bool(strtoull(c_str, NULL, 16));
mbedtls_ct_uint_t t = (mbedtls_ct_uint_t) strtoull(t_str, NULL, 16);
mbedtls_ct_uint_t f = (mbedtls_ct_uint_t) strtoull(f_str, NULL, 16);
mbedtls_ct_uint_t expected = c ? t : f;
mbedtls_ct_uint_t expected0 = c ? t : 0;
TEST_CF_SECRET(&c, sizeof(c));
TEST_CF_SECRET(&t, sizeof(t));
TEST_CF_SECRET(&f, sizeof(f));
TEST_EQUAL(mbedtls_ct_if(c, t, f), expected);
TEST_EQUAL(mbedtls_ct_size_if(c, t, f), (size_t) expected);
TEST_EQUAL(mbedtls_ct_uint_if(c, t, f), (unsigned) expected);
#if defined(MBEDTLS_BIGNUM_C)
TEST_EQUAL(mbedtls_ct_mpi_uint_if(c, t, f), (mbedtls_mpi_uint) expected);
#endif
TEST_EQUAL(mbedtls_ct_uint_if_else_0(c, t), (unsigned) expected0);
TEST_EQUAL(mbedtls_ct_size_if_else_0(c, (size_t) t), (size_t) expected0);
#if defined(MBEDTLS_BIGNUM_C)
TEST_EQUAL(mbedtls_ct_mpi_uint_if_else_0(c, t), (mbedtls_mpi_uint) expected0);
#endif
TEST_CF_PUBLIC(&c, sizeof(c));
TEST_CF_PUBLIC(&t, sizeof(t));
TEST_CF_PUBLIC(&f, sizeof(f));
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_PKCS1_V15:MBEDTLS_RSA_C:!MBEDTLS_RSA_ALT */
void mbedtls_ct_zeroize_if(char *c_str, int len)
{
uint8_t *buf = NULL;
mbedtls_ct_condition_t c = mbedtls_ct_bool(strtoull(c_str, NULL, 16));
TEST_CALLOC(buf, len);
for (size_t i = 0; i < (size_t) len; i++) {
buf[i] = 1;
}
TEST_CF_SECRET(&c, sizeof(c));
TEST_CF_SECRET(buf, len);
mbedtls_ct_zeroize_if(c, buf, len);
TEST_CF_PUBLIC(&c, sizeof(c));
TEST_CF_PUBLIC(buf, len);
for (size_t i = 0; i < (size_t) len; i++) {
TEST_EQUAL(buf[i], c != 0 ? 0 : 1);
}
exit:
mbedtls_free(buf);
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_ct_memcmp(int same, int size, int offset)
{
@ -32,9 +179,6 @@ void mbedtls_ct_memcmp(int same, int size, int offset)
TEST_CALLOC(a, size + offset);
TEST_CALLOC(b, size + offset);
TEST_CF_SECRET(a + offset, size);
TEST_CF_SECRET(b + offset, size);
/* Construct data that matches, if same == -1, otherwise
* same gives the number of bytes (after the initial offset)
* that will match; after that it will differ.
@ -49,9 +193,15 @@ void mbedtls_ct_memcmp(int same, int size, int offset)
}
int reference = memcmp(a + offset, b + offset, size);
TEST_CF_SECRET(a, size + offset);
TEST_CF_SECRET(b, size + offset);
int actual = mbedtls_ct_memcmp(a + offset, b + offset, size);
TEST_CF_PUBLIC(a + offset, size);
TEST_CF_PUBLIC(b + offset, size);
TEST_CF_PUBLIC(a, size + offset);
TEST_CF_PUBLIC(b, size + offset);
TEST_CF_PUBLIC(&actual, sizeof(actual));
if (same == -1 || same >= size) {
TEST_ASSERT(reference == 0);
@ -66,59 +216,139 @@ exit:
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_MAC */
void mbedtls_ct_memcpy_if_eq(int eq, int size, int offset)
/* BEGIN_CASE */
void mbedtls_ct_memcpy_if(int eq, int size, int offset)
{
uint8_t *src = NULL, *result = NULL, *expected = NULL;
uint8_t *src = NULL, *src2 = NULL, *result = NULL, *expected = NULL;
TEST_CALLOC(src, size + offset);
TEST_CALLOC(src2, size + offset);
TEST_CALLOC(result, size + offset);
TEST_CALLOC(expected, size + offset);
/* Apply offset to result only */
for (int i = 0; i < size + offset; i++) {
src[i] = 1;
result[i] = 0xff;
src[i] = 1;
result[i] = 0xff;
expected[i] = eq ? 1 : 0xff;
}
int one, secret_eq;
TEST_CF_SECRET(&one, sizeof(one));
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
one = 1;
secret_eq = eq;
int secret_eq = eq;
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
TEST_CF_SECRET(src, size + offset);
TEST_CF_SECRET(result, size + offset);
mbedtls_ct_memcpy_if_eq(result + offset, src, size, secret_eq, one);
mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result + offset, src, NULL, size);
TEST_CF_PUBLIC(&one, sizeof(one));
TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
TEST_CF_PUBLIC(src, size + offset);
TEST_CF_PUBLIC(result, size + offset);
TEST_MEMORY_COMPARE(expected, size, result + offset, size);
/* Apply offset to src only */
for (int i = 0; i < size + offset; i++) {
src[i] = 1;
result[i] = 0xff;
expected[i] = eq ? 1 : 0xff;
}
TEST_CF_SECRET(&one, sizeof(one));
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
one = 1;
secret_eq = eq;
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
TEST_CF_SECRET(src, size + offset);
TEST_CF_SECRET(result, size + offset);
mbedtls_ct_memcpy_if_eq(result, src + offset, size, secret_eq, one);
mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result, src + offset, NULL, size);
TEST_CF_PUBLIC(&one, sizeof(one));
TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
TEST_CF_PUBLIC(src, size + offset);
TEST_CF_PUBLIC(result, size + offset);
TEST_MEMORY_COMPARE(expected, size, result, size);
/* Apply offset to src and src2 */
for (int i = 0; i < size + offset; i++) {
src[i] = 1;
src2[i] = 2;
result[i] = 0xff;
expected[i] = eq ? 1 : 2;
}
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
TEST_CF_SECRET(src, size + offset);
TEST_CF_SECRET(src2, size + offset);
TEST_CF_SECRET(result, size + offset);
mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), result, src + offset, src2 + offset, size);
TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
TEST_CF_PUBLIC(src, size + offset);
TEST_CF_SECRET(src2, size + offset);
TEST_CF_PUBLIC(result, size + offset);
TEST_MEMORY_COMPARE(expected, size, result, size);
/* result == src == dest */
for (int i = 0; i < size + offset; i++) {
src[i] = 2;
expected[i] = 2;
}
TEST_CF_SECRET(&secret_eq, sizeof(secret_eq));
TEST_CF_SECRET(src, size + offset);
TEST_CF_SECRET(result, size + offset);
mbedtls_ct_memcpy_if(mbedtls_ct_bool(secret_eq), src + offset, src + offset, src + offset,
size);
TEST_CF_PUBLIC(&secret_eq, sizeof(secret_eq));
TEST_CF_PUBLIC(src, size + offset);
TEST_CF_PUBLIC(result, size + offset);
TEST_MEMORY_COMPARE(expected, size, src + offset, size);
exit:
mbedtls_free(src);
mbedtls_free(src2);
mbedtls_free(result);
mbedtls_free(expected);
}
/* END_CASE */
/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
void ssl_cf_memcpy_offset(int offset_min, int offset_max, int len)
/* BEGIN_CASE depends_on:MBEDTLS_PKCS1_V15:MBEDTLS_RSA_C:!MBEDTLS_RSA_ALT */
void mbedtls_ct_memmove_left(int len, int offset)
{
size_t l = (size_t) len;
size_t o = (size_t) offset;
uint8_t *buf = NULL, *buf_expected = NULL;
TEST_CALLOC(buf, l);
TEST_CALLOC(buf_expected, l);
for (size_t i = 0; i < l; i++) {
buf[i] = (uint8_t) i;
buf_expected[i] = buf[i];
}
TEST_CF_SECRET(&o, sizeof(o));
TEST_CF_SECRET(buf, l);
mbedtls_ct_memmove_left(buf, l, o);
TEST_CF_PUBLIC(&o, sizeof(o));
TEST_CF_PUBLIC(buf, l);
if (l > 0) {
memmove(buf_expected, buf_expected + o, l - o);
memset(buf_expected + (l - o), 0, o);
TEST_ASSERT(memcmp(buf, buf_expected, l) == 0);
}
exit:
mbedtls_free(buf);
mbedtls_free(buf_expected);
}
/* END_CASE */
/* BEGIN_CASE */
void mbedtls_ct_memcpy_offset(int offset_min, int offset_max, int len)
{
unsigned char *dst = NULL;
unsigned char *src = NULL;
@ -135,9 +365,12 @@ void ssl_cf_memcpy_offset(int offset_min, int offset_max, int len)
mbedtls_test_set_step((int) secret);
TEST_CF_SECRET(&secret, sizeof(secret));
TEST_CF_SECRET(src, len);
TEST_CF_SECRET(dst, len);
mbedtls_ct_memcpy_offset(dst, src, secret,
offset_min, offset_max, len);
TEST_CF_PUBLIC(&secret, sizeof(secret));
TEST_CF_PUBLIC(src, len);
TEST_CF_PUBLIC(dst, len);
TEST_MEMORY_COMPARE(dst, len, src + secret, len);

View file

@ -8,7 +8,7 @@
#include <test/constant_flow.h>
/* END_HEADER */
/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
/* BEGIN_CASE depends_on:MBEDTLS_SSL_SOME_SUITES_USE_MAC:MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC:MBEDTLS_TEST_HOOKS */
void ssl_cf_hmac(int hash)
{
/*

View file

@ -1622,7 +1622,7 @@ void ecp_mod_random(int id, int ctype)
TEST_EQUAL(0, mbedtls_mpi_mod_random(&rX, 1, &m,
mbedtls_test_rnd_std_rand, NULL));
TEST_ASSERT(mbedtls_mpi_core_lt_ct(rX.p, m.p, limbs) == 1);
TEST_ASSERT(mbedtls_mpi_core_lt_ct(rX.p, m.p, limbs) == MBEDTLS_CT_TRUE);
exit:
mbedtls_mpi_mod_modulus_free(&m);