mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-07-30 22:43:08 +03:00
Merge pull request #7578 from daverodgman/safer-ct5
Improve constant-time interface
This commit is contained in:
@ -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
57
library/base64_internal.h
Normal 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 */
|
130
library/bignum.c
130
library/bignum.c
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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.
|
||||
*
|
||||
|
@ -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
306
library/constant_time_impl.h
Normal file
306
library/constant_time_impl.h
Normal 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 */
|
@ -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 */
|
||||
|
@ -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 */
|
158
library/rsa.c
158
library/rsa.c
@ -56,6 +56,164 @@
|
||||
|
||||
#include "mbedtls/platform.h"
|
||||
|
||||
|
||||
#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
|
||||
|
||||
/** This function performs the unpadding part of a PKCS#1 v1.5 decryption
|
||||
* operation (EME-PKCS1-v1_5 decoding).
|
||||
*
|
||||
* \note The return value from this function is a sensitive value
|
||||
* (this is unusual). #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE shouldn't happen
|
||||
* in a well-written application, but 0 vs #MBEDTLS_ERR_RSA_INVALID_PADDING
|
||||
* is often a situation that an attacker can provoke and leaking which
|
||||
* one is the result is precisely the information the attacker wants.
|
||||
*
|
||||
* \param input The input buffer which is the payload inside PKCS#1v1.5
|
||||
* encryption padding, called the "encoded message EM"
|
||||
* by the terminology.
|
||||
* \param ilen The length of the payload in the \p input buffer.
|
||||
* \param output The buffer for the payload, called "message M" by the
|
||||
* PKCS#1 terminology. This must be a writable buffer of
|
||||
* length \p output_max_len bytes.
|
||||
* \param olen The address at which to store the length of
|
||||
* the payload. This must not be \c NULL.
|
||||
* \param output_max_len The length in bytes of the output buffer \p output.
|
||||
*
|
||||
* \return \c 0 on success.
|
||||
* \return #MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE
|
||||
* The output buffer is too small for the unpadded payload.
|
||||
* \return #MBEDTLS_ERR_RSA_INVALID_PADDING
|
||||
* The input doesn't contain properly formatted padding.
|
||||
*/
|
||||
static int mbedtls_ct_rsaes_pkcs1_v15_unpadding(unsigned char *input,
|
||||
size_t ilen,
|
||||
unsigned char *output,
|
||||
size_t output_max_len,
|
||||
size_t *olen)
|
||||
{
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
size_t i, plaintext_max_size;
|
||||
|
||||
/* The following variables take sensitive values: their value must
|
||||
* not leak into the observable behavior of the function other than
|
||||
* the designated outputs (output, olen, return value). Otherwise
|
||||
* this would open the execution of the function to
|
||||
* side-channel-based variants of the Bleichenbacher padding oracle
|
||||
* attack. Potential side channels include overall timing, memory
|
||||
* access patterns (especially visible to an adversary who has access
|
||||
* to a shared memory cache), and branches (especially visible to
|
||||
* an adversary who has access to a shared code cache or to a shared
|
||||
* branch predictor). */
|
||||
size_t pad_count = 0;
|
||||
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,
|
||||
|
@ -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 */
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
Reference in New Issue
Block a user