-
Notifications
You must be signed in to change notification settings - Fork 2.6k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Backport 2.16: range-based constant-flow base64 #4819
Merged
mpg
merged 15 commits into
Mbed-TLS:mbedtls-2.16
from
gilles-peskine-arm:base64-no-table-2.16
Oct 27, 2021
Merged
Changes from 13 commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7023d6a
New sample program to benchmark certificate loading
gilles-peskine-arm a47fdcf
Base64 decoding: don't use the table for '='
gilles-peskine-arm f4a0a27
Base64 decoding: use ranges instead of tables
gilles-peskine-arm ea96b3a
Base64 decode: simplify local variables
gilles-peskine-arm b44517e
Base64 encoding: use ranges instead of tables
gilles-peskine-arm 32e9530
Base64 range-based constant-flow code: changelog entry
gilles-peskine-arm 231b67a
Base64 decode: simplify local variables (n)
gilles-peskine-arm bbf97cd
mask_of_range: simplify high comparison
gilles-peskine-arm 5bc763d
load_roots: arguments must be files
gilles-peskine-arm 051b1da
load_roots: fix no-argument detection
gilles-peskine-arm 9f6ab29
load_roots: properly error out on an invalid option
gilles-peskine-arm 0b39970
Fix printf format signedness error
gilles-peskine-arm 29d29c2
Fix the build of sample programs without mbedtls_strerror
gilles-peskine-arm cda1281
Fix copypasta in comment
gilles-peskine-arm 8e82c78
Fix typo in documentation
gilles-peskine-arm File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
Changes | ||
* Improve the performance of base64 constant-flow code. The result is still | ||
slower than the original non-constant-flow implementation, but much faster | ||
than the previous constant-flow implemenation. Fixes #4814. | ||
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
Bugfix | ||
* Fix the build of sample programs when neither MBEDTLS_ERROR_C nor | ||
MBEDTLS_ERROR_STRERROR_DUMMY is enabled. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -66,127 +66,38 @@ | |
#endif /* MBEDTLS_PLATFORM_C */ | ||
#endif /* MBEDTLS_SELF_TEST */ | ||
|
||
static const unsigned char base64_enc_map[64] = | ||
{ | ||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J', | ||
'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R', 'S', 'T', | ||
'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', | ||
'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', | ||
'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', | ||
'y', 'z', '0', '1', '2', '3', '4', '5', '6', '7', | ||
'8', '9', '+', '/' | ||
}; | ||
|
||
static const unsigned char base64_dec_map[128] = | ||
{ | ||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, | ||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, | ||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, | ||
127, 127, 127, 127, 127, 127, 127, 127, 127, 127, | ||
127, 127, 127, 62, 127, 127, 127, 63, 52, 53, | ||
54, 55, 56, 57, 58, 59, 60, 61, 127, 127, | ||
127, 64, 127, 127, 127, 0, 1, 2, 3, 4, | ||
5, 6, 7, 8, 9, 10, 11, 12, 13, 14, | ||
15, 16, 17, 18, 19, 20, 21, 22, 23, 24, | ||
25, 127, 127, 127, 127, 127, 127, 26, 27, 28, | ||
29, 30, 31, 32, 33, 34, 35, 36, 37, 38, | ||
39, 40, 41, 42, 43, 44, 45, 46, 47, 48, | ||
49, 50, 51, 127, 127, 127, 127, 127 | ||
}; | ||
|
||
#define BASE64_SIZE_T_MAX ( (size_t) -1 ) /* SIZE_T_MAX is not standard */ | ||
|
||
/* | ||
* Constant flow conditional assignment to unsigned char | ||
*/ | ||
static void mbedtls_base64_cond_assign_uchar( unsigned char * dest, const unsigned char * const src, | ||
unsigned char condition ) | ||
{ | ||
/* 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 | ||
|
||
/* Generate bitmask from condition, mask will either be 0xFF or 0 */ | ||
unsigned char mask = ( condition | -condition ); | ||
mask >>= 7; | ||
mask = -mask; | ||
|
||
#if defined(_MSC_VER) | ||
#pragma warning( pop ) | ||
#endif | ||
|
||
*dest = ( ( *src ) & mask ) | ( ( *dest ) & ~mask ); | ||
} | ||
|
||
/* | ||
* Constant flow conditional assignment to uint_32 | ||
*/ | ||
static void mbedtls_base64_cond_assign_uint32( uint32_t * dest, const uint32_t src, | ||
uint32_t condition ) | ||
{ | ||
/* 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 | ||
|
||
/* Generate bitmask from condition, mask will either be 0xFFFFFFFF or 0 */ | ||
uint32_t mask = ( condition | -condition ); | ||
mask >>= 31; | ||
mask = -mask; | ||
|
||
#if defined(_MSC_VER) | ||
#pragma warning( pop ) | ||
#endif | ||
|
||
*dest = ( src & mask ) | ( ( *dest ) & ~mask ); | ||
} | ||
|
||
/* | ||
* Constant flow check for equality | ||
/* Return 0xff if low <= c <= high, 0 otherwise. | ||
* | ||
* Constant flow with respect to c. | ||
*/ | ||
static unsigned char mbedtls_base64_eq( size_t in_a, size_t in_b ) | ||
static unsigned char mask_of_range( unsigned char low, unsigned char high, | ||
unsigned char c ) | ||
{ | ||
size_t difference = in_a ^ in_b; | ||
|
||
/* 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 | ||
|
||
difference |= -difference; | ||
|
||
#if defined(_MSC_VER) | ||
#pragma warning( pop ) | ||
#endif | ||
|
||
/* cope with the varying size of size_t per platform */ | ||
difference >>= ( sizeof( difference ) * 8 - 1 ); | ||
|
||
return (unsigned char) ( 1 ^ difference ); | ||
/* 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 high > c */ | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This comment is wrong: |
||
unsigned high_mask = ( (unsigned) high - c ) >> 8; | ||
return( ~( low_mask | high_mask ) & 0xff ); | ||
} | ||
|
||
/* | ||
* Constant flow lookup into table. | ||
/* 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). | ||
*/ | ||
static unsigned char mbedtls_base64_table_lookup( const unsigned char * const table, | ||
const size_t table_size, const size_t table_index ) | ||
static unsigned char enc_char( unsigned char val ) | ||
{ | ||
size_t i; | ||
unsigned char result = 0; | ||
|
||
for( i = 0; i < table_size; ++i ) | ||
{ | ||
mbedtls_base64_cond_assign_uchar( &result, &table[i], mbedtls_base64_eq( i, table_index ) ); | ||
} | ||
|
||
return result; | ||
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 |= mask_of_range( 0, 25, val ) & ( 'A' + val ); | ||
digit |= mask_of_range( 26, 51, val ) & ( 'a' + val - 26 ); | ||
digit |= mask_of_range( 52, 61, val ) & ( '0' + val - 52 ); | ||
digit |= mask_of_range( 62, 62, val ) & '+'; | ||
digit |= mask_of_range( 63, 63, val ) & '/'; | ||
return( digit ); | ||
} | ||
|
||
/* | ||
|
@@ -229,33 +140,22 @@ int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen, | |
C2 = *src++; | ||
C3 = *src++; | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( C1 >> 2 ) & 0x3F ) ); | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) ); | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F ) ); | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( C3 & 0x3F ) ); | ||
*p++ = enc_char( ( C1 >> 2 ) & 0x3F ); | ||
*p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ); | ||
*p++ = enc_char( ( ( ( C2 & 15 ) << 2 ) + ( C3 >> 6 ) ) & 0x3F ); | ||
*p++ = enc_char( C3 & 0x3F ); | ||
} | ||
|
||
if( i < slen ) | ||
{ | ||
C1 = *src++; | ||
C2 = ( ( i + 1 ) < slen ) ? *src++ : 0; | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( C1 >> 2 ) & 0x3F ) ); | ||
|
||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ) ); | ||
*p++ = enc_char( ( C1 >> 2 ) & 0x3F ); | ||
*p++ = enc_char( ( ( ( C1 & 3 ) << 4 ) + ( C2 >> 4 ) ) & 0x3F ); | ||
|
||
if( ( i + 1 ) < slen ) | ||
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ), | ||
( ( ( C2 & 15 ) << 2 ) & 0x3F ) ); | ||
*p++ = enc_char( ( ( C2 & 15 ) << 2 ) & 0x3F ); | ||
else *p++ = '='; | ||
|
||
*p++ = '='; | ||
|
@@ -267,26 +167,57 @@ 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. | ||
*/ | ||
static signed char 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 |= mask_of_range( 'A', 'Z', c ) & ( c - 'A' + 0 + 1 ); | ||
val |= mask_of_range( 'a', 'z', c ) & ( c - 'a' + 26 + 1 ); | ||
val |= mask_of_range( '0', '9', c ) & ( c - '0' + 52 + 1 ); | ||
val |= mask_of_range( '+', '+', c ) & ( c - '+' + 62 + 1 ); | ||
mpg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
val |= 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 | ||
*/ | ||
int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, | ||
const unsigned char *src, size_t slen ) | ||
{ | ||
size_t i, n; | ||
uint32_t j, x; | ||
size_t i; /* index in source */ | ||
size_t n; /* number of digits or trailing = in source */ | ||
mpg marked this conversation as resolved.
Show resolved
Hide resolved
|
||
uint32_t x; /* value accumulator */ | ||
unsigned accumulated_digits = 0; | ||
unsigned equals = 0; | ||
int spaces_present = 0; | ||
unsigned char *p; | ||
unsigned char dec_map_lookup; | ||
|
||
/* First pass: check for validity and get output length */ | ||
for( i = n = j = 0; i < slen; i++ ) | ||
for( i = n = 0; i < slen; i++ ) | ||
{ | ||
/* Skip spaces before checking for EOL */ | ||
x = 0; | ||
spaces_present = 0; | ||
while( i < slen && src[i] == ' ' ) | ||
{ | ||
++i; | ||
++x; | ||
spaces_present = 1; | ||
} | ||
|
||
/* Spaces at end of buffer are OK */ | ||
|
@@ -301,20 +232,24 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, | |
continue; | ||
|
||
/* Space inside a line is an error */ | ||
if( x != 0 ) | ||
if( spaces_present ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
|
||
if( src[i] == '=' && ++j > 2 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
|
||
dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), src[i] ); | ||
|
||
if( src[i] > 127 || dec_map_lookup == 127 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
|
||
if( dec_map_lookup < 64 && j != 0 ) | ||
if( src[i] > 127 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
|
||
if( src[i] == '=' ) | ||
{ | ||
if( ++equals > 2 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
} | ||
else | ||
{ | ||
if( equals != 0 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
if( dec_value( src[i] ) < 0 ) | ||
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER ); | ||
} | ||
n++; | ||
} | ||
|
||
|
@@ -329,30 +264,32 @@ int mbedtls_base64_decode( unsigned char *dst, size_t dlen, size_t *olen, | |
* n = ( ( n * 6 ) + 7 ) >> 3; | ||
*/ | ||
n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 ); | ||
n -= j; | ||
n -= equals; | ||
|
||
if( dst == NULL || dlen < n ) | ||
{ | ||
*olen = n; | ||
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL ); | ||
} | ||
|
||
for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) | ||
{ | ||
equals = 0; | ||
for( x = 0, p = dst; i > 0; i--, src++ ) | ||
{ | ||
if( *src == '\r' || *src == '\n' || *src == ' ' ) | ||
continue; | ||
|
||
dec_map_lookup = mbedtls_base64_table_lookup( base64_dec_map, sizeof( base64_dec_map ), *src ); | ||
|
||
mbedtls_base64_cond_assign_uint32( &j, j - 1, mbedtls_base64_eq( dec_map_lookup, 64 ) ); | ||
x = ( x << 6 ) | ( dec_map_lookup & 0x3F ); | ||
x = x << 6; | ||
if( *src == '=' ) | ||
++equals; | ||
else | ||
x |= dec_value( *src ); | ||
|
||
if( ++n == 4 ) | ||
if( ++accumulated_digits == 4 ) | ||
{ | ||
n = 0; | ||
if( j > 0 ) *p++ = (unsigned char)( x >> 16 ); | ||
if( j > 1 ) *p++ = (unsigned char)( x >> 8 ); | ||
if( j > 2 ) *p++ = (unsigned char)( x ); | ||
accumulated_digits = 0; | ||
*p++ = (unsigned char)( x >> 16 ); | ||
if( equals <= 1 ) *p++ = (unsigned char)( x >> 8 ); | ||
if( equals <= 0 ) *p++ = (unsigned char)( x ); | ||
} | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo nit: "implemenation" -> "implementation"