Skip to content
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

Added support for AES-ECB to the PSA Crypto implementation #3480

Merged
merged 5 commits into from
Sep 14, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ChangeLog.d/add-aes-ecb-to-psa.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Features
* Add support for ECB to the PSA cipher API.
2 changes: 2 additions & 0 deletions include/mbedtls/psa_util.h
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ static inline psa_algorithm_t mbedtls_psa_translate_cipher_mode(
{
switch( mode )
{
case MBEDTLS_MODE_ECB:
return( PSA_ALG_ECB_NO_PADDING );
case MBEDTLS_MODE_GCM:
return( PSA_ALG_AEAD_WITH_TAG_LENGTH( PSA_ALG_GCM, taglen ) );
case MBEDTLS_MODE_CCM:
Expand Down
20 changes: 20 additions & 0 deletions include/psa/crypto_values.h
Original file line number Diff line number Diff line change
Expand Up @@ -980,6 +980,26 @@
*/
#define PSA_ALG_XTS ((psa_algorithm_t)0x044000ff)

/** The Electronic Code Book (ECB) mode of a block cipher, with no padding.
*
* \warning ECB mode does not protect the confidentiality of the encrypted data
* except in extremely narrow circumstances. It is recommended that applications
* only use ECB if they need to construct an operating mode that the
* implementation does not provide. Implementations are encouraged to provide
* the modes that applications need in preference to supporting direct access
* to ECB.
*
* The underlying block cipher is determined by the key type.
*
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please include the warning against use as well.

* This symmetric cipher mode can only be used with messages whose lengths are a
* multiple of the block size of the chosen block cipher.
*
* ECB mode does not accept an initialization vector (IV). When using a
* multi-part cipher operation with this algorithm, psa_cipher_generate_iv()
* and psa_cipher_set_iv() must not be called.
*/
#define PSA_ALG_ECB_NO_PADDING ((psa_algorithm_t)0x04404400)

/** The CBC block cipher chaining mode, with no padding.
*
* The underlying block cipher is determined by the key type.
Expand Down
136 changes: 126 additions & 10 deletions library/psa_crypto.c
Original file line number Diff line number Diff line change
Expand Up @@ -2518,6 +2518,9 @@ static const mbedtls_cipher_info_t *mbedtls_cipher_info_from_psa(
case PSA_ALG_OFB:
mode = MBEDTLS_MODE_OFB;
break;
case PSA_ALG_ECB_NO_PADDING:
mode = MBEDTLS_MODE_ECB;
break;
case PSA_ALG_CBC_NO_PADDING:
mode = MBEDTLS_MODE_CBC;
break;
Expand Down Expand Up @@ -3746,7 +3749,14 @@ static psa_status_t psa_cipher_init( psa_cipher_operation_t *operation,
operation->alg = alg;
operation->key_set = 0;
operation->iv_set = 0;
operation->iv_required = 1;
if( alg == PSA_ALG_ECB_NO_PADDING )
{
operation->iv_required = 0;
}
else
{
operation->iv_required = 1;
}
operation->iv_size = 0;
operation->block_size = 0;
mbedtls_cipher_init( &operation->ctx.cipher );
Expand Down Expand Up @@ -3837,7 +3847,8 @@ static psa_status_t psa_cipher_setup( psa_cipher_operation_t *operation,
operation->key_set = 1;
operation->block_size = ( PSA_ALG_IS_STREAM_CIPHER( alg ) ? 1 :
PSA_BLOCK_CIPHER_BLOCK_SIZE( slot->attr.type ) );
if( alg & PSA_ALG_CIPHER_FROM_BLOCK_FLAG )
if( ( alg & PSA_ALG_CIPHER_FROM_BLOCK_FLAG ) != 0 &&
alg != PSA_ALG_ECB_NO_PADDING )
{
operation->iv_size = PSA_BLOCK_CIPHER_BLOCK_SIZE( slot->attr.type );
}
Expand Down Expand Up @@ -3927,15 +3938,102 @@ psa_status_t psa_cipher_set_iv( psa_cipher_operation_t *operation,
return( status );
}

/* Process input for which the algorithm is set to ECB mode. This requires
* manual processing, since the PSA API is defined as being able to process
* arbitrary-length calls to psa_cipher_update() with ECB mode, but the
* underlying mbedtls_cipher_update only takes full blocks. */
static psa_status_t psa_cipher_update_ecb_internal(
mbedtls_cipher_context_t *ctx,
const uint8_t *input,
size_t input_length,
uint8_t *output,
size_t output_size,
size_t *output_length )
{
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t block_size = ctx->cipher_info->block_size;
size_t internal_output_length = 0;
*output_length = 0;

if( input_length == 0 )
{
status = PSA_SUCCESS;
goto exit;
}

if( ctx->unprocessed_len > 0 )
{
/* Fill up to block size, and run the block if there's a full one. */
size_t bytes_to_copy = block_size - ctx->unprocessed_len;

if( input_length < bytes_to_copy )
bytes_to_copy = input_length;

memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ),
input, bytes_to_copy );
input_length -= bytes_to_copy;
input += bytes_to_copy;
ctx->unprocessed_len += bytes_to_copy;

if( ctx->unprocessed_len == block_size )
{
status = mbedtls_to_psa_error(
mbedtls_cipher_update( ctx,
ctx->unprocessed_data,
block_size,
output, &internal_output_length ) );

if( status != PSA_SUCCESS )
goto exit;

output += internal_output_length;
output_size -= internal_output_length;
*output_length += internal_output_length;
ctx->unprocessed_len = 0;
}
}

while( input_length >= block_size )
{
/* Run all full blocks we have, one by one */
status = mbedtls_to_psa_error(
mbedtls_cipher_update( ctx, input,
block_size,
output, &internal_output_length ) );

if( status != PSA_SUCCESS )
goto exit;

input_length -= block_size;
input += block_size;

output += internal_output_length;
output_size -= internal_output_length;
*output_length += internal_output_length;
}

if( input_length > 0 )
{
/* Save unprocessed bytes for later processing */
memcpy( &( ctx->unprocessed_data[ctx->unprocessed_len] ),
input, input_length );
ctx->unprocessed_len += input_length;
}

status = PSA_SUCCESS;

exit:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there's no cleanup, it would be more readable to use return( status ) instead of goto exit (and return( whatever ); instead of status = whatever; goto exit;). I don't consider it a blocker.

return( status );
}

psa_status_t psa_cipher_update( psa_cipher_operation_t *operation,
const uint8_t *input,
size_t input_length,
uint8_t *output,
size_t output_size,
size_t *output_length )
{
psa_status_t status;
int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED;
psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED;
size_t expected_output_size;

if( operation->alg == 0 )
Expand Down Expand Up @@ -3964,9 +4062,24 @@ psa_status_t psa_cipher_update( psa_cipher_operation_t *operation,
goto exit;
}

ret = mbedtls_cipher_update( &operation->ctx.cipher, input,
input_length, output, output_length );
status = mbedtls_to_psa_error( ret );
if( operation->alg == PSA_ALG_ECB_NO_PADDING )
{
gilles-peskine-arm marked this conversation as resolved.
Show resolved Hide resolved
/* mbedtls_cipher_update has an API inconsistency: it will only
* process a single block at a time in ECB mode. Abstract away that
* inconsistency here to match the PSA API behaviour. */
status = psa_cipher_update_ecb_internal( &operation->ctx.cipher,
input,
input_length,
output,
output_size,
output_length );
}
else
{
status = mbedtls_to_psa_error(
mbedtls_cipher_update( &operation->ctx.cipher, input,
input_length, output, output_length ) );
}
exit:
if( status != PSA_SUCCESS )
psa_cipher_abort( operation );
Expand All @@ -3991,12 +4104,15 @@ psa_status_t psa_cipher_finish( psa_cipher_operation_t *operation,
return( PSA_ERROR_BAD_STATE );
}

if( operation->ctx.cipher.operation == MBEDTLS_ENCRYPT &&
operation->alg == PSA_ALG_CBC_NO_PADDING &&
operation->ctx.cipher.unprocessed_len != 0 )
if( operation->ctx.cipher.unprocessed_len != 0 )
{
if( operation->alg == PSA_ALG_ECB_NO_PADDING ||
( operation->alg == PSA_ALG_CBC_NO_PADDING &&
operation->ctx.cipher.operation == MBEDTLS_ENCRYPT ) )
{
status = PSA_ERROR_INVALID_ARGUMENT;
goto error;
}
}

cipher_ret = mbedtls_cipher_finish( &operation->ctx.cipher,
Expand Down
Loading