mirror of
https://github.com/Mbed-TLS/mbedtls.git
synced 2025-07-29 11:41:15 +03:00
Merge pull request #716 from mpg/ct-varlen-hmac
Add constant-flow variable-length HMAC function
This commit is contained in:
78
library/ssl_invasive.h
Normal file
78
library/ssl_invasive.h
Normal file
@ -0,0 +1,78 @@
|
||||
/**
|
||||
* \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 (C) 2020, ARM Limited, All Rights Reserved
|
||||
* 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.
|
||||
*
|
||||
* This file is part of mbed TLS (https://tls.mbed.org)
|
||||
*/
|
||||
#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 );
|
||||
#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC */
|
||||
|
||||
#endif /* MBEDTLS_SSL_INVASIVE_H */
|
@ -47,6 +47,8 @@
|
||||
#include "mbedtls/platform_util.h"
|
||||
#include "mbedtls/version.h"
|
||||
|
||||
#include "ssl_invasive.h"
|
||||
|
||||
#include <string.h>
|
||||
|
||||
#if defined(MBEDTLS_USE_PSA_CRYPTO)
|
||||
@ -320,7 +322,7 @@ int (*mbedtls_ssl_hw_record_finish)( mbedtls_ssl_context *ssl ) = NULL;
|
||||
defined(MBEDTLS_SSL_PROTO_TLS1_2) )
|
||||
/* This function makes sure every byte in the memory region is accessed
|
||||
* (in ascending addresses order) */
|
||||
static void ssl_read_memory( unsigned char *p, size_t len )
|
||||
static void ssl_read_memory( const unsigned char *p, size_t len )
|
||||
{
|
||||
unsigned char acc = 0;
|
||||
volatile unsigned char force;
|
||||
@ -609,10 +611,7 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
|
||||
|
||||
/* The PRNG is used for dynamic IV generation that's used
|
||||
* for CBC transformations in TLS 1.1 and TLS 1.2. */
|
||||
#if !( defined(MBEDTLS_CIPHER_MODE_CBC) && \
|
||||
( defined(MBEDTLS_AES_C) || \
|
||||
defined(MBEDTLS_ARIA_C) || \
|
||||
defined(MBEDTLS_CAMELLIA_C) ) && \
|
||||
#if !( defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC) && \
|
||||
( defined(MBEDTLS_SSL_PROTO_TLS1_1) || defined(MBEDTLS_SSL_PROTO_TLS1_2) ) )
|
||||
((void) f_rng);
|
||||
((void) p_rng);
|
||||
@ -910,8 +909,7 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
|
||||
}
|
||||
else
|
||||
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C || MBEDTLS_CHACHAPOLY_C */
|
||||
#if defined(MBEDTLS_CIPHER_MODE_CBC) && \
|
||||
( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) || defined(MBEDTLS_ARIA_C) )
|
||||
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC)
|
||||
if( mode == MBEDTLS_MODE_CBC )
|
||||
{
|
||||
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
|
||||
@ -1050,8 +1048,7 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
|
||||
#endif /* MBEDTLS_SSL_ENCRYPT_THEN_MAC */
|
||||
}
|
||||
else
|
||||
#endif /* MBEDTLS_CIPHER_MODE_CBC &&
|
||||
( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C || MBEDTLS_ARIA_C ) */
|
||||
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC) */
|
||||
{
|
||||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
|
||||
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
|
||||
@ -1069,6 +1066,135 @@ int mbedtls_ssl_encrypt_buf( mbedtls_ssl_context *ssl,
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_TLS_CBC)
|
||||
/*
|
||||
* 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.
|
||||
*
|
||||
* Use only bit operations to avoid branches that could be used by some
|
||||
* compilers on some platforms to translate comparison operators.
|
||||
*/
|
||||
static void mbedtls_ssl_cf_memcpy_if_eq( unsigned char *dst,
|
||||
const unsigned char *src,
|
||||
size_t len,
|
||||
size_t c1, size_t c2 )
|
||||
{
|
||||
/* diff = 0 if c1 == c2, non-zero otherwise */
|
||||
const size_t diff = c1 ^ c2;
|
||||
|
||||
/* 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 c1 != c2 */
|
||||
const size_t diff_msb = ( diff | -diff );
|
||||
|
||||
/* diff1 = c1 != c2 */
|
||||
const size_t diff1 = diff_msb >> ( sizeof( diff_msb ) * 8 - 1 );
|
||||
|
||||
/* mask = c1 != c2 ? 0xff : 0x00 */
|
||||
const unsigned char mask = (unsigned char) -diff1;
|
||||
|
||||
#if defined(_MSC_VER)
|
||||
#pragma warning( pop )
|
||||
#endif
|
||||
|
||||
/* dst[i] = c1 != c2 ? dst[i] : src[i] */
|
||||
for( size_t i = 0; i < len; i++ )
|
||||
dst[i] = ( dst[i] & mask ) | ( src[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 ) );
|
||||
}
|
||||
|
||||
/* 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 */
|
||||
|
||||
int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
|
||||
mbedtls_ssl_transform *transform,
|
||||
mbedtls_record *rec )
|
||||
@ -1239,8 +1365,7 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
|
||||
}
|
||||
else
|
||||
#endif /* MBEDTLS_GCM_C || MBEDTLS_CCM_C */
|
||||
#if defined(MBEDTLS_CIPHER_MODE_CBC) && \
|
||||
( defined(MBEDTLS_AES_C) || defined(MBEDTLS_CAMELLIA_C) || defined(MBEDTLS_ARIA_C) )
|
||||
#if defined(MBEDTLS_SSL_SOME_SUITES_USE_CBC)
|
||||
if( mode == MBEDTLS_MODE_CBC )
|
||||
{
|
||||
size_t minlen = 0;
|
||||
@ -1493,8 +1618,7 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
|
||||
rec->data_len -= padlen;
|
||||
}
|
||||
else
|
||||
#endif /* MBEDTLS_CIPHER_MODE_CBC &&
|
||||
( MBEDTLS_AES_C || MBEDTLS_CAMELLIA_C || MBEDTLS_ARIA_C ) */
|
||||
#endif /* MBEDTLS_SSL_SOME_SUITES_USE_CBC */
|
||||
{
|
||||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
|
||||
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
|
||||
@ -1546,38 +1670,6 @@ int mbedtls_ssl_decrypt_buf( mbedtls_ssl_context const *ssl,
|
||||
defined(MBEDTLS_SSL_PROTO_TLS1_2)
|
||||
if( transform->minor_ver > MBEDTLS_SSL_MINOR_VERSION_0 )
|
||||
{
|
||||
/*
|
||||
* Process MAC and always update for padlen afterwards to make
|
||||
* total time independent of padlen.
|
||||
*
|
||||
* Known timing attacks:
|
||||
* - Lucky Thirteen (http://www.isg.rhul.ac.uk/tls/TLStiming.pdf)
|
||||
*
|
||||
* To compensate for different timings for the MAC calculation
|
||||
* depending on how much padding was removed (which is determined
|
||||
* by padlen), process extra_run more blocks through the hash
|
||||
* function.
|
||||
*
|
||||
* The formula in the paper is
|
||||
* extra_run = ceil( (L1-55) / 64 ) - ceil( (L2-55) / 64 )
|
||||
* where L1 is the size of the header plus the decrypted message
|
||||
* plus CBC padding and L2 is the size of the header plus the
|
||||
* decrypted message. This is for an underlying hash function
|
||||
* with 64-byte blocks.
|
||||
* We use ( (Lx+8) / 64 ) to handle 'negative Lx' values
|
||||
* correctly. We round down instead of up, so -56 is the correct
|
||||
* value for our calculations instead of -55.
|
||||
*
|
||||
* Repeat the formula rather than defining a block_size variable.
|
||||
* This avoids requiring division by a variable at runtime
|
||||
* (which would be marginally less efficient and would require
|
||||
* linking an extra division function in some builds).
|
||||
*/
|
||||
size_t j, extra_run = 0;
|
||||
/* This size is enough to server either as input to
|
||||
* md_process() or as output to md_finish() */
|
||||
unsigned char tmp[MBEDTLS_MD_MAX_BLOCK_SIZE];
|
||||
|
||||
/*
|
||||
* The next two sizes are the minimum and maximum values of
|
||||
* in_msglen over all padlen values.
|
||||
@ -1591,58 +1683,16 @@ 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;
|
||||
|
||||
memset( tmp, 0, sizeof( tmp ) );
|
||||
|
||||
switch( mbedtls_md_get_type( transform->md_ctx_dec.md_info ) )
|
||||
ret = mbedtls_ssl_cf_hmac( &transform->md_ctx_dec,
|
||||
add_data, add_data_len,
|
||||
data, rec->data_len, min_len, max_len,
|
||||
mac_expect );
|
||||
if( ret != 0 )
|
||||
{
|
||||
#if defined(MBEDTLS_MD5_C) || defined(MBEDTLS_SHA1_C) || \
|
||||
defined(MBEDTLS_SHA256_C)
|
||||
case MBEDTLS_MD_MD5:
|
||||
case MBEDTLS_MD_SHA1:
|
||||
case MBEDTLS_MD_SHA256:
|
||||
/* 8 bytes of message size, 64-byte compression blocks */
|
||||
extra_run =
|
||||
( add_data_len + rec->data_len + padlen + 8 ) / 64 -
|
||||
( add_data_len + rec->data_len + 8 ) / 64;
|
||||
break;
|
||||
#endif
|
||||
#if defined(MBEDTLS_SHA512_C)
|
||||
case MBEDTLS_MD_SHA384:
|
||||
/* 16 bytes of message size, 128-byte compression blocks */
|
||||
extra_run =
|
||||
( add_data_len + rec->data_len + padlen + 16 ) / 128 -
|
||||
( add_data_len + rec->data_len + 16 ) / 128;
|
||||
break;
|
||||
#endif
|
||||
default:
|
||||
MBEDTLS_SSL_DEBUG_MSG( 1, ( "should never happen" ) );
|
||||
return( MBEDTLS_ERR_SSL_INTERNAL_ERROR );
|
||||
MBEDTLS_SSL_DEBUG_RET( 1, "mbedtls_ssl_cf_hmac", ret );
|
||||
return( ret );
|
||||
}
|
||||
|
||||
extra_run &= correct * 0xFF;
|
||||
|
||||
mbedtls_md_hmac_update( &transform->md_ctx_dec, add_data,
|
||||
add_data_len );
|
||||
mbedtls_md_hmac_update( &transform->md_ctx_dec, data,
|
||||
rec->data_len );
|
||||
/* Make sure we access everything even when padlen > 0. This
|
||||
* makes the synchronisation requirements for just-in-time
|
||||
* Prime+Probe attacks much tighter and hopefully impractical. */
|
||||
ssl_read_memory( data + rec->data_len, padlen );
|
||||
mbedtls_md_hmac_finish( &transform->md_ctx_dec, mac_expect );
|
||||
|
||||
/* Dummy calls to compression function.
|
||||
* Call mbedtls_md_process at least once due to cache attacks
|
||||
* that observe whether md_process() was called of not.
|
||||
* Respect the usual start-(process|update)-finish sequence for
|
||||
* the sake of hardware accelerators that might require it. */
|
||||
mbedtls_md_starts( &transform->md_ctx_dec );
|
||||
for( j = 0; j < extra_run + 1; j++ )
|
||||
mbedtls_md_process( &transform->md_ctx_dec, tmp );
|
||||
mbedtls_md_finish( &transform->md_ctx_dec, tmp );
|
||||
|
||||
mbedtls_md_hmac_reset( &transform->md_ctx_dec );
|
||||
|
||||
/* Make sure we access all the memory that could contain the MAC,
|
||||
* before we check it in the next code block. This makes the
|
||||
* synchronisation requirements for just-in-time Prime+Probe
|
||||
|
@ -557,6 +557,9 @@ static const char * const features[] = {
|
||||
#if defined(MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH)
|
||||
"MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH",
|
||||
#endif /* MBEDTLS_SSL_VARIABLE_BUFFER_LENGTH */
|
||||
#if defined(MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN)
|
||||
"MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN",
|
||||
#endif /* MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN */
|
||||
#if defined(MBEDTLS_TEST_HOOKS)
|
||||
"MBEDTLS_TEST_HOOKS",
|
||||
#endif /* MBEDTLS_TEST_HOOKS */
|
||||
|
Reference in New Issue
Block a user