Merge pull request #5154 from gabor-mezei-arm/3649_bp2x_move_constant_time_functions_into_separate_module

[Backport 2.x] Move constant-time functions into a separate module
This commit is contained in:
Gilles Peskine 2021-11-24 19:33:03 +01:00 committed by GitHub
commit 3107b337e1
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
19 changed files with 1182 additions and 983 deletions

View file

@ -0,0 +1,10 @@
Changes
* The mbedcrypto library includes a new source code module constant_time.c,
containing various functions meant to resist timing side channel attacks.
This module does not have a separate configuration option, and functions
from this module will be included in the build as required. Currently
most of the interface of this module is private and may change at any
time.
Features
* Add new API mbedtls_ct_memcmp for constant time buffer comparison.

View file

@ -0,0 +1,45 @@
/**
* Constant-time functions
*
* 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_H
#define MBEDTLS_CONSTANT_TIME_H
#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.
*
* 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.
*
* \return Zero if the content of the two buffer is the same,
* otherwise non-zero.
*/
int mbedtls_ct_memcmp( const void *a,
const void *b,
size_t n );
#endif /* MBEDTLS_CONSTANT_TIME_H */

View file

@ -1212,26 +1212,6 @@ void mbedtls_ssl_dtls_replay_update( mbedtls_ssl_context *ssl );
int mbedtls_ssl_session_copy( mbedtls_ssl_session *dst,
const mbedtls_ssl_session *src );
/* constant-time buffer comparison */
static inline int mbedtls_ssl_safer_memcmp( const void *a, const void *b, size_t n )
{
size_t i;
volatile const unsigned char *A = (volatile const unsigned char *) a;
volatile const unsigned char *B = (volatile const unsigned char *) b;
volatile unsigned char diff = 0;
for( i = 0; i < n; i++ )
{
/* Read volatile data in order before computing diff.
* This avoids IAR compiler warning:
* 'the order of volatile accesses is undefined ..' */
unsigned char x = A[i], y = B[i];
diff |= x ^ y;
}
return( diff );
}
#if defined(MBEDTLS_SSL_PROTO_SSL3) || defined(MBEDTLS_SSL_PROTO_TLS1) || \
defined(MBEDTLS_SSL_PROTO_TLS1_1)
int mbedtls_ssl_get_key_exchange_md_ssl_tls( mbedtls_ssl_context *ssl,

View file

@ -26,6 +26,7 @@ set(src_crypto
chachapoly.c
cipher.c
cipher_wrap.c
constant_time.c
cmac.c
ctr_drbg.c
des.c

View file

@ -84,6 +84,7 @@ OBJS_CRYPTO= \
cipher.o \
cipher_wrap.o \
cmac.o \
constant_time.o \
ctr_drbg.o \
des.o \
dhm.o \

View file

@ -41,6 +41,7 @@
#include "mbedtls/bn_mul.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
#include "constant_time_internal.h"
#include <string.h>
@ -268,162 +269,6 @@ void mbedtls_mpi_swap( mbedtls_mpi *X, mbedtls_mpi *Y )
memcpy( Y, &T, sizeof( mbedtls_mpi ) );
}
/**
* Select between two sign values in constant-time.
*
* This is functionally equivalent to second ? a : b but uses only bit
* operations in order to avoid branches.
*
* \param[in] a The first sign; must be either +1 or -1.
* \param[in] b The second sign; must be either +1 or -1.
* \param[in] second Must be either 1 (return b) or 0 (return a).
*
* \return The selected sign value.
*/
static int mpi_safe_cond_select_sign( int a, int b, unsigned char second )
{
/* In order to avoid questions about what we can reasonnably assume about
* the representations of signed integers, move everything to unsigned
* by taking advantage of the fact that a and b are either +1 or -1. */
unsigned ua = a + 1;
unsigned ub = b + 1;
/* second was 0 or 1, mask is 0 or 2 as are ua and ub */
const unsigned mask = second << 1;
/* select ua or ub */
unsigned ur = ( ua & ~mask ) | ( ub & mask );
/* ur is now 0 or 2, convert back to -1 or +1 */
return( (int) ur - 1 );
}
/*
* Conditionally assign dest = src, without leaking information
* about whether the assignment was made or not.
* dest and src must be arrays of limbs of size n.
* assign must be 0 or 1.
*/
static void mpi_safe_cond_assign( size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
unsigned char assign )
{
size_t i;
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */
const mbedtls_mpi_uint mask = -assign;
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
for( i = 0; i < n; i++ )
dest[i] = ( src[i] & mask ) | ( dest[i] & ~mask );
}
/*
* 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.)
*/
int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X, const mbedtls_mpi *Y, unsigned char assign )
{
int ret = 0;
size_t i;
mbedtls_mpi_uint limb_mask;
MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( Y != NULL );
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* make sure assign is 0 or 1 in a time-constant manner */
assign = (assign | (unsigned char)-assign) >> (sizeof( assign ) * 8 - 1);
/* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */
limb_mask = -assign;
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
X->s = mpi_safe_cond_select_sign( X->s, Y->s, assign );
mpi_safe_cond_assign( Y->n, X->p, Y->p, assign );
for( i = Y->n; i < X->n; i++ )
X->p[i] &= ~limb_mask;
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 whould 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, s;
size_t i;
mbedtls_mpi_uint limb_mask;
mbedtls_mpi_uint tmp;
MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( Y != NULL );
if( X == Y )
return( 0 );
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* make sure swap is 0 or 1 in a time-constant manner */
swap = (swap | (unsigned char)-swap) >> (sizeof( swap ) * 8 - 1);
/* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */
limb_mask = -swap;
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( Y, X->n ) );
s = X->s;
X->s = mpi_safe_cond_select_sign( X->s, Y->s, swap );
Y->s = mpi_safe_cond_select_sign( Y->s, s, swap );
for( i = 0; i < X->n; i++ )
{
tmp = X->p[i];
X->p[i] = ( X->p[i] & ~limb_mask ) | ( Y->p[i] & limb_mask );
Y->p[i] = ( Y->p[i] & ~limb_mask ) | ( tmp & limb_mask );
}
cleanup:
return( ret );
}
/*
* Set value from integer
*/
@ -1246,107 +1091,6 @@ int mbedtls_mpi_cmp_mpi( const mbedtls_mpi *X, const mbedtls_mpi *Y )
return( 0 );
}
/** Decide if an integer is less than the other, without branches.
*
* \param x First integer.
* \param y Second integer.
*
* \return 1 if \p x is less than \p y, 0 otherwise
*/
static unsigned ct_lt_mpi_uint( const mbedtls_mpi_uint x,
const mbedtls_mpi_uint y )
{
mbedtls_mpi_uint ret;
mbedtls_mpi_uint cond;
/*
* Check if the most significant bits (MSB) of the operands are different.
*/
cond = ( x ^ y );
/*
* 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.
*/
ret = ( x - y ) & ~cond;
/*
* 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.)
*/
ret |= y & cond;
ret = ret >> ( biL - 1 );
return (unsigned) ret;
}
/*
* Compare signed values in constant time
*/
int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X, const mbedtls_mpi *Y,
unsigned *ret )
{
size_t i;
/* The value of any of these variables is either 0 or 1 at all times. */
unsigned cond, done, X_is_negative, Y_is_negative;
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 = ( X->s & 2 ) >> 1;
Y_is_negative = ( 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).
*/
cond = ( X_is_negative ^ Y_is_negative );
*ret = cond & X_is_negative;
/*
* This is a constant-time function. We might have the result, but we still
* need to go through the loop. Record if we have the result already.
*/
done = cond;
for( i = X->n; i > 0; i-- )
{
/*
* If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
* X and Y are negative.
*
* Again even if we can make a decision, we just mark the result and
* the fact that we are done and continue looping.
*/
cond = ct_lt_mpi_uint( Y->p[i - 1], X->p[i - 1] );
*ret |= cond & ( 1 - done ) & X_is_negative;
done |= cond;
/*
* If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
* X and Y are positive.
*
* Again even if we can make a decision, we just mark the result and
* the fact that we are done and continue looping.
*/
cond = ct_lt_mpi_uint( X->p[i - 1], Y->p[i - 1] );
*ret |= cond & ( 1 - done ) & ( 1 - X_is_negative );
done |= cond;
}
return( 0 );
}
/*
* Compare signed values
*/
@ -2207,7 +1951,7 @@ static void mpi_montmul( mbedtls_mpi *A, const mbedtls_mpi *B, const mbedtls_mpi
* so d[n] == 1 and we want to set A to the result of the subtraction
* which is d - (2^biL)^n, i.e. the n least significant limbs of d.
* This exactly corresponds to a conditional assignment. */
mpi_safe_cond_assign( n, A->p, d, (unsigned char) d[n] );
mbedtls_ct_mpi_uint_cond_assign( n, A->p, d, (unsigned char) d[n] );
}
/*
@ -2227,42 +1971,6 @@ static void mpi_montred( mbedtls_mpi *A, const mbedtls_mpi *N,
mpi_montmul( A, &U, N, mm, T );
}
/*
* Constant-flow boolean "equal" comparison:
* return x == y
*
* This function can be used to write constant-time code by replacing branches
* with bit operations - it can be used in conjunction with
* mbedtls_ssl_cf_mask_from_bit().
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static size_t mbedtls_mpi_cf_bool_eq( size_t x, size_t y )
{
/* diff = 0 if x == y, non-zero otherwise */
const size_t diff = x ^ y;
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* diff_msb's most significant bit is equal to x != y */
const size_t diff_msb = ( diff | (size_t) -diff );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
/* diff1 = (x != y) ? 1 : 0 */
const size_t diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
return( 1 ^ diff1 );
}
/**
* Select an MPI from a table without leaking the index.
*
@ -2285,7 +1993,7 @@ 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_mpi_cf_bool_eq( i, idx ) ) );
(unsigned char) mbedtls_ct_size_bool_eq( i, idx ) ) );
}
cleanup:

View file

@ -29,6 +29,7 @@
#include "mbedtls/cipher_internal.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
#include "mbedtls/constant_time.h"
#include <stdlib.h>
#include <string.h>
@ -74,27 +75,6 @@
#define CIPHER_VALIDATE( cond ) \
MBEDTLS_INTERNAL_VALIDATE( cond )
#if defined(MBEDTLS_GCM_C) || defined(MBEDTLS_CHACHAPOLY_C)
/* Compare the contents of two buffers in constant time.
* Returns 0 if the contents are bitwise identical, otherwise returns
* a non-zero value.
* This is currently only used by GCM and ChaCha20+Poly1305.
*/
static int mbedtls_constant_time_memcmp( const void *v1, const void *v2,
size_t len )
{
const unsigned char *p1 = (const unsigned char*) v1;
const unsigned char *p2 = (const unsigned char*) v2;
size_t i;
unsigned char diff;
for( diff = 0, i = 0; i < len; i++ )
diff |= p1[i] ^ p2[i];
return( (int)diff );
}
#endif /* MBEDTLS_GCM_C || MBEDTLS_CHACHAPOLY_C */
static int supported_init = 0;
const int *mbedtls_cipher_list( void )
@ -1159,7 +1139,7 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
}
/* Check the tag in "constant-time" */
if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 )
if( mbedtls_ct_memcmp( tag, check_tag, tag_len ) != 0 )
return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
return( 0 );
@ -1181,7 +1161,7 @@ int mbedtls_cipher_check_tag( mbedtls_cipher_context_t *ctx,
}
/* Check the tag in "constant-time" */
if( mbedtls_constant_time_memcmp( tag, check_tag, tag_len ) != 0 )
if( mbedtls_ct_memcmp( tag, check_tag, tag_len ) != 0 )
return( MBEDTLS_ERR_CIPHER_AUTH_FAILED );
return( 0 );

