mirror of
https://github.com/yuzu-emu/mbedtls.git
synced 2024-11-27 07:34:14 +01:00
ad1115a3fd
byte shifting opertations throughout library/ were only replaced with the byte reading macros when an 0xff mask was being used. The byte reading macros are now more widley used, however they have not been used in all cases of a byte shift operation, as it detracted from the immediate readability or otherwise did not seem appropriate. Signed-off-by: Joe Subbiani <joe.subbiani@arm.com>
397 lines
11 KiB
C
397 lines
11 KiB
C
/*
|
|
* RFC 1521 base64 encoding/decoding
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
#include "common.h"
|
|
|
|
#if defined(MBEDTLS_BASE64_C)
|
|
|
|
#include "mbedtls/base64.h"
|
|
|
|
#include <stdint.h>
|
|
|
|
#if defined(MBEDTLS_SELF_TEST)
|
|
#include <string.h>
|
|
#if defined(MBEDTLS_PLATFORM_C)
|
|
#include "mbedtls/platform.h"
|
|
#else
|
|
#include <stdio.h>
|
|
#define mbedtls_printf printf
|
|
#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
|
|
*/
|
|
static unsigned char mbedtls_base64_eq( size_t in_a, size_t in_b )
|
|
{
|
|
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 );
|
|
}
|
|
|
|
/*
|
|
* Constant flow lookup into table.
|
|
*/
|
|
static unsigned char mbedtls_base64_table_lookup( const unsigned char * const table,
|
|
const size_t table_size, const size_t table_index )
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* Encode a buffer into base64 format
|
|
*/
|
|
int mbedtls_base64_encode( unsigned char *dst, size_t dlen, size_t *olen,
|
|
const unsigned char *src, size_t slen )
|
|
{
|
|
size_t i, n;
|
|
int C1, C2, C3;
|
|
unsigned char *p;
|
|
|
|
if( slen == 0 )
|
|
{
|
|
*olen = 0;
|
|
return( 0 );
|
|
}
|
|
|
|
n = slen / 3 + ( slen % 3 != 0 );
|
|
|
|
if( n > ( BASE64_SIZE_T_MAX - 1 ) / 4 )
|
|
{
|
|
*olen = BASE64_SIZE_T_MAX;
|
|
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
|
|
}
|
|
|
|
n *= 4;
|
|
|
|
if( ( dlen < n + 1 ) || ( NULL == dst ) )
|
|
{
|
|
*olen = n + 1;
|
|
return( MBEDTLS_ERR_BASE64_BUFFER_TOO_SMALL );
|
|
}
|
|
|
|
n = ( slen / 3 ) * 3;
|
|
|
|
for( i = 0, p = dst; i < n; i += 3 )
|
|
{
|
|
C1 = *src++;
|
|
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 ) );
|
|
}
|
|
|
|
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 ) );
|
|
|
|
if( ( i + 1 ) < slen )
|
|
*p++ = mbedtls_base64_table_lookup( base64_enc_map, sizeof( base64_enc_map ),
|
|
( ( ( C2 & 15 ) << 2 ) & 0x3F ) );
|
|
else *p++ = '=';
|
|
|
|
*p++ = '=';
|
|
}
|
|
|
|
*olen = p - dst;
|
|
*p = 0;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/*
|
|
* 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;
|
|
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++ )
|
|
{
|
|
/* Skip spaces before checking for EOL */
|
|
x = 0;
|
|
while( i < slen && src[i] == ' ' )
|
|
{
|
|
++i;
|
|
++x;
|
|
}
|
|
|
|
/* Spaces at end of buffer are OK */
|
|
if( i == slen )
|
|
break;
|
|
|
|
if( ( slen - i ) >= 2 &&
|
|
src[i] == '\r' && src[i + 1] == '\n' )
|
|
continue;
|
|
|
|
if( src[i] == '\n' )
|
|
continue;
|
|
|
|
/* Space inside a line is an error */
|
|
if( x != 0 )
|
|
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 )
|
|
return( MBEDTLS_ERR_BASE64_INVALID_CHARACTER );
|
|
|
|
n++;
|
|
}
|
|
|
|
if( n == 0 )
|
|
{
|
|
*olen = 0;
|
|
return( 0 );
|
|
}
|
|
|
|
/* The following expression is to calculate the following formula without
|
|
* risk of integer overflow in n:
|
|
* n = ( ( n * 6 ) + 7 ) >> 3;
|
|
*/
|
|
n = ( 6 * ( n >> 3 ) ) + ( ( 6 * ( n & 0x7 ) + 7 ) >> 3 );
|
|
n -= j;
|
|
|
|
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++ )
|
|
{
|
|
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 );
|
|
|
|
if( ++n == 4 )
|
|
{
|
|
n = 0;
|
|
if( j > 0 ) *p++ = MBEDTLS_BYTE_2( x );
|
|
if( j > 1 ) *p++ = MBEDTLS_BYTE_1( x );
|
|
if( j > 2 ) *p++ = MBEDTLS_BYTE_0( x );
|
|
}
|
|
}
|
|
|
|
*olen = p - dst;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#if defined(MBEDTLS_SELF_TEST)
|
|
|
|
static const unsigned char base64_test_dec[64] =
|
|
{
|
|
0x24, 0x48, 0x6E, 0x56, 0x87, 0x62, 0x5A, 0xBD,
|
|
0xBF, 0x17, 0xD9, 0xA2, 0xC4, 0x17, 0x1A, 0x01,
|
|
0x94, 0xED, 0x8F, 0x1E, 0x11, 0xB3, 0xD7, 0x09,
|
|
0x0C, 0xB6, 0xE9, 0x10, 0x6F, 0x22, 0xEE, 0x13,
|
|
0xCA, 0xB3, 0x07, 0x05, 0x76, 0xC9, 0xFA, 0x31,
|
|
0x6C, 0x08, 0x34, 0xFF, 0x8D, 0xC2, 0x6C, 0x38,
|
|
0x00, 0x43, 0xE9, 0x54, 0x97, 0xAF, 0x50, 0x4B,
|
|
0xD1, 0x41, 0xBA, 0x95, 0x31, 0x5A, 0x0B, 0x97
|
|
};
|
|
|
|
static const unsigned char base64_test_enc[] =
|
|
"JEhuVodiWr2/F9mixBcaAZTtjx4Rs9cJDLbpEG8i7hPK"
|
|
"swcFdsn6MWwINP+Nwmw4AEPpVJevUEvRQbqVMVoLlw==";
|
|
|
|
/*
|
|
* Checkup routine
|
|
*/
|
|
int mbedtls_base64_self_test( int verbose )
|
|
{
|
|
size_t len;
|
|
const unsigned char *src;
|
|
unsigned char buffer[128];
|
|
|
|
if( verbose != 0 )
|
|
mbedtls_printf( " Base64 encoding test: " );
|
|
|
|
src = base64_test_dec;
|
|
|
|
if( mbedtls_base64_encode( buffer, sizeof( buffer ), &len, src, 64 ) != 0 ||
|
|
memcmp( base64_test_enc, buffer, 88 ) != 0 )
|
|
{
|
|
if( verbose != 0 )
|
|
mbedtls_printf( "failed\n" );
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
if( verbose != 0 )
|
|
mbedtls_printf( "passed\n Base64 decoding test: " );
|
|
|
|
src = base64_test_enc;
|
|
|
|
if( mbedtls_base64_decode( buffer, sizeof( buffer ), &len, src, 88 ) != 0 ||
|
|
memcmp( base64_test_dec, buffer, 64 ) != 0 )
|
|
{
|
|
if( verbose != 0 )
|
|
mbedtls_printf( "failed\n" );
|
|
|
|
return( 1 );
|
|
}
|
|
|
|
if( verbose != 0 )
|
|
mbedtls_printf( "passed\n\n" );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
#endif /* MBEDTLS_SELF_TEST */
|
|
|
|
#endif /* MBEDTLS_BASE64_C */
|