diff --git a/library/base64.c b/library/base64.c index a516c1d4a3..83daa0bcc6 100644 --- a/library/base64.c +++ b/library/base64.c @@ -22,7 +22,7 @@ #if defined(MBEDTLS_BASE64_C) #include "mbedtls/base64.h" -#include "base64_invasive.h" +#include "constant_time_internal.h" #include @@ -38,41 +38,6 @@ #define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */ -/* Return 0xff if low <= c <= high, 0 otherwise. - * - * Constant flow with respect to c. - */ -MBEDTLS_STATIC_TESTABLE -unsigned char mbedtls_base64_mask_of_range( unsigned char low, - unsigned char high, - unsigned char c ) -{ - /* low_mask is: 0 if low <= c, 0x...ff if low > c */ - unsigned low_mask = ( (unsigned) c - low ) >> 8; - /* high_mask is: 0 if c <= high, 0x...ff if c > high */ - unsigned high_mask = ( (unsigned) high - c ) >> 8; - return( ~( low_mask | high_mask ) & 0xff ); -} - -/* 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). - */ -MBEDTLS_STATIC_TESTABLE -unsigned char mbedtls_base64_enc_char( unsigned char val ) -{ - unsigned char digit = 0; - /* For each range of values, if val is in that range, mask digit with - * the corresponding value. Since val can only be in a single range, - * only at most one masking will change digit. */ - digit |= mbedtls_base64_mask_of_range( 0, 25, val ) & ( 'A' + val ); - digit |= mbedtls_base64_mask_of_range( 26, 51, val ) & ( 'a' + val - 26 ); - digit |= mbedtls_base64_mask_of_range( 52, 61, val ) & ( '0' + val - 52 ); - digit |= mbedtls_base64_mask_of_range( 62, 62, val ) & '+'; - digit |= mbedtls_base64_mask_of_range( 63, 63, val ) & '/'; - return( digit ); -} - /* * Encode a buffer into base64 format */ @@ -113,12 +78,12 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen, C2 = *src++; C3 = *src++; - *p++ = mbedtls_base64_enc_char( ( C1 >> 2 ) & 0x3F ); - *p++ = mbedtls_base64_enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) + *p++ = mbedtls_ct_base64_enc_char( ( C1 >> 2 ) & 0x3F ); + *p++ = mbedtls_ct_base64_enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ); - *p++ = mbedtls_base64_enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) + *p++ = mbedtls_ct_base64_enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F ); - *p++ = mbedtls_base64_enc_char( C3 & 0x3F ); + *p++ = mbedtls_ct_base64_enc_char( C3 & 0x3F ); } if( i < slen ) @@ -126,12 +91,12 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen, C1 = *src++; C2 = ( ( i + 1 ) < slen ) ? *src++ : 0; - *p++ = mbedtls_base64_enc_char( ( C1 >> 2 ) & 0x3F ); - *p++ = mbedtls_base64_enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) + *p++ = mbedtls_ct_base64_enc_char( ( C1 >> 2 ) & 0x3F ); + *p++ = mbedtls_ct_base64_enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ); if( ( i + 1 ) < slen ) - *p++ = mbedtls_base64_enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F ); + *p++ = mbedtls_ct_base64_enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F ); else *p++ = '='; *p++ = '='; @@ -143,35 +108,6 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen, return( 0 ); } -/* 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). - * - * The implementation is constant-flow (no branch or memory access depending - * on the value of c) unless the compiler inlines and optimizes a specific - * access. - */ -MBEDTLS_STATIC_TESTABLE -signed char mbedtls_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_base64_mask_of_range( 'A', 'Z', c ) & ( c - 'A' + 0 + 1 ); - val |= mbedtls_base64_mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 ); - val |= mbedtls_base64_mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 ); - val |= mbedtls_base64_mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 ); - val |= mbedtls_base64_mask_of_range( '/', '/', 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 ); -} - /* * Decode a base64-formatted buffer */ @@ -224,7 +160,7 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, { if( equals != 0 ) return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); - if( mbedtls_base64_dec_value( src[i] ) < 0 ) + if( mbedtls_ct_base64_dec_value( src[i] ) < 0 ) return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); } n++; @@ -259,7 +195,7 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, if( *src == '=' ) ++equals; else - x |= mbedtls_base64_dec_value( *src ); + x |= mbedtls_ct_base64_dec_value( *src ); if( ++accumulated_digits == 4 ) { diff --git a/library/base64_invasive.h b/library/base64_invasive.h deleted file mode 100644 index ed5f7cb824..0000000000 --- a/library/base64_invasive.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * \file base_invasive.h - * - * \brief Base64 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_BASE64_INVASIVE_H -#define MBEDTLS_BASE64_INVASIVE_H - -#include "common.h" - -#if defined(MBEDTLS_TEST_HOOKS) -/* Return 0xff if low <= c <= high, 0 otherwise. - * - * Constant flow with respect to c. - */ -unsigned char mbedtls_base64_mask_of_range( unsigned char low, - unsigned char high, - unsigned char c ); - -/* Given a value in the range 0..63, return the corresponding Base64 digit. - * - * Operates in constant time (no branches or memory access depending on val). - */ -unsigned char mbedtls_base64_enc_char( unsigned char val ); - -/* Given a Base64 digit, return its value. - * If c is not a Base64 digit ('A'..'Z', 'a'..'z', '0'..'9', '+' or '/'), - * return -1. - * - * Operates in constant time (no branches or memory access depending on c). - */ -signed char mbedtls_base64_dec_value( unsigned char c ); -#endif /* MBEDTLS_TEST_HOOKS */ - -#endif /* MBEDTLS_BASE64_INVASIVE_H */ diff --git a/library/constant_time.c b/library/constant_time.c index 9bb275cf5c..d8870ae7f3 100644 --- a/library/constant_time.c +++ b/library/constant_time.c @@ -40,6 +40,10 @@ #include "mbedtls/rsa.h" #endif +#if defined(MBEDTLS_BASE64_C) +#include "constant_time_invasive.h" +#endif + #include int mbedtls_ct_memcmp( const void *a, @@ -150,6 +154,26 @@ size_t mbedtls_ct_size_mask_ge( size_t x, #endif /* MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */ +#if defined(MBEDTLS_BASE64_C) + +/* Return 0xff if low <= c <= high, 0 otherwise. + * + * Constant flow with respect to c. + */ +MBEDTLS_STATIC_TESTABLE +unsigned char mbedtls_ct_uchar_mask_of_range( unsigned char low, + unsigned char high, + unsigned char c ) +{ + /* low_mask is: 0 if low <= c, 0x...ff if low > c */ + unsigned low_mask = ( (unsigned) c - low ) >> 8; + /* high_mask is: 0 if c <= high, 0x...ff if c > high */ + unsigned high_mask = ( (unsigned) high - c ) >> 8; + return( ~( low_mask | high_mask ) & 0xff ); +} + +#endif /* MBEDTLS_BASE64_C */ + unsigned mbedtls_ct_size_bool_eq( size_t x, size_t y ) { @@ -301,6 +325,41 @@ void mbedtls_ct_mpi_uint_cond_assign( size_t n, #endif /* MBEDTLS_BIGNUM_C */ +#if defined(MBEDTLS_BASE64_C) + +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_mask_of_range( 0, 25, value ) & ( 'A' + value ); + digit |= mbedtls_ct_uchar_mask_of_range( 26, 51, value ) & ( 'a' + value - 26 ); + digit |= mbedtls_ct_uchar_mask_of_range( 52, 61, value ) & ( '0' + value - 52 ); + digit |= mbedtls_ct_uchar_mask_of_range( 62, 62, value ) & '+'; + digit |= mbedtls_ct_uchar_mask_of_range( 63, 63, value ) & '/'; + return( digit ); +} + +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_mask_of_range( 'A', 'Z', c ) & ( c - 'A' + 0 + 1 ); + val |= mbedtls_ct_uchar_mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 ); + val |= mbedtls_ct_uchar_mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 ); + val |= mbedtls_ct_uchar_mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 ); + val |= mbedtls_ct_uchar_mask_of_range( '/', '/', 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 ); +} + +#endif /* MBEDTLS_BASE64_C */ + #if defined(MBEDTLS_PKCS1_V15) && defined(MBEDTLS_RSA_C) && !defined(MBEDTLS_RSA_ALT) /** Shift some data towards the left inside a buffer. diff --git a/library/constant_time_internal.h b/library/constant_time_internal.h index ac18b56a9a..053cf123cc 100644 --- a/library/constant_time_internal.h +++ b/library/constant_time_internal.h @@ -167,6 +167,35 @@ void mbedtls_ct_mpi_uint_cond_assign( size_t n, #endif /* MBEDTLS_BIGNUM_C */ +#if defined(MBEDTLS_BASE64_C) + +/** 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_BASE64_C */ + #if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC) /** Conditional memcpy without branches. diff --git a/library/constant_time_invasive.h b/library/constant_time_invasive.h new file mode 100644 index 0000000000..4620ca1379 --- /dev/null +++ b/library/constant_time_invasive.h @@ -0,0 +1,51 @@ +/** + * \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 */ diff --git a/tests/suites/test_suite_base64.function b/tests/suites/test_suite_base64.function index 67fbb67505..7baa3d501c 100644 --- a/tests/suites/test_suite_base64.function +++ b/tests/suites/test_suite_base64.function @@ -1,6 +1,7 @@ /* BEGIN_HEADER */ #include "mbedtls/base64.h" -#include "base64_invasive.h" +#include "constant_time_internal.h" +#include "constant_time_invasive.h" #include #if defined(MBEDTLS_TEST_HOOKS) @@ -24,7 +25,7 @@ void mask_of_range( int low_arg, int high_arg ) { mbedtls_test_set_step( c ); TEST_CF_SECRET( &c, sizeof( c ) ); - unsigned char m = mbedtls_base64_mask_of_range( low, high, c ); + unsigned char m = mbedtls_ct_uchar_mask_of_range( low, high, c ); TEST_CF_PUBLIC( &c, sizeof( c ) ); TEST_CF_PUBLIC( &m, sizeof( m ) ); if( low <= c && c <= high ) @@ -42,7 +43,7 @@ void enc_chars( ) { mbedtls_test_set_step( value ); TEST_CF_SECRET( &value, sizeof( value ) ); - unsigned char digit = mbedtls_base64_enc_char( value ); + unsigned char digit = mbedtls_ct_base64_enc_char( value ); TEST_CF_PUBLIC( &value, sizeof( value ) ); TEST_CF_PUBLIC( &digit, sizeof( digit ) ); TEST_EQUAL( digit, base64_digits[value] ); @@ -66,7 +67,7 @@ void dec_chars( ) else expected = p - base64_digits; TEST_CF_SECRET( &c, sizeof( c ) ); - signed char actual = mbedtls_base64_dec_value( c ); + signed char actual = mbedtls_ct_base64_dec_value( c ); TEST_CF_PUBLIC( &c, sizeof( c ) ); TEST_CF_PUBLIC( &actual, sizeof( actual ) ); TEST_EQUAL( actual, expected );