Refactor AEAD setup into a common function

There was a lot of repetition between psa_aead_encrypt and
psa_aead_decrypt. Refactor the code into a new function psa_aead_setup.
The new code should behave identically except that in some cases where
multiple error conditions apply, the code may now return a different
error code.

Internally, I rearranged some of the code:

* I removed a check that the key type was in CATEGORY_SYMMETRIC because
  it's redundant with mbedtls_cipher_info_from_psa which enumerates
  supported key types explicitly.
* The order of some validations is different to allow the split between
  setup and data processing. The code now calls a more robust function
  psa_aead_abort in case of any error after the early stage of the setup.
This commit is contained in:
Gilles Peskine 2018-08-17 18:11:56 +02:00
parent d911eb7915
commit edf9a6576d

View File

@ -2821,6 +2821,102 @@ psa_status_t psa_set_key_lifetime( psa_key_slot_t key,
/* AEAD */ /* AEAD */
/****************************************************************/ /****************************************************************/
typedef struct
{
key_slot_t *slot;
const mbedtls_cipher_info_t *cipher_info;
union
{
#if defined(MBEDTLS_CCM_C)
mbedtls_ccm_context ccm;
#endif /* MBEDTLS_CCM_C */
#if defined(MBEDTLS_GCM_C)
mbedtls_gcm_context gcm;
#endif /* MBEDTLS_GCM_C */
} ctx;
uint8_t tag_length;
} aead_operation_t;
static void psa_aead_abort( aead_operation_t *operation,
psa_algorithm_t alg )
{
switch( alg )
{
#if defined(MBEDTLS_CCM_C)
case PSA_ALG_CCM:
mbedtls_ccm_free( &operation->ctx.ccm );
break;
#endif /* MBEDTLS_CCM_C */
#if defined(MBEDTLS_CCM_C)
case PSA_ALG_GCM:
mbedtls_gcm_free( &operation->ctx.gcm );
break;
#endif /* MBEDTLS_GCM_C */
}
}
static psa_status_t psa_aead_setup( aead_operation_t *operation,
psa_key_slot_t key,
psa_key_usage_t usage,
psa_algorithm_t alg )
{
psa_status_t status;
size_t key_bits;
mbedtls_cipher_id_t cipher_id;
status = psa_get_key_from_slot( key, &operation->slot, usage, alg );
if( status != PSA_SUCCESS )
return( status );
key_bits = psa_get_key_bits( operation->slot );
operation->cipher_info =
mbedtls_cipher_info_from_psa( alg, operation->slot->type, key_bits,
&cipher_id );
if( operation->cipher_info == NULL )
return( PSA_ERROR_NOT_SUPPORTED );
switch( alg )
{
#if defined(MBEDTLS_CCM_C)
case PSA_ALG_CCM:
operation->tag_length = 16;
if( PSA_BLOCK_CIPHER_BLOCK_SIZE( operation->slot->type ) != 16 )
return( PSA_ERROR_INVALID_ARGUMENT );
mbedtls_ccm_init( &operation->ctx.ccm );
status = mbedtls_to_psa_error(
mbedtls_ccm_setkey( &operation->ctx.ccm, cipher_id,
operation->slot->data.raw.data,
(unsigned int) key_bits ) );
if( status != 0 )
goto cleanup;
break;
#endif /* MBEDTLS_CCM_C */
#if defined(MBEDTLS_GCM_C)
case PSA_ALG_GCM:
operation->tag_length = 16;
if( PSA_BLOCK_CIPHER_BLOCK_SIZE( operation->slot->type ) != 16 )
return( PSA_ERROR_INVALID_ARGUMENT );
mbedtls_gcm_init( &operation->ctx.gcm );
status = mbedtls_to_psa_error(
mbedtls_gcm_setkey( &operation->ctx.gcm, cipher_id,
operation->slot->data.raw.data,
(unsigned int) key_bits ) );
break;
#endif /* MBEDTLS_GCM_C */
default:
return( PSA_ERROR_NOT_SUPPORTED );
}
return( PSA_SUCCESS );
cleanup:
psa_aead_abort( operation, alg );
return( status );
}
psa_status_t psa_aead_encrypt( psa_key_slot_t key, psa_status_t psa_aead_encrypt( psa_key_slot_t key,
psa_algorithm_t alg, psa_algorithm_t alg,
const uint8_t *nonce, const uint8_t *nonce,
@ -2833,113 +2929,60 @@ psa_status_t psa_aead_encrypt( psa_key_slot_t key,
size_t ciphertext_size, size_t ciphertext_size,
size_t *ciphertext_length ) size_t *ciphertext_length )
{ {
int ret;
psa_status_t status; psa_status_t status;
key_slot_t *slot; aead_operation_t operation;
size_t key_bits;
uint8_t *tag; uint8_t *tag;
size_t tag_length;
mbedtls_cipher_id_t cipher_id;
const mbedtls_cipher_info_t *cipher_info = NULL;
*ciphertext_length = 0; *ciphertext_length = 0;
status = psa_get_key_from_slot( key, &slot, PSA_KEY_USAGE_ENCRYPT, alg ); status = psa_aead_setup( &operation, key, PSA_KEY_USAGE_ENCRYPT, alg );
if( status != PSA_SUCCESS ) if( status != PSA_SUCCESS )
return( status ); return( status );
key_bits = psa_get_key_bits( slot );
cipher_info = mbedtls_cipher_info_from_psa( alg, slot->type, /* For all currently supported modes, the tag is at the end of the
key_bits, &cipher_id ); * ciphertext. */
if( cipher_info == NULL ) if( ciphertext_size < ( plaintext_length + operation.tag_length ) )
return( PSA_ERROR_NOT_SUPPORTED ); {
status = PSA_ERROR_BUFFER_TOO_SMALL;
if( ( slot->type & PSA_KEY_TYPE_CATEGORY_MASK ) != goto exit;
PSA_KEY_TYPE_CATEGORY_SYMMETRIC ) }
return( PSA_ERROR_INVALID_ARGUMENT ); tag = ciphertext + plaintext_length;
if( alg == PSA_ALG_GCM ) if( alg == PSA_ALG_GCM )
{ {
mbedtls_gcm_context gcm; status = mbedtls_to_psa_error(
tag_length = 16; mbedtls_gcm_crypt_and_tag( &operation.ctx.gcm,
MBEDTLS_GCM_ENCRYPT,
if( PSA_BLOCK_CIPHER_BLOCK_SIZE( slot->type ) != 16 ) plaintext_length,
return( PSA_ERROR_INVALID_ARGUMENT ); nonce, nonce_length,
additional_data, additional_data_length,
//make sure we have place to hold the tag in the ciphertext buffer plaintext, ciphertext,
if( ciphertext_size < ( plaintext_length + tag_length ) ) operation.tag_length, tag ) );
return( PSA_ERROR_BUFFER_TOO_SMALL );
//update the tag pointer to point to the end of the ciphertext_length
tag = ciphertext + plaintext_length;
mbedtls_gcm_init( &gcm );
ret = mbedtls_gcm_setkey( &gcm, cipher_id,
slot->data.raw.data,
(unsigned int) key_bits );
if( ret != 0 )
{
mbedtls_gcm_free( &gcm );
return( mbedtls_to_psa_error( ret ) );
}
ret = mbedtls_gcm_crypt_and_tag( &gcm, MBEDTLS_GCM_ENCRYPT,
plaintext_length, nonce,
nonce_length, additional_data,
additional_data_length, plaintext,
ciphertext, tag_length, tag );
mbedtls_gcm_free( &gcm );
} }
else if( alg == PSA_ALG_CCM ) else if( alg == PSA_ALG_CCM )
{ {
mbedtls_ccm_context ccm; status = mbedtls_to_psa_error(
tag_length = 16; mbedtls_ccm_encrypt_and_tag( &operation.ctx.ccm,
plaintext_length,
if( PSA_BLOCK_CIPHER_BLOCK_SIZE( slot->type ) != 16 ) nonce, nonce_length,
return( PSA_ERROR_INVALID_ARGUMENT ); additional_data,
additional_data_length,
if( nonce_length < 7 || nonce_length > 13 ) plaintext, ciphertext,
return( PSA_ERROR_INVALID_ARGUMENT ); tag, operation.tag_length ) );
//make sure we have place to hold the tag in the ciphertext buffer
if( ciphertext_size < ( plaintext_length + tag_length ) )
return( PSA_ERROR_BUFFER_TOO_SMALL );
//update the tag pointer to point to the end of the ciphertext_length
tag = ciphertext + plaintext_length;
mbedtls_ccm_init( &ccm );
ret = mbedtls_ccm_setkey( &ccm, cipher_id,
slot->data.raw.data,
(unsigned int) key_bits );
if( ret != 0 )
{
mbedtls_ccm_free( &ccm );
return( mbedtls_to_psa_error( ret ) );
}
ret = mbedtls_ccm_encrypt_and_tag( &ccm, plaintext_length,
nonce, nonce_length,
additional_data,
additional_data_length,
plaintext, ciphertext,
tag, tag_length );
mbedtls_ccm_free( &ccm );
} }
else else
{ {
return( PSA_ERROR_NOT_SUPPORTED ); return( PSA_ERROR_NOT_SUPPORTED );
} }
if( ret != 0 ) if( status != PSA_SUCCESS && ciphertext_size != 0 )
{ memset( ciphertext, 0, ciphertext_size );
/* If ciphertext_size is 0 then ciphertext may be NULL and then the
* call to memset would have undefined behavior. */
if( ciphertext_size != 0 )
memset( ciphertext, 0, ciphertext_size );
return( mbedtls_to_psa_error( ret ) );
}
*ciphertext_length = plaintext_length + tag_length; exit:
return( PSA_SUCCESS ); psa_aead_abort( &operation, alg );
if( status == PSA_SUCCESS )
*ciphertext_length = plaintext_length + operation.tag_length;
return( status );
} }
/* Locate the tag in a ciphertext buffer containing the encrypted data /* Locate the tag in a ciphertext buffer containing the encrypted data
@ -2975,108 +3018,63 @@ psa_status_t psa_aead_decrypt( psa_key_slot_t key,
size_t plaintext_size, size_t plaintext_size,
size_t *plaintext_length ) size_t *plaintext_length )
{ {
int ret;
psa_status_t status; psa_status_t status;
key_slot_t *slot; aead_operation_t operation;
size_t key_bits; const uint8_t *tag = NULL;
const uint8_t *tag;
size_t tag_length;
mbedtls_cipher_id_t cipher_id;
const mbedtls_cipher_info_t *cipher_info = NULL;
*plaintext_length = 0; *plaintext_length = 0;
status = psa_get_key_from_slot( key, &slot, PSA_KEY_USAGE_DECRYPT, alg ); status = psa_aead_setup( &operation, key, PSA_KEY_USAGE_DECRYPT, alg );
if( status != PSA_SUCCESS ) if( status != PSA_SUCCESS )
return( status ); return( status );
key_bits = psa_get_key_bits( slot );
cipher_info = mbedtls_cipher_info_from_psa( alg, slot->type,
key_bits, &cipher_id );
if( cipher_info == NULL )
return( PSA_ERROR_NOT_SUPPORTED );
if( ( slot->type & PSA_KEY_TYPE_CATEGORY_MASK ) !=
PSA_KEY_TYPE_CATEGORY_SYMMETRIC )
return( PSA_ERROR_INVALID_ARGUMENT );
if( alg == PSA_ALG_GCM ) if( alg == PSA_ALG_GCM )
{ {
mbedtls_gcm_context gcm; status = psa_aead_unpadded_locate_tag( operation.tag_length,
tag_length = 16;
status = psa_aead_unpadded_locate_tag( tag_length,
ciphertext, ciphertext_length, ciphertext, ciphertext_length,
plaintext_size, &tag ); plaintext_size, &tag );
if( status != PSA_SUCCESS ) if( status != PSA_SUCCESS )
return( status ); goto exit;
mbedtls_gcm_init( &gcm ); status = mbedtls_to_psa_error(
ret = mbedtls_gcm_setkey( &gcm, cipher_id, mbedtls_gcm_auth_decrypt( &operation.ctx.gcm,
slot->data.raw.data, ciphertext_length - operation.tag_length,
(unsigned int) key_bits ); nonce, nonce_length,
if( ret != 0 ) additional_data,
{ additional_data_length,
mbedtls_gcm_free( &gcm ); tag, operation.tag_length,
return( mbedtls_to_psa_error( ret ) ); ciphertext, plaintext ) );
}
ret = mbedtls_gcm_auth_decrypt( &gcm,
ciphertext_length - tag_length,
nonce, nonce_length,
additional_data,
additional_data_length,
tag, tag_length,
ciphertext, plaintext );
mbedtls_gcm_free( &gcm );
} }
else if( alg == PSA_ALG_CCM ) else if( alg == PSA_ALG_CCM )
{ {
mbedtls_ccm_context ccm; status = psa_aead_unpadded_locate_tag( operation.tag_length,
if( nonce_length < 7 || nonce_length > 13 )
return( PSA_ERROR_INVALID_ARGUMENT );
tag_length = 16;
status = psa_aead_unpadded_locate_tag( tag_length,
ciphertext, ciphertext_length, ciphertext, ciphertext_length,
plaintext_size, &tag ); plaintext_size, &tag );
if( status != PSA_SUCCESS ) if( status != PSA_SUCCESS )
return( status ); goto exit;
mbedtls_ccm_init( &ccm ); status = mbedtls_to_psa_error(
ret = mbedtls_ccm_setkey( &ccm, cipher_id, mbedtls_ccm_auth_decrypt( &operation.ctx.ccm,
slot->data.raw.data, ciphertext_length - operation.tag_length,
(unsigned int) key_bits ); nonce, nonce_length,
if( ret != 0 ) additional_data,
{ additional_data_length,
mbedtls_ccm_free( &ccm ); ciphertext, plaintext,
return( mbedtls_to_psa_error( ret ) ); tag, operation.tag_length ) );
}
ret = mbedtls_ccm_auth_decrypt( &ccm, ciphertext_length - tag_length,
nonce, nonce_length,
additional_data,
additional_data_length,
ciphertext, plaintext,
tag, tag_length );
mbedtls_ccm_free( &ccm );
} }
else else
{ {
return( PSA_ERROR_NOT_SUPPORTED ); return( PSA_ERROR_NOT_SUPPORTED );
} }
if( ret != 0 ) if( status != PSA_SUCCESS && plaintext_size != 0 )
{ memset( plaintext, 0, plaintext_size );
/* If plaintext_size is 0 then plaintext may be NULL and then the
* call to memset has undefined behavior. */
if( plaintext_size != 0 )
memset( plaintext, 0, plaintext_size );
}
else
*plaintext_length = ciphertext_length - tag_length;
return( mbedtls_to_psa_error( ret ) ); exit:
psa_aead_abort( &operation, alg );
if( status == PSA_SUCCESS )
*plaintext_length = ciphertext_length - operation.tag_length;
return( status );
} }