760
library/constant_time.c Normal file
View file

@ -0,0 +1,760 @@
/**
* Constant-time functions
*
* 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.
*/
/*
* The following functions are implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
#include "common.h"
#include "constant_time_internal.h"
#include "mbedtls/constant_time.h"
#include "mbedtls/error.h"
#include "mbedtls/platform_util.h"
#if defined(MBEDTLS_BIGNUM_C)
#include "mbedtls/bignum.h"
#endif
#if defined(MBEDTLS_SSL_TLS_C)
#include "mbedtls/ssl_internal.h"
#endif
#if defined(MBEDTLS_RSA_C)
#include "mbedtls/rsa.h"
#endif
#include <string.h>
int mbedtls_ct_memcmp( const void *a,
const void *b,
size_t n )
{
size_t i;
volatile const unsigned char *A = (volatile const unsigned char *) a;
volatile const unsigned char *B = (volatile const unsigned char *) b;
volatile unsigned char diff = 0;
for( i = 0; i < n; i++ )
{
/* Read volatile data in order before computing diff.
* This avoids IAR compiler warning:
* 'the order of volatile accesses is undefined ..' */
unsigned char x = A[i], y = B[i];
diff |= x ^ y;
}
return( (int)diff );
}
unsigned mbedtls_ct_uint_mask( unsigned value )
{
/* MSVC has a warning about unary minus on unsigned, but this is
* well-defined and precisely what we want to do here */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
}
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
size_t mbedtls_ct_size_mask( size_t value )
{
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
}
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
#if defined(MBEDTLS_BIGNUM_C)
mbedtls_mpi_uint mbedtls_ct_mpi_uint_mask( mbedtls_mpi_uint value )
{
/* MSVC has a warning about unary minus on unsigned, but this is
* well-defined and precisely what we want to do here */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
}
#endif /* MBEDTLS_BIGNUM_C */
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/** Constant-flow mask generation for "less than" 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.
*
* \param x The first value to analyze.
* \param y The second value to analyze.
*
* \return All-bits-one if \p x is less than \p y, otherwise zero.
*/
static size_t mbedtls_ct_size_mask_lt( size_t x,
size_t y )
{
/* This has the most significant bit set if and only if x < y */
const size_t sub = x - y;
/* sub1 = (x < y) ? 1 : 0 */
const size_t sub1 = sub >> ( sizeof( sub ) * 8 - 1 );
/* mask = (x < y) ? 0xff... : 0x00... */
const size_t mask = mbedtls_ct_size_mask( sub1 );
return( mask );
}
size_t mbedtls_ct_size_mask_ge( size_t x,
size_t y )
{
return( ~mbedtls_ct_size_mask_lt( x, y ) );
}
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
unsigned mbedtls_ct_size_bool_eq( size_t x,
size_t y )
{
/* diff = 0 if x == y, non-zero otherwise */
const size_t diff = x ^ y;
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* diff_msb's most significant bit is equal to x != y */
const size_t diff_msb = ( diff | (size_t) -diff );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
/* diff1 = (x != y) ? 1 : 0 */
const unsigned diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
return( 1 ^ diff1 );
}
#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
/** Constant-flow "greater than" comparison:
* return x > y
*
* This is equivalent to \p x > \p y, but is likely to be compiled
* to code using bitwise operation rather than a branch.
*
* \param x The first value to analyze.
* \param y The second value to analyze.
*
* \return 1 if \p x greater than \p y, otherwise 0.
*/
static unsigned mbedtls_ct_size_gt( size_t x,
size_t y )
{
/* Return the sign bit (1 for negative) of (y - x). */
return( ( y - x ) >> ( sizeof( size_t ) * 8 - 1 ) );
}
#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
#if defined(MBEDTLS_BIGNUM_C)
unsigned mbedtls_ct_mpi_uint_lt( const mbedtls_mpi_uint x,
const mbedtls_mpi_uint y )
{
mbedtls_mpi_uint ret;
mbedtls_mpi_uint cond;
/*
* Check if the most significant bits (MSB) of the operands are different.
*/
cond = ( x ^ y );
/*
* 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.
*/
ret = ( x - y ) & ~cond;
/*
* 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.)
*/
ret |= y & cond;
ret = ret >> ( sizeof( mbedtls_mpi_uint ) * 8 - 1 );
return (unsigned) ret;
}
#endif /* MBEDTLS_BIGNUM_C */
unsigned mbedtls_ct_uint_if( unsigned condition,
unsigned if1,
unsigned if0 )
{
unsigned mask = mbedtls_ct_uint_mask( condition );
return( ( mask & if1 ) | (~mask & if0 ) );
}
#if defined(MBEDTLS_BIGNUM_C)
/** Select between two sign values without branches.
*
* This is functionally equivalent to `condition ? if1 : if0` but uses only bit
* operations in order to avoid branches.
*
* \note if1 and if0 must be either 1 or -1, otherwise the result
* is undefined.
*
* \param condition Condition to test.
* \param if1 The first sign; must be either +1 or -1.
* \param if0 The second sign; must be either +1 or -1.
*
* \return \c if1 if \p condition is nonzero, otherwise \c if0.
* */
static int mbedtls_ct_cond_select_sign( unsigned char condition,
int if1,
int if0 )
{
/* In order to avoid questions about what we can reasonably assume about
* the representations of signed integers, move everything to unsigned
* by taking advantage of the fact that if1 and if0 are either +1 or -1. */
unsigned uif1 = if1 + 1;
unsigned uif0 = if0 + 1;
/* condition was 0 or 1, mask is 0 or 2 as are uif1 and uif0 */
const unsigned mask = condition << 1;
/* select uif1 or uif0 */
unsigned ur = ( uif0 & ~mask ) | ( uif1 & mask );
/* ur is now 0 or 2, convert back to -1 or +1 */
return( (int) ur - 1 );
}
void mbedtls_ct_mpi_uint_cond_assign( size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
unsigned char condition )
{
size_t i;
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* all-bits 1 if condition is 1, all-bits 0 if condition is 0 */
const mbedtls_mpi_uint mask = -condition;
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
for( i = 0; i < n; i++ )
dest[i] = ( src[i] & mask ) | ( dest[i] & ~mask );
}
#endif /* MBEDTLS_BIGNUM_C */
#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
/** Shift some data towards the left inside a buffer.
*
* `mbedtls_ct_mem_move_to_left(start, total, offset)` is functionally
* equivalent to
* ```
* memmove(start, start + offset, total - offset);
* memset(start + offset, 0, total - offset);
* ```
* but it strives to use a memory access pattern (and thus total timing)
* that does not depend on \p offset. This timing independence comes at
* the expense of performance.
*
* \param start Pointer to the start of the buffer.
* \param total Total size of the buffer.
* \param offset Offset from which to copy \p total - \p offset bytes.
*/
static void mbedtls_ct_mem_move_to_left( void *start,
size_t total,
size_t offset )
{
volatile unsigned char *buf = start;
size_t i, n;
if( total == 0 )
return;
for( i = 0; i < total; i++ )
{
unsigned no_op = mbedtls_ct_size_gt( total - offset, i );
/* The first `total - offset` passes are a no-op. The last
* `offset` passes shift the data one byte to the left and
* zero out the last byte. */
for( n = 0; n < total - 1; n++ )
{
unsigned char current = buf[n];
unsigned char next = buf[n+1];
buf[n] = mbedtls_ct_uint_if( no_op, current, next );
}
buf[total-1] = mbedtls_ct_uint_if( no_op, buf[total-1], 0 );
}
}
#endif /* MBEDTLS_PKCS1_V15 && MBEDTLS_RSA_C && ! MBEDTLS_RSA_ALT */
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
void mbedtls_ct_memcpy_if_eq( unsigned char *dest,
const unsigned char *src,
size_t len,
size_t c1,
size_t c2 )
{
/* mask = c1 == c2 ? 0xff : 0x00 */
const size_t equal = mbedtls_ct_size_bool_eq( c1, c2 );
const unsigned char mask = (unsigned char) mbedtls_ct_size_mask( equal );
/* dest[i] = c1 == c2 ? src[i] : dest[i] */
for( size_t i = 0; i < len; i++ )
dest[i] = ( src[i] & mask ) | ( dest[i] & ~mask );
}
void mbedtls_ct_memcpy_offset( unsigned char *dest,
const unsigned char *src,
size_t offset,
size_t offset_min,
size_t offset_max,
size_t len )
{
size_t offsetval;
for( offsetval = offset_min; offsetval <= offset_max; offsetval++ )
{
mbedtls_ct_memcpy_if_eq( dest, src + offsetval, len,
offsetval, offset );
}
}
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.0-1.2 only support 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 ) );
/* 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_eq( output, aux_out, hash_size,
offset, data_len_secret );
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_SSL_SOME_SUITES_USE_TLS_CBC */
#if defined(MBEDTLS_BIGNUM_C)
#define MPI_VALIDATE_RET( cond ) \
MBEDTLS_INTERNAL_VALIDATE_RET( cond, MBEDTLS_ERR_MPI_BAD_INPUT_DATA )
/*
* 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.)
*/
int mbedtls_mpi_safe_cond_assign( mbedtls_mpi *X,
const mbedtls_mpi *Y,
unsigned char assign )
{
int ret = 0;
size_t i;
mbedtls_mpi_uint limb_mask;
MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( Y != NULL );
/* all-bits 1 if assign is 1, all-bits 0 if assign is 0 */
limb_mask = mbedtls_ct_mpi_uint_mask( assign );;
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
X->s = mbedtls_ct_cond_select_sign( assign, Y->s, X->s );
mbedtls_ct_mpi_uint_cond_assign( Y->n, X->p, Y->p, assign );
for( i = Y->n; i < X->n; i++ )
X->p[i] &= ~limb_mask;
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 whould 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, s;
size_t i;
mbedtls_mpi_uint limb_mask;
mbedtls_mpi_uint tmp;
MPI_VALIDATE_RET( X != NULL );
MPI_VALIDATE_RET( Y != NULL );
if( X == Y )
return( 0 );
/* all-bits 1 if swap is 1, all-bits 0 if swap is 0 */
limb_mask = mbedtls_ct_mpi_uint_mask( swap );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( X, Y->n ) );
MBEDTLS_MPI_CHK( mbedtls_mpi_grow( Y, X->n ) );
s = X->s;
X->s = mbedtls_ct_cond_select_sign( swap, Y->s, X->s );
Y->s = mbedtls_ct_cond_select_sign( swap, s, Y->s );
for( i = 0; i < X->n; i++ )
{
tmp = X->p[i];
X->p[i] = ( X->p[i] & ~limb_mask ) | ( Y->p[i] & limb_mask );
Y->p[i] = ( Y->p[i] & ~limb_mask ) | ( tmp & limb_mask );
}
cleanup:
return( ret );
}
/*
* Compare signed values in constant time
*/
int mbedtls_mpi_lt_mpi_ct( const mbedtls_mpi *X,
const mbedtls_mpi *Y,
unsigned *ret )
{
size_t i;
/* The value of any of these variables is either 0 or 1 at all times. */
unsigned cond, done, X_is_negative, Y_is_negative;
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 = ( X->s & 2 ) >> 1;
Y_is_negative = ( 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).
*/
cond = ( X_is_negative ^ Y_is_negative );
*ret = cond & X_is_negative;
/*
* This is a constant-time function. We might have the result, but we still
* need to go through the loop. Record if we have the result already.
*/
done = cond;
for( i = X->n; i > 0; i-- )
{
/*
* If Y->p[i - 1] < X->p[i - 1] then X < Y is true if and only if both
* X and Y are negative.
*
* 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_mpi_uint_lt( Y->p[i - 1], X->p[i - 1] );
*ret |= cond & ( 1 - done ) & X_is_negative;
done |= cond;
/*
* If X->p[i - 1] < Y->p[i - 1] then X < Y is true if and only if both
* X and Y are positive.
*
* 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_mpi_uint_lt( X->p[i - 1], Y->p[i - 1] );
*ret |= cond & ( 1 - done ) & ( 1 - X_is_negative );
done |= cond;
}
return( 0 );
}
#endif /* MBEDTLS_BIGNUM_C */
#if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT)
int mbedtls_ct_rsaes_pkcs1_v15_unpadding( int mode,
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;
unsigned bad = 0;
unsigned char pad_done = 0;
size_t plaintext_size = 0;
unsigned 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 |= input[0];
if( mode == MBEDTLS_RSA_PRIVATE )
{
/* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
* where PS must be at least 8 nonzero bytes. */
bad |= 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. */
for( i = 2; i < ilen; i++ )
{
pad_done |= ((input[i] | (unsigned char)-input[i]) >> 7) ^ 1;
pad_count += ((pad_done | (unsigned char)-pad_done) >> 7) ^ 1;
}
}
else
{
/* Decode EMSA-PKCS1-v1_5 padding: 0x00 || 0x01 || PS || 0x00
* where PS must be at least 8 bytes with the value 0xFF. */
bad |= input[1] ^ MBEDTLS_RSA_SIGN;
/* Read the whole buffer. Set pad_done to nonzero if we find
* the 0x00 byte and remember the padding length in pad_count.
* If there's a non-0xff byte in the padding, the padding is bad. */
for( i = 2; i < ilen; i++ )
{
pad_done |= mbedtls_ct_uint_if( input[i], 0, 1 );
pad_count += mbedtls_ct_uint_if( pad_done, 0, 1 );
bad |= mbedtls_ct_uint_if( pad_done, 0, input[i] ^ 0xFF );
}
}
/* If pad_done is still zero, there's no data, only unfinished padding. */
bad |= mbedtls_ct_uint_if( pad_done, 0, 1 );
/* There must be at least 8 bytes of padding. */
bad |= mbedtls_ct_size_gt( 8, pad_count );
/* If the padding is valid, set plaintext_size to the number of
* remaining bytes after stripping the padding. If the padding
* is invalid, avoid leaking this fact through the size of the
* output: use the maximum message size that fits in the output
* buffer. Do it without branches to avoid leaking the padding
* validity through timing. RSA keys are small enough that all the
* size_t values involved fit in unsigned int. */
plaintext_size = mbedtls_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_size_gt( plaintext_size,
plaintext_max_size );
/* Set ret without branches to avoid timing attacks. Return:
* - INVALID_PADDING if the padding is bad (bad != 0).
* - OUTPUT_TOO_LARGE if the padding is good but the decrypted
* plaintext does not fit in the output buffer.
* - 0 if the padding is correct. */
ret = - (int) mbedtls_ct_uint_if(
bad, - MBEDTLS_ERR_RSA_INVALID_PADDING,
mbedtls_ct_uint_if( output_too_large,
- MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE,
0 ) );
/* If the padding is bad or the plaintext is too large, zero the
* data that we're about to copy to the output buffer.
* We need to copy the same amount of data
* from the same buffer whether the padding is good or not to
* avoid leaking the padding validity through overall timing or
* through memory or cache access patterns. */
bad = mbedtls_ct_uint_mask( bad | output_too_large );
for( i = 11; i < ilen; i++ )
input[i] &= ~bad;
/* If the plaintext is too large, truncate it to the buffer size.
* Copy anyway to avoid revealing the length through timing, because
* revealing the length is as bad as revealing the padding validity
* for a Bleichenbacher attack. */
plaintext_size = mbedtls_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_mem_move_to_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 */

View file

@ -0,0 +1,300 @@
/**
* Constant-time functions
*
* 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_INTERNAL_H
#define MBEDTLS_CONSTANT_TIME_INTERNAL_H
#include "common.h"
#if defined(MBEDTLS_BIGNUM_C)
#include "mbedtls/bignum.h"
#endif
#if defined(MBEDTLS_SSL_TLS_C)
#include "mbedtls/ssl_internal.h"
#endif
#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.
*/
unsigned mbedtls_ct_uint_mask( unsigned value );
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/** 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
*
* 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.
*/
size_t mbedtls_ct_size_mask( size_t value );
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
#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
*
* 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.
*/
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.
*
* \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.
*/
size_t mbedtls_ct_size_mask_ge( size_t x,
size_t y );
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
/** Constant-flow boolean "equal" comparison:
* return x == y
*
* This is equivalent to \p x == \p y, but is likely to be compiled
* to code using bitwise operation rather than a branch.
*
* \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.
*/
unsigned mbedtls_ct_size_bool_eq( size_t x,
size_t y );
#if defined(MBEDTLS_BIGNUM_C)
/** Decide if an integer is less than the other, without branches.
*
* This is equivalent to \p x < \p y, but is likely to be compiled
* to code using bitwise operation rather than a branch.
*
* \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.
*/
unsigned mbedtls_ct_mpi_uint_lt( const mbedtls_mpi_uint x,
const mbedtls_mpi_uint y );
#endif /* MBEDTLS_BIGNUM_C */
/** Choose between two integer values without branches.
*
* This is equivalent to `condition ? if1 : if0`, but is likely to be compiled
* to code using bitwise operation rather than a branch.
*
* \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.
*
* \return \c if1 if \p condition is nonzero, otherwise \c if0.
*/
unsigned mbedtls_ct_uint_if( unsigned condition,
unsigned if1,
unsigned if0 );
#if defined(MBEDTLS_BIGNUM_C)
/** Conditionally assign a value without branches.
*
* This is equivalent to `if ( condition ) dest = src`, but is likely
* to be compiled to code using bitwise operation rather than a branch.
*
* \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.
*/
void mbedtls_ct_mpi_uint_cond_assign( size_t n,
mbedtls_mpi_uint *dest,
const mbedtls_mpi_uint *src,
unsigned char condition );
#endif /* MBEDTLS_BIGNUM_C */
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/** Conditional memcpy without branches.
*
* 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.
*
* \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.
*/
void mbedtls_ct_memcpy_if_eq( unsigned char *dest,
const unsigned char *src,
size_t len,
size_t c1, size_t c2 );
/** Copy data from a secret position with constant flow.
*
* 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 dest 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
* 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.
* This must be no less than \p offset_min and no greater
* than \p offset_max.
* \param offset_min The minimal value of \p offset.
* \param offset_max The maximal value of \p offset.
* \param len The number of bytes to copy.
*/
void mbedtls_ct_memcpy_offset( unsigned char *dest,
const unsigned char *src,
size_t offset,
size_t offset_min,
size_t offset_max,
size_t len );
/** 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.
*/
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_SSL_SOME_SUITES_USE_TLS_CBC */
#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 mode The mode of operation. This must be either
* #MBEDTLS_RSA_PRIVATE or #MBEDTLS_RSA_PUBLIC (deprecated).
* \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( int mode,
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 */
#endif /* MBEDTLS_CONSTANT_TIME_INTERNAL_H */

View file

@ -34,6 +34,7 @@
#include "mbedtls/nist_kw.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
#include "mbedtls/constant_time.h"
#include <stdint.h>
#include <string.h>
@ -52,26 +53,6 @@
#define KW_SEMIBLOCK_LENGTH 8
#define MIN_SEMIBLOCKS_COUNT 3
/* constant-time buffer comparison */
static inline unsigned char mbedtls_nist_kw_safer_memcmp( const void *a, const void *b, size_t n )
{
size_t i;
volatile const unsigned char *A = (volatile const unsigned char *) a;
volatile const unsigned char *B = (volatile const unsigned char *) b;
volatile unsigned char diff = 0;
for( i = 0; i < n; i++ )
{
/* Read volatile data in order before computing diff.
* This avoids IAR compiler warning:
* 'the order of volatile accesses is undefined ..' */
unsigned char x = A[i], y = B[i];
diff |= x ^ y;
}
return( diff );
}
/*! The 64-bit default integrity check value (ICV) for KW mode. */
static const unsigned char NIST_KW_ICV1[] = {0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6, 0xA6};
/*! The 32-bit default integrity check value (ICV) for KWP mode. */
@ -398,7 +379,7 @@ int mbedtls_nist_kw_unwrap( mbedtls_nist_kw_context *ctx,
goto cleanup;
/* Check ICV in "constant-time" */
diff = mbedtls_nist_kw_safer_memcmp( NIST_KW_ICV1, A, KW_SEMIBLOCK_LENGTH );
diff = mbedtls_ct_memcmp( NIST_KW_ICV1, A, KW_SEMIBLOCK_LENGTH );
if( diff != 0 )
{
@ -447,7 +428,7 @@ int mbedtls_nist_kw_unwrap( mbedtls_nist_kw_context *ctx,
}
/* Check ICV in "constant-time" */
diff = mbedtls_nist_kw_safer_memcmp( NIST_KW_ICV2, A, KW_SEMIBLOCK_LENGTH / 2 );
diff = mbedtls_ct_memcmp( NIST_KW_ICV2, A, KW_SEMIBLOCK_LENGTH / 2 );
if( diff != 0 )
{

View file

@ -44,6 +44,8 @@
#include "mbedtls/oid.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/error.h"
#include "constant_time_internal.h"
#include "mbedtls/constant_time.h"
#include <string.h>
@ -72,22 +74,6 @@
#define RSA_VALIDATE( cond ) \
MBEDTLS_INTERNAL_VALIDATE( cond )
#if defined(MBEDTLS_PKCS1_V15)
/* constant-time buffer comparison */
static inline int mbedtls_safer_memcmp( const void *a, const void *b, size_t n )
{
size_t i;
const unsigned char *A = (const unsigned char *) a;
const unsigned char *B = (const unsigned char *) b;
unsigned char diff = 0;
for( i = 0; i < n; i++ )
diff |= A[i] ^ B[i];
return( diff );
}
#endif /* MBEDTLS_PKCS1_V15 */
int mbedtls_rsa_import( mbedtls_rsa_context *ctx,
const mbedtls_mpi *N,
const mbedtls_mpi *P, const mbedtls_mpi *Q,
@ -1494,126 +1480,21 @@ cleanup:
#endif /* MBEDTLS_PKCS1_V21 */
#if defined(MBEDTLS_PKCS1_V15)
/** Turn zero-or-nonzero into zero-or-all-bits-one, without branches.
*
* \param value The value to analyze.
* \return Zero if \p value is zero, otherwise all-bits-one.
*/
static unsigned all_or_nothing_int( unsigned value )
{
/* MSVC has a warning about unary minus on unsigned, but this is
* well-defined and precisely what we want to do here */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
return( - ( ( value | - value ) >> ( sizeof( value ) * 8 - 1 ) ) );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
}
/** Check whether a size is out of bounds, without branches.
*
* This is equivalent to `size > max`, but is likely to be compiled to
* to code using bitwise operation rather than a branch.
*
* \param size Size to check.
* \param max Maximum desired value for \p size.
* \return \c 0 if `size <= max`.
* \return \c 1 if `size > max`.
*/
static unsigned size_greater_than( size_t size, size_t max )
{
/* Return the sign bit (1 for negative) of (max - size). */
return( ( max - size ) >> ( sizeof( size_t ) * 8 - 1 ) );
}
/** Choose between two integer values, without branches.
*
* This is equivalent to `cond ? if1 : if0`, but is likely to be compiled
* to code using bitwise operation rather than a branch.
*
* \param cond Condition to test.
* \param if1 Value to use if \p cond is nonzero.
* \param if0 Value to use if \p cond is zero.
* \return \c if1 if \p cond is nonzero, otherwise \c if0.
*/
static unsigned if_int( unsigned cond, unsigned if1, unsigned if0 )
{
unsigned mask = all_or_nothing_int( cond );
return( ( mask & if1 ) | (~mask & if0 ) );
}
/** Shift some data towards the left inside a buffer without leaking
* the length of the data through side channels.
*
* `mem_move_to_left(start, total, offset)` is functionally equivalent to
* ```
* memmove(start, start + offset, total - offset);
* memset(start + offset, 0, total - offset);
* ```
* but it strives to use a memory access pattern (and thus total timing)
* that does not depend on \p offset. This timing independence comes at
* the expense of performance.
*
* \param start Pointer to the start of the buffer.
* \param total Total size of the buffer.
* \param offset Offset from which to copy \p total - \p offset bytes.
*/
static void mem_move_to_left( void *start,
size_t total,
size_t offset )
{
volatile unsigned char *buf = start;
size_t i, n;
if( total == 0 )
return;
for( i = 0; i < total; i++ )
{
unsigned no_op = size_greater_than( total - offset, i );
/* The first `total - offset` passes are a no-op. The last
* `offset` passes shift the data one byte to the left and
* zero out the last byte. */
for( n = 0; n < total - 1; n++ )
{
unsigned char current = buf[n];
unsigned char next = buf[n+1];
buf[n] = if_int( no_op, current, next );
}
buf[total-1] = if_int( no_op, buf[total-1], 0 );
}
}
/*
* Implementation of the PKCS#1 v2.1 RSAES-PKCS1-V1_5-DECRYPT function
*/
int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
int (*f_rng)(void *, unsigned char *, size_t),
void *p_rng,
int mode, size_t *olen,
int mode,
size_t *olen,
const unsigned char *input,
unsigned char *output,
size_t output_max_len )
{
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
size_t ilen, i, plaintext_max_size;
size_t ilen;
unsigned char buf[MBEDTLS_MPI_MAX_SIZE];
/* The following variables take sensitive values: their value must
* not leak into the observable behavior of the function other than
* the designated outputs (output, olen, return value). Otherwise
* this would open the execution of the function to
* side-channel-based variants of the Bleichenbacher padding oracle
* attack. Potential side channels include overall timing, memory
* access patterns (especially visible to an adversary who has access
* to a shared memory cache), and branches (especially visible to
* an adversary who has access to a shared code cache or to a shared
* branch predictor). */
size_t pad_count = 0;
unsigned bad = 0;
unsigned char pad_done = 0;
size_t plaintext_size = 0;
unsigned output_too_large;
RSA_VALIDATE_RET( ctx != NULL );
RSA_VALIDATE_RET( mode == MBEDTLS_RSA_PRIVATE ||
@ -1623,9 +1504,6 @@ int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
RSA_VALIDATE_RET( olen != NULL );
ilen = ctx->len;
plaintext_max_size = ( output_max_len > ilen - 11 ?
ilen - 11 :
output_max_len );
if( mode == MBEDTLS_RSA_PRIVATE && ctx->padding != MBEDTLS_RSA_PKCS_V15 )
return( MBEDTLS_ERR_RSA_BAD_INPUT_DATA );
@ -1640,115 +1518,8 @@ int mbedtls_rsa_rsaes_pkcs1_v15_decrypt( mbedtls_rsa_context *ctx,
if( ret != 0 )
goto cleanup;
/* Check and get padding length in constant time and constant
* memory trace. The first byte must be 0. */
bad |= buf[0];
if( mode == MBEDTLS_RSA_PRIVATE )
{
/* Decode EME-PKCS1-v1_5 padding: 0x00 || 0x02 || PS || 0x00
* where PS must be at least 8 nonzero bytes. */
bad |= buf[1] ^ MBEDTLS_RSA_CRYPT;
/* Read the whole buffer. Set pad_done to nonzero if we find
* the 0x00 byte and remember the padding length in pad_count. */
for( i = 2; i < ilen; i++ )
{
pad_done |= ((buf[i] | (unsigned char)-buf[i]) >> 7) ^ 1;
pad_count += ((pad_done | (unsigned char)-pad_done) >> 7) ^ 1;
}
}
else
{
/* Decode EMSA-PKCS1-v1_5 padding: 0x00 || 0x01 || PS || 0x00
* where PS must be at least 8 bytes with the value 0xFF. */
bad |= buf[1] ^ MBEDTLS_RSA_SIGN;
/* Read the whole buffer. Set pad_done to nonzero if we find
* the 0x00 byte and remember the padding length in pad_count.
* If there's a non-0xff byte in the padding, the padding is bad. */
for( i = 2; i < ilen; i++ )
{
pad_done |= if_int( buf[i], 0, 1 );
pad_count += if_int( pad_done, 0, 1 );
bad |= if_int( pad_done, 0, buf[i] ^ 0xFF );
}
}
/* If pad_done is still zero, there's no data, only unfinished padding. */
bad |= if_int( pad_done, 0, 1 );
/* There must be at least 8 bytes of padding. */
bad |= size_greater_than( 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 = if_int( 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 = size_greater_than( 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) if_int( bad, - MBEDTLS_ERR_RSA_INVALID_PADDING,
if_int( output_too_large, - MBEDTLS_ERR_RSA_OUTPUT_TOO_LARGE,
0 ) );
/* If the padding is bad or the plaintext is too large, zero the
* data that we're about to copy to the output buffer.
* We need to copy the same amount of data
* from the same buffer whether the padding is good or not to
* avoid leaking the padding validity through overall timing or
* through memory or cache access patterns. */
bad = all_or_nothing_int( bad | output_too_large );
for( i = 11; i < ilen; i++ )
buf[i] &= ~bad;
/* If the plaintext is too large, truncate it to the buffer size.
* Copy anyway to avoid revealing the length through timing, because
* revealing the length is as bad as revealing the padding validity
* for a Bleichenbacher attack. */
plaintext_size = if_int( 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. */
mem_move_to_left( buf + ilen - plaintext_max_size,
plaintext_max_size,
plaintext_max_size - plaintext_size );
/* Finally copy the decrypted plaintext plus trailing zeros into the output
* buffer. If output_max_len is 0, then output may be an invalid pointer
* and the result of memcpy() would be undefined; prevent undefined
* behavior making sure to depend only on output_max_len (the size of the
* user-provided output buffer), which is independent from plaintext
* length, validity of padding, success of the decryption, and other
* secrets. */
if( output_max_len != 0 )
memcpy( output, buf + ilen - plaintext_max_size, plaintext_max_size );
/* Report the amount of data we copied to the output buffer. In case
* of errors (bad padding or output too large), the value of *olen
* when this function returns is not specified. Making it equivalent
* to the good case limits the risks of leaking the padding validity. */
*olen = plaintext_size;
ret = mbedtls_ct_rsaes_pkcs1_v15_unpadding( mode, buf, ilen,
output, output_max_len, olen );
cleanup:
mbedtls_platform_zeroize( buf, sizeof( buf ) );
@ -2162,7 +1933,7 @@ int mbedtls_rsa_rsassa_pkcs1_v15_sign( mbedtls_rsa_context *ctx,
MBEDTLS_MPI_CHK( mbedtls_rsa_private( ctx, f_rng, p_rng, sig, sig_try ) );
MBEDTLS_MPI_CHK( mbedtls_rsa_public( ctx, sig_try, verif ) );
if( mbedtls_safer_memcmp( verif, sig, ctx->len ) != 0 )
if( mbedtls_ct_memcmp( verif, sig, ctx->len ) != 0 )
{
ret = MBEDTLS_ERR_RSA_PRIVATE_FAILED;
goto cleanup;
@ -2460,8 +2231,8 @@ int mbedtls_rsa_rsassa_pkcs1_v15_verify( mbedtls_rsa_context *ctx,
* Compare
*/
if( ( ret = mbedtls_safer_memcmp( encoded, encoded_expected,
sig_len ) ) != 0 )
if( ( ret = mbedtls_ct_memcmp( encoded, encoded_expected,
sig_len ) ) != 0 )
{
ret = MBEDTLS_ERR_RSA_VERIFY_FAILED;
goto cleanup;

View file

@ -33,6 +33,7 @@
#include "mbedtls/ssl_internal.h"
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
#include "mbedtls/constant_time.h"
#if defined(MBEDTLS_USE_PSA_CRYPTO)
#include "mbedtls/psa_util.h"
@ -1458,9 +1459,9 @@ static int ssl_parse_renegotiation_info( mbedtls_ssl_context *ssl,
/* Check verify-data in constant-time. The length OTOH is no secret */
if( len != 1 + ssl->verify_data_len * 2 ||
buf[0] != ssl->verify_data_len * 2 ||
mbedtls_ssl_safer_memcmp( buf + 1,
mbedtls_ct_memcmp( buf + 1,
ssl->own_verify_data, ssl->verify_data_len ) != 0 ||
mbedtls_ssl_safer_memcmp( buf + 1 + ssl->verify_data_len,
mbedtls_ct_memcmp( buf + 1 + ssl->verify_data_len,
ssl->peer_verify_data, ssl->verify_data_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "non-matching renegotiation info" ) );

View file

@ -36,6 +36,7 @@
#include "mbedtls/ssl_internal.h"
#include "mbedtls/error.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/constant_time.h"
#include <string.h>
@ -224,7 +225,7 @@ int mbedtls_ssl_cookie_check( void *p_ctx,
if( ret != 0 )
return( ret );
if( mbedtls_ssl_safer_memcmp( cookie + 4, ref_hmac, sizeof( ref_hmac ) ) != 0 )
if( mbedtls_ct_memcmp( cookie + 4, ref_hmac, sizeof( ref_hmac ) ) != 0 )
return( -1 );
#if defined(MBEDTLS_HAVE_TIME)

View file

@ -1,100 +0,0 @@
/**
* \file ssl_invasive.h
*
* \brief SSL 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_SSL_INVASIVE_H
#define MBEDTLS_SSL_INVASIVE_H
#include "common.h"
#include "mbedtls/md.h"
#if defined(MBEDTLS_TEST_HOOKS) && \
defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/** \brief 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 additional data prepended to \p data. 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 data appended to \p add_data. 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 \p data in bytes.
* \param max_data_len The maximal length of \p data in bytes.
* \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
* Success.
* \retval MBEDTLS_ERR_PLATFORM_HW_ACCEL_FAILED
* The hardware accelerator failed.
*/
int mbedtls_ssl_cf_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 );
/** \brief Copy data from a secret position with constant flow.
*
* 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.
*
* \param dst The destination buffer. This must point to a writable
* buffer of at least \p len bytes.
* \param src_base The base of the source buffer. This must point to a
* readable buffer of at least \p offset_max + \p len
* bytes.
* \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_secret.
* \param offset_max The maximal value of \p offset_secret.
* \param len The number of bytes to copy.
*/
void mbedtls_ssl_cf_memcpy_offset( unsigned char *dst,
const unsigned char *src_base,
size_t offset_secret,
size_t offset_min, size_t offset_max,
size_t len );
#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
#endif /* MBEDTLS_SSL_INVASIVE_H */

View file

@ -44,8 +44,8 @@
#include "mbedtls/error.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/version.h"
#include "ssl_invasive.h"
#include "constant_time_internal.h"
#include "mbedtls/constant_time.h"
#include <string.h>
@ -1043,242 +1043,6 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
return( 0 );
}
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
/*
* Turn a bit into a mask:
* - if bit == 1, return the all-bits 1 mask, aka (size_t) -1
* - if bit == 0, return the all-bits 0 mask, aka 0
*
* This function can be used to write constant-time code by replacing branches
* with bit operations using masks.
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static size_t mbedtls_ssl_cf_mask_from_bit( size_t bit )
{
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
return -bit;
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
}
/*
* Constant-flow mask generation for "less than" comparison:
* - if x < 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.
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static size_t mbedtls_ssl_cf_mask_lt( size_t x, size_t y )
{
/* This has the most significant bit set if and only if x < y */
const size_t sub = x - y;
/* sub1 = (x < y) ? 1 : 0 */
const size_t sub1 = sub >> ( sizeof( sub ) * 8 - 1 );
/* mask = (x < y) ? 0xff... : 0x00... */
const size_t mask = mbedtls_ssl_cf_mask_from_bit( sub1 );
return( mask );
}
/*
* Constant-flow mask generation for "greater or equal" comparison:
* - if x >= 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.
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static size_t mbedtls_ssl_cf_mask_ge( size_t x, size_t y )
{
return( ~mbedtls_ssl_cf_mask_lt( x, y ) );
}
/*
* Constant-flow boolean "equal" comparison:
* return x == y
*
* This function can be used to write constant-time code by replacing branches
* with bit operations - it can be used in conjunction with
* mbedtls_ssl_cf_mask_from_bit().
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static size_t mbedtls_ssl_cf_bool_eq( size_t x, size_t y )
{
/* diff = 0 if x == y, non-zero otherwise */
const size_t diff = x ^ y;
/* MSVC has a warning about unary minus on unsigned integer types,
* but this is well-defined and precisely what we want to do here. */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
/* diff_msb's most significant bit is equal to x != y */
const size_t diff_msb = ( diff | -diff );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
/* diff1 = (x != y) ? 1 : 0 */
const size_t diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
return( 1 ^ diff1 );
}
/*
* Constant-flow conditional memcpy:
* - if c1 == c2, equivalent to memcpy(dst, src, len),
* - otherwise, a no-op,
* but with execution flow independent of the values of c1 and c2.
*
* This function is implemented without using comparison operators, as those
* might be translated to branches by some compilers on some platforms.
*/
static void mbedtls_ssl_cf_memcpy_if_eq( unsigned char *dst,
const unsigned char *src,
size_t len,
size_t c1, size_t c2 )
{
/* mask = c1 == c2 ? 0xff : 0x00 */
const size_t equal = mbedtls_ssl_cf_bool_eq( c1, c2 );
const unsigned char mask = (unsigned char) mbedtls_ssl_cf_mask_from_bit( equal );
/* dst[i] = c1 == c2 ? src[i] : dst[i] */
for( size_t i = 0; i < len; i++ )
dst[i] = ( src[i] & mask ) | ( dst[i] & ~mask );
}
/*
* Compute HMAC of variable-length data with constant flow.
*
* Only works with MD-5, SHA-1, SHA-256 and SHA-384.
* (Otherwise, computation of block_size needs to be adapted.)
*/
MBEDTLS_STATIC_TESTABLE int mbedtls_ssl_cf_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.0-1.2 only support 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 ) );
/* 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_ssl_cf_memcpy_if_eq( output, aux_out, hash_size,
offset, data_len_secret );
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 );
}
/*
* Constant-flow memcpy from variable position in buffer.
* - functionally equivalent to memcpy(dst, src + offset_secret, len)
* - but with execution flow independent from the value of offset_secret.
*/
MBEDTLS_STATIC_TESTABLE void mbedtls_ssl_cf_memcpy_offset(
unsigned char *dst,
const unsigned char *src_base,
size_t offset_secret,
size_t offset_min, size_t offset_max,
size_t len )
{
size_t offset;
for( offset = offset_min; offset <= offset_max; offset++ )
{
mbedtls_ssl_cf_memcpy_if_eq( dst, src_base + offset, len,
offset, offset_secret );
}
}
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
mbedtls_ssl_transform *transform,
mbedtls_record *rec )
@ -1518,7 +1282,7 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
*
* Afterwards, we know that data + data_len is followed by at
* least maclen Bytes, which justifies the call to
* mbedtls_ssl_safer_memcmp() below.
* mbedtls_ct_memcmp() below.
*
* Further, we still know that data_len > minlen */
rec->data_len -= transform->maclen;
@ -1541,8 +1305,8 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
transform->maclen );
/* Compare expected MAC with MAC at the end of the record. */
if( mbedtls_ssl_safer_memcmp( data + rec->data_len, mac_expect,
transform->maclen ) != 0 )
if( mbedtls_ct_memcmp( data + rec->data_len, mac_expect,
transform->maclen ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "message mac does not match" ) );
return( MBEDTLS_ERR_SSL_INVALID_MAC );
@ -1620,7 +1384,7 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
if( auth_done == 1 )
{
const size_t mask = mbedtls_ssl_cf_mask_ge(
const size_t mask = mbedtls_ct_size_mask_ge(
rec->data_len,
padlen + 1 );
correct &= mask;
@ -1640,7 +1404,7 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
}
#endif
const size_t mask = mbedtls_ssl_cf_mask_ge(
const size_t mask = mbedtls_ct_size_mask_ge(
rec->data_len,
transform->maclen + padlen + 1 );
correct &= mask;
@ -1696,18 +1460,18 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
/* pad_count += (idx >= padding_idx) &&
* (check[idx] == padlen - 1);
*/
const size_t mask = mbedtls_ssl_cf_mask_ge( idx, padding_idx );
const size_t equal = mbedtls_ssl_cf_bool_eq( 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;
}
correct &= mbedtls_ssl_cf_bool_eq( pad_count, padlen );
correct &= mbedtls_ct_size_bool_eq( pad_count, padlen );
#if defined(MBEDTLS_SSL_DEBUG_ALL)
if( padlen > 0 && correct == 0 )
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad padding byte detected" ) );
#endif
padlen &= mbedtls_ssl_cf_mask_from_bit( correct );
padlen &= mbedtls_ct_size_mask( correct );
}
else
#endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \
@ -1791,20 +1555,20 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
const size_t max_len = rec->data_len + padlen;
const size_t min_len = ( max_len > 256 ) ? max_len - 256 : 0;
ret = mbedtls_ssl_cf_hmac( &transform->md_ctx_dec,
add_data, add_data_len,
data, rec->data_len, min_len, max_len,
mac_expect );
ret = mbedtls_ct_hmac( &transform->md_ctx_dec,
add_data, add_data_len,
data, rec->data_len, min_len, max_len,
mac_expect );
if( ret != 0 )
{
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_cf_hmac", ret );
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ct_hmac", ret );
return( ret );
}
mbedtls_ssl_cf_memcpy_offset( mac_peer, data,
rec->data_len,
min_len, max_len,
transform->maclen );
mbedtls_ct_memcpy_offset( mac_peer, data,
rec->data_len,
min_len, max_len,
transform->maclen );
}
else
#endif /* MBEDTLS_SSL_PROTO_TLS1 || MBEDTLS_SSL_PROTO_TLS1_1 || \
@ -1819,8 +1583,8 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
MBEDTLS_SSL_DEBUG_BUF( 4, "message mac", mac_peer, transform->maclen );
#endif
if( mbedtls_ssl_safer_memcmp( mac_peer, mac_expect,
transform->maclen ) != 0 )
if( mbedtls_ct_memcmp( mac_peer, mac_expect,
transform->maclen ) != 0 )
{
#if defined(MBEDTLS_SSL_DEBUG_ALL)
MBEDTLS_SSL_DEBUG_MSG( 1, ( "message mac does not match" ) );

View file

@ -34,6 +34,8 @@
#include "mbedtls/debug.h"
#include "mbedtls/error.h"
#include "mbedtls/platform_util.h"
#include "constant_time_internal.h"
#include "mbedtls/constant_time.h"
#include <string.h>
@ -196,7 +198,7 @@ static int ssl_parse_renegotiation_info( mbedtls_ssl_context *ssl,
/* Check verify-data in constant-time. The length OTOH is no secret */
if( len != 1 + ssl->verify_data_len ||
buf[0] != ssl->verify_data_len ||
mbedtls_ssl_safer_memcmp( buf + 1, ssl->peer_verify_data,
mbedtls_ct_memcmp( buf + 1, ssl->peer_verify_data,
ssl->verify_data_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "non-matching renegotiation info" ) );
@ -3971,16 +3973,7 @@ static int ssl_parse_encrypted_pms( mbedtls_ssl_context *ssl,
diff |= peer_pms[1] ^ ver[1];
/* mask = diff ? 0xff : 0x00 using bit operations to avoid branches */
/* MSVC has a warning about unary minus on unsigned, but this is
* well-defined and precisely what we want to do here */
#if defined(_MSC_VER)
#pragma warning( push )
#pragma warning( disable : 4146 )
#endif
mask = - ( ( diff | - diff ) >> ( sizeof( unsigned int ) * 8 - 1 ) );
#if defined(_MSC_VER)
#pragma warning( pop )
#endif
mask = mbedtls_ct_uint_mask( diff );
/*
* Protection against Bleichenbacher's attack: invalid PKCS#1 v1.5 padding
@ -4063,7 +4056,7 @@ static int ssl_parse_client_psk_identity( mbedtls_ssl_context *ssl, unsigned cha
/* Identity is not a big secret since clients send it in the clear,
* but treat it carefully anyway, just in case */
if( n != ssl->conf->psk_identity_len ||
mbedtls_ssl_safer_memcmp( ssl->conf->psk_identity, *p, n ) != 0 )
mbedtls_ct_memcmp( ssl->conf->psk_identity, *p, n ) != 0 )
{
ret = MBEDTLS_ERR_SSL_UNKNOWN_IDENTITY;
}

View file

@ -43,6 +43,7 @@
#include "mbedtls/error.h"
#include "mbedtls/platform_util.h"
#include "mbedtls/version.h"
#include "mbedtls/constant_time.h"
#include <string.h>
@ -3603,7 +3604,7 @@ int mbedtls_ssl_parse_finished( mbedtls_ssl_context *ssl )
return( MBEDTLS_ERR_SSL_BAD_HS_FINISHED );
}
if( mbedtls_ssl_safer_memcmp( ssl->in_msg + mbedtls_ssl_hs_hdr_len( ssl ),
if( mbedtls_ct_memcmp( ssl->in_msg + mbedtls_ssl_hs_hdr_len( ssl ),
buf, hash_len ) != 0 )
{
MBEDTLS_SSL_DEBUG_MSG( 1, ( "bad finished message" ) );

View file

@ -8,7 +8,7 @@
#include <mbedtls/debug.h>
#include <ssl_tls13_keys.h>
#include <ssl_invasive.h>
#include <constant_time_internal.h>
#include <test/constant_flow.h>
@ -4428,7 +4428,7 @@ void resize_buffers_renegotiate_mfl( int mfl, int legacy_renegotiation,
void ssl_cf_hmac( int hash )
{
/*
* Test the function mbedtls_ssl_cf_hmac() against a reference
* Test the function mbedtls_ct_hmac() against a reference
* implementation.
*/
mbedtls_md_context_t ctx, ref_ctx;
@ -4487,10 +4487,10 @@ void ssl_cf_hmac( int hash )
/* Get the function's result */
TEST_CF_SECRET( &in_len, sizeof( in_len ) );
TEST_EQUAL( 0, mbedtls_ssl_cf_hmac( &ctx, add_data, sizeof( add_data ),
data, in_len,
min_in_len, max_in_len,
out ) );
TEST_EQUAL( 0, mbedtls_ct_hmac( &ctx, add_data, sizeof( add_data ),
data, in_len,
min_in_len, max_in_len,
out ) );
TEST_CF_PUBLIC( &in_len, sizeof( in_len ) );
TEST_CF_PUBLIC( out, out_len );
@ -4537,8 +4537,8 @@ 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 ) );
mbedtls_ssl_cf_memcpy_offset( dst, src, secret,
offset_min, offset_max, len );
mbedtls_ct_memcpy_offset( dst, src, secret,
offset_min, offset_max, len );
TEST_CF_PUBLIC( &secret, sizeof( secret ) );
TEST_CF_PUBLIC( dst, len );

View file

@ -163,6 +163,7 @@
<ClInclude Include="..\..\include\mbedtls\compat-1.3.h" />
<ClInclude Include="..\..\include\mbedtls\config.h" />
<ClInclude Include="..\..\include\mbedtls\config_psa.h" />
<ClInclude Include="..\..\include\mbedtls\constant_time.h" />
<ClInclude Include="..\..\include\mbedtls\ctr_drbg.h" />
<ClInclude Include="..\..\include\mbedtls\debug.h" />
<ClInclude Include="..\..\include\mbedtls\des.h" />
@ -256,6 +257,7 @@
<ClInclude Include="..\..\library\base64_invasive.h" />
<ClInclude Include="..\..\library\check_crypto_config.h" />
<ClInclude Include="..\..\library\common.h" />
<ClInclude Include="..\..\library\constant_time_internal.h" />
<ClInclude Include="..\..\library\ecp_invasive.h" />
<ClInclude Include="..\..\library\mps_common.h" />
<ClInclude Include="..\..\library\mps_error.h" />
@ -275,7 +277,6 @@
<ClInclude Include="..\..\library\psa_crypto_se.h" />
<ClInclude Include="..\..\library\psa_crypto_slot_management.h" />
<ClInclude Include="..\..\library\psa_crypto_storage.h" />
<ClInclude Include="..\..\library\ssl_invasive.h" />
<ClInclude Include="..\..\library\ssl_tls13_keys.h" />
<ClInclude Include="..\..\3rdparty\everest\include\everest\everest.h" />
<ClInclude Include="..\..\3rdparty\everest\include\everest\Hacl_Curve25519.h" />
@ -300,6 +301,7 @@
<ClCompile Include="..\..\library\cipher.c" />
<ClCompile Include="..\..\library\cipher_wrap.c" />
<ClCompile Include="..\..\library\cmac.c" />
<ClCompile Include="..\..\library\constant_time.c" />
<ClCompile Include="..\..\library\ctr_drbg.c" />
<ClCompile Include="..\..\library\debug.c" />
<ClCompile Include="..\..\library\des.c" />