From ef0cb407361cc42cacf3d9a8c74ddf028ffd966e Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 16:55:59 +0200 Subject: [PATCH 01/18] Fix bug in exercise_mac_key that almost always broke the SIGN case That case isn't used in the test suite yet. --- tests/suites/test_suite_psa_crypto.function | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index e9efb3a0a..37d6aca3f 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -143,7 +143,7 @@ static int exercise_mac_key( psa_key_slot_t key, TEST_ASSERT( psa_mac_update( &operation, input, sizeof( input ) ) == PSA_SUCCESS ); TEST_ASSERT( psa_mac_sign_finish( &operation, - mac, sizeof( input ), + mac, sizeof( mac ), &mac_length ) == PSA_SUCCESS ); } From f64ee8a7f10f05c5b624afd817aef7ef7d1d5adb Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 16:57:40 +0200 Subject: [PATCH 02/18] Fix "unknown MAC algorithm" to actually use a MAC algorithm --- tests/suites/test_suite_psa_crypto.data | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 27c15389f..b44c347e2 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -294,7 +294,7 @@ mac_setup:PSA_KEY_TYPE_AES:"000102030405060708090a0b0c0d0e0f":PSA_ALG_CMAC:PSA_S PSA MAC setup: bad algorithm (unknown MAC algorithm) depends_on:MBEDTLS_MD_C -mac_setup:PSA_KEY_TYPE_RAW_DATA:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_NOT_SUPPORTED +mac_setup:PSA_KEY_TYPE_HMAC:"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f":PSA_ALG_HMAC(0):PSA_ERROR_NOT_SUPPORTED PSA MAC setup: bad algorithm (not a MAC algorithm) depends_on:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CBC From 94e44540ff9eb42fb4e63f371335d3e7a36c32e4 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 16:58:43 +0200 Subject: [PATCH 03/18] psa_hash_update: robustify the case length=0 Don't require hash implementations to behave correctly on a zero-length input, which may have an invalid pointer. --- library/psa_crypto.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index eb140ea2c..47605d432 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1018,6 +1018,12 @@ psa_status_t psa_hash_update( psa_hash_operation_t *operation, size_t input_length ) { int ret; + + /* Don't require hash implementations to behave correctly on a + * zero-length input, which may have an invalid pointer. */ + if( input_length == 0 ) + return( PSA_SUCCESS ); + switch( operation->alg ) { #if defined(MBEDTLS_MD2_C) @@ -1068,6 +1074,7 @@ psa_status_t psa_hash_update( psa_hash_operation_t *operation, ret = MBEDTLS_ERR_MD_BAD_INPUT_DATA; break; } + if( ret != 0 ) psa_hash_abort( operation ); return( mbedtls_to_psa_error( ret ) ); From 01126fae7fa4c4d0584a852818cf1087b74d367f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:04:55 +0200 Subject: [PATCH 04/18] Isolate HMAC code into its own functions Create internal functions for HMAC operations. This prepares for two things: separating crypto-sensitive code from argument decoding and validation, and using HMAC for other purposes than a MAC inside the library (e.g. HMAC_DRBG, HKDF). No intended observable behavior change in this commit. --- library/psa_crypto.c | 151 ++++++++++++++++++++++--------------------- 1 file changed, 79 insertions(+), 72 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 47605d432..de1f772ea 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1332,6 +1332,14 @@ static psa_status_t psa_mac_init( psa_mac_operation_t *operation, return( status ); } +#if defined(MBEDTLS_MD_C) +static psa_status_t psa_hmac_abort_internal( psa_hmac_internal_data *hmac ) +{ + mbedtls_zeroize( hmac->opad, sizeof( hmac->opad ) ); + return( psa_hash_abort( &hmac->hash_ctx ) ); +} +#endif /* MBEDTLS_MD_C */ + psa_status_t psa_mac_abort( psa_mac_operation_t *operation ) { if( operation->alg == 0 ) @@ -1352,12 +1360,7 @@ psa_status_t psa_mac_abort( psa_mac_operation_t *operation ) #if defined(MBEDTLS_MD_C) if( PSA_ALG_IS_HMAC( operation->alg ) ) { - size_t block_size = - psa_get_hash_block_size( PSA_ALG_HMAC_HASH( operation->alg ) ); - if( block_size == 0 ) - goto bad_state; - psa_hash_abort( &operation->ctx.hmac.hash_ctx ); - mbedtls_zeroize( operation->ctx.hmac.opad, block_size ); + psa_hmac_abort_internal( &operation->ctx.hmac ); } else #endif /* MBEDTLS_MD_C */ @@ -1407,43 +1410,33 @@ static int psa_cmac_setup( psa_mac_operation_t *operation, #endif /* MBEDTLS_CMAC_C */ #if defined(MBEDTLS_MD_C) -static int psa_hmac_setup( psa_mac_operation_t *operation, - psa_key_type_t key_type, - key_slot_t *slot, - psa_algorithm_t alg ) +static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, + const uint8_t *key, + size_t key_length, + psa_algorithm_t hash_alg ) { unsigned char ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE]; - unsigned char *opad = operation->ctx.hmac.opad; size_t i; - size_t block_size = - psa_get_hash_block_size( PSA_ALG_HMAC_HASH( alg ) ); - unsigned int digest_size = - PSA_HASH_SIZE( PSA_ALG_HMAC_HASH( alg ) ); - size_t key_length = slot->data.raw.bytes; + size_t block_size = psa_get_hash_block_size( hash_alg ); psa_status_t status; - if( block_size == 0 || digest_size == 0 ) + if( block_size == 0 ) return( PSA_ERROR_NOT_SUPPORTED ); - if( key_type != PSA_KEY_TYPE_HMAC ) - return( PSA_ERROR_INVALID_ARGUMENT ); - - operation->mac_size = digest_size; /* The hash was started earlier in psa_mac_init. */ if( key_length > block_size ) { - status = psa_hash_update( &operation->ctx.hmac.hash_ctx, - slot->data.raw.data, slot->data.raw.bytes ); + status = psa_hash_update( &hmac->hash_ctx, key, key_length ); if( status != PSA_SUCCESS ) return( status ); - status = psa_hash_finish( &operation->ctx.hmac.hash_ctx, + status = psa_hash_finish( &hmac->hash_ctx, ipad, sizeof( ipad ), &key_length ); if( status != PSA_SUCCESS ) return( status ); } else - memcpy( ipad, slot->data.raw.data, slot->data.raw.bytes ); + memcpy( ipad, key, key_length ); /* ipad contains the key followed by garbage. Xor and fill with 0x36 * to create the ipad value. */ @@ -1454,22 +1447,17 @@ static int psa_hmac_setup( psa_mac_operation_t *operation, /* Copy the key material from ipad to opad, flipping the requisite bits, * and filling the rest of opad with the requisite constant. */ for( i = 0; i < key_length; i++ ) - opad[i] = ipad[i] ^ 0x36 ^ 0x5C; - memset( opad + key_length, 0x5C, block_size - key_length ); + hmac->opad[i] = ipad[i] ^ 0x36 ^ 0x5C; + memset( hmac->opad + key_length, 0x5C, block_size - key_length ); - status = psa_hash_setup( &operation->ctx.hmac.hash_ctx, - PSA_ALG_HMAC_HASH( alg ) ); + status = psa_hash_setup( &hmac->hash_ctx, hash_alg ); if( status != PSA_SUCCESS ) goto cleanup; - status = psa_hash_update( &operation->ctx.hmac.hash_ctx, ipad, - block_size ); + status = psa_hash_update( &hmac->hash_ctx, ipad, block_size ); cleanup: mbedtls_zeroize( ipad, key_length ); - /* opad is in the context. It needs to stay in memory if this function - * succeeds, and it will be wiped by psa_mac_abort() called from - * psa_mac_setup in the error case. */ return( status ); } @@ -1517,7 +1505,22 @@ static psa_status_t psa_mac_setup( psa_mac_operation_t *operation, #if defined(MBEDTLS_MD_C) if( PSA_ALG_IS_HMAC( alg ) ) { - status = psa_hmac_setup( operation, slot->type, slot, alg ); + psa_algorithm_t hash_alg = PSA_ALG_HMAC_HASH( alg ); + if( hash_alg == 0 ) + { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + if( slot->type != PSA_KEY_TYPE_HMAC ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + status = psa_hmac_setup_internal( &operation->ctx.hmac, + slot->data.raw.data, + slot->data.raw.bytes, + hash_alg ); + operation->mac_size = PSA_HASH_SIZE( hash_alg ); } else #endif /* MBEDTLS_MD_C */ @@ -1591,12 +1594,49 @@ cleanup: return( status ); } +#if defined(MBEDTLS_MD_C) +static psa_status_t psa_hmac_finish_internal( psa_hmac_internal_data *hmac, + uint8_t *mac, + size_t mac_size ) +{ + unsigned char tmp[MBEDTLS_MD_MAX_SIZE]; + psa_algorithm_t hash_alg = hmac->hash_ctx.alg; + size_t hash_size = 0; + size_t block_size = psa_get_hash_block_size( hash_alg ); + psa_status_t status; + + if( block_size == 0 ) + return( PSA_ERROR_NOT_SUPPORTED ); + + status = psa_hash_finish( &hmac->hash_ctx, tmp, sizeof( tmp ), &hash_size ); + if( status != PSA_SUCCESS ) + return( status ); + /* From here on, tmp needs to be wiped. */ + + status = psa_hash_setup( &hmac->hash_ctx, hash_alg ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &hmac->hash_ctx, hmac->opad, block_size ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_update( &hmac->hash_ctx, tmp, hash_size ); + if( status != PSA_SUCCESS ) + goto exit; + + status = psa_hash_finish( &hmac->hash_ctx, mac, mac_size, &hash_size ); + +exit: + mbedtls_zeroize( tmp, hash_size ); + return( status ); +} +#endif /* MBEDTLS_MD_C */ + static psa_status_t psa_mac_finish_internal( psa_mac_operation_t *operation, uint8_t *mac, size_t mac_size ) { - psa_status_t status; - if( ! operation->key_set ) return( PSA_ERROR_BAD_STATE ); if( operation->iv_required && ! operation->iv_set ) @@ -1616,41 +1656,8 @@ static psa_status_t psa_mac_finish_internal( psa_mac_operation_t *operation, #if defined(MBEDTLS_MD_C) if( PSA_ALG_IS_HMAC( operation->alg ) ) { - unsigned char tmp[MBEDTLS_MD_MAX_SIZE]; - unsigned char *opad = operation->ctx.hmac.opad; - size_t hash_size = 0; - size_t block_size = - psa_get_hash_block_size( PSA_ALG_HMAC_HASH( operation->alg ) ); - - if( block_size == 0 ) - return( PSA_ERROR_NOT_SUPPORTED ); - - status = psa_hash_finish( &operation->ctx.hmac.hash_ctx, tmp, - sizeof( tmp ), &hash_size ); - if( status != PSA_SUCCESS ) - return( status ); - /* From here on, tmp needs to be wiped. */ - - status = psa_hash_setup( &operation->ctx.hmac.hash_ctx, - PSA_ALG_HMAC_HASH( operation->alg ) ); - if( status != PSA_SUCCESS ) - goto hmac_cleanup; - - status = psa_hash_update( &operation->ctx.hmac.hash_ctx, opad, - block_size ); - if( status != PSA_SUCCESS ) - goto hmac_cleanup; - - status = psa_hash_update( &operation->ctx.hmac.hash_ctx, tmp, - hash_size ); - if( status != PSA_SUCCESS ) - goto hmac_cleanup; - - status = psa_hash_finish( &operation->ctx.hmac.hash_ctx, mac, - mac_size, &hash_size ); - hmac_cleanup: - mbedtls_zeroize( tmp, hash_size ); - return( status ); + return( psa_hmac_finish_internal( &operation->ctx.hmac, + mac, mac_size ) ); } else #endif /* MBEDTLS_MD_C */ From 9688997301a315933290ed4d55ec407d045ac324 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:07:03 +0200 Subject: [PATCH 05/18] MAC setup: support 0-length HMAC key Avoid undefined behavior when using a 0-length HMAC key (Asan complained). --- library/psa_crypto.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index de1f772ea..a0f278086 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1435,7 +1435,11 @@ static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, if( status != PSA_SUCCESS ) return( status ); } - else + /* A 0-length key is not commonly used in HMAC when used as a MAC, + * but it is permitted. It is common when HMAC is used in HKDF, for + * example. Don't call `memcpy` in the 0-length because `key` could be + * an invalid pointer which would make the behavior undefined. */ + else if( key_length != 0 ) memcpy( ipad, key, key_length ); /* ipad contains the key followed by garbage. Xor and fill with 0x36 From b8be288374a462738613861d776362ee11f9b854 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 17 Jul 2018 16:24:34 +0200 Subject: [PATCH 06/18] psa_hmac_setup_internal: add some missing cleanup on failure Clean ipad if hashing the key failed. --- library/psa_crypto.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index a0f278086..f157f4506 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1429,11 +1429,11 @@ static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, { status = psa_hash_update( &hmac->hash_ctx, key, key_length ); if( status != PSA_SUCCESS ) - return( status ); + goto cleanup; status = psa_hash_finish( &hmac->hash_ctx, ipad, sizeof( ipad ), &key_length ); if( status != PSA_SUCCESS ) - return( status ); + goto cleanup; } /* A 0-length key is not commonly used in HMAC when used as a MAC, * but it is permitted. It is common when HMAC is used in HKDF, for From ff94abdf3a95d08921c31281f82d8f7bfda9db15 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:07:52 +0200 Subject: [PATCH 07/18] Make psa_hmac_setup_internal more standalone Call psa_hash_setup in psa_hmac_setup_internal rather than psa_mac_init. This makes it easier to use psa_hmac_setup_internal on its own (for the sake of using HMAC internally inside the library). --- library/psa_crypto.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index f157f4506..b1555631e 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1317,8 +1317,9 @@ static psa_status_t psa_mac_init( psa_mac_operation_t *operation, #if defined(MBEDTLS_MD_C) if( PSA_ALG_IS_HMAC( operation->alg ) ) { - status = psa_hash_setup( &operation->ctx.hmac.hash_ctx, - PSA_ALG_HMAC_HASH( alg ) ); + /* We'll set up the hash operation later in psa_hmac_setup_internal. */ + operation->ctx.hmac.hash_ctx.alg = 0; + status = PSA_SUCCESS; } else #endif /* MBEDTLS_MD_C */ @@ -1423,8 +1424,10 @@ static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, if( block_size == 0 ) return( PSA_ERROR_NOT_SUPPORTED ); + status = psa_hash_setup( &hmac->hash_ctx, hash_alg ); + if( status != PSA_SUCCESS ) + return( status ); - /* The hash was started earlier in psa_mac_init. */ if( key_length > block_size ) { status = psa_hash_update( &hmac->hash_ctx, key, key_length ); From 1e6bfdff5eb0e3d0b50acd3416ce4545aa5c3d46 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 17 Jul 2018 16:22:47 +0200 Subject: [PATCH 08/18] psa_hmac_setup_internal: fix double call of psa_hash_setup In the common case (key no longer than the block size), psa_hash_setup was being called twice in succession. With current implementations this is just a small performance loss, but potentially with alternative implementations this could have lead to a memory leak. --- library/psa_crypto.c | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index b1555631e..7ea614f45 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1424,12 +1424,11 @@ static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, if( block_size == 0 ) return( PSA_ERROR_NOT_SUPPORTED ); - status = psa_hash_setup( &hmac->hash_ctx, hash_alg ); - if( status != PSA_SUCCESS ) - return( status ); - if( key_length > block_size ) { + status = psa_hash_setup( &hmac->hash_ctx, hash_alg ); + if( status != PSA_SUCCESS ) + goto cleanup; status = psa_hash_update( &hmac->hash_ctx, key, key_length ); if( status != PSA_SUCCESS ) goto cleanup; From 9aa369eafb2a19b84d0f46e837d8ec1e73f0dd9a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 16 Jul 2018 00:36:29 +0200 Subject: [PATCH 09/18] HMAC: improve robustness checks on hash/block size In psa_mac_setup and psa_hmac_setup_internal, perform a sanity check on the hash size and the hash block size respectively. These sanity checks should only trigger on an incompletely or incorrectly implemented hash function. Remove the check on the block size in psa_hmac_finish_internal because at this point it has already been checked and used. --- library/psa_crypto.c | 28 +++++++++++++++++++++++----- 1 file changed, 23 insertions(+), 5 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 7ea614f45..67536f2ac 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1418,10 +1418,21 @@ static psa_status_t psa_hmac_setup_internal( psa_hmac_internal_data *hmac, { unsigned char ipad[PSA_HMAC_MAX_HASH_BLOCK_SIZE]; size_t i; + size_t hash_size = PSA_HASH_SIZE( hash_alg ); size_t block_size = psa_get_hash_block_size( hash_alg ); psa_status_t status; - if( block_size == 0 ) + /* Sanity checks on block_size, to guarantee that there won't be a buffer + * overflow below. This should never trigger if the hash algorithm + * is implemented correctly. */ + /* The size checks against the ipad and opad buffers cannot be written + * `block_size > sizeof( ipad ) || block_size > sizeof( hmac->opad )` + * because that triggers -Wlogical-op on GCC 7.3. */ + if( block_size > sizeof( ipad ) ) + return( PSA_ERROR_NOT_SUPPORTED ); + if( block_size > sizeof( hmac->opad ) ) + return( PSA_ERROR_NOT_SUPPORTED ); + if( block_size < hash_size ) return( PSA_ERROR_NOT_SUPPORTED ); if( key_length > block_size ) @@ -1517,16 +1528,26 @@ static psa_status_t psa_mac_setup( psa_mac_operation_t *operation, status = PSA_ERROR_NOT_SUPPORTED; goto exit; } + + operation->mac_size = PSA_HASH_SIZE( hash_alg ); + /* Sanity check. This shouldn't fail on a valid configuration. */ + if( operation->mac_size == 0 || + operation->mac_size > sizeof( operation->ctx.hmac.opad ) ) + { + status = PSA_ERROR_NOT_SUPPORTED; + goto exit; + } + if( slot->type != PSA_KEY_TYPE_HMAC ) { status = PSA_ERROR_INVALID_ARGUMENT; goto exit; } + status = psa_hmac_setup_internal( &operation->ctx.hmac, slot->data.raw.data, slot->data.raw.bytes, hash_alg ); - operation->mac_size = PSA_HASH_SIZE( hash_alg ); } else #endif /* MBEDTLS_MD_C */ @@ -1611,9 +1632,6 @@ static psa_status_t psa_hmac_finish_internal( psa_hmac_internal_data *hmac, size_t block_size = psa_get_hash_block_size( hash_alg ); psa_status_t status; - if( block_size == 0 ) - return( PSA_ERROR_NOT_SUPPORTED ); - status = psa_hash_finish( &hmac->hash_ctx, tmp, sizeof( tmp ), &hash_size ); if( status != PSA_SUCCESS ) return( status ); From eab56e4159cdfb7ca7a033735025aadb0ad1a627 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:12:33 +0200 Subject: [PATCH 10/18] Add generator API Add an API for byte generators: psa_crypto_generator_t, PSA_CRYPTO_GENERATOR_INIT, psa_crypto_generator_init, psa_get_generator_capacity, psa_generator_read, psa_generator_import_key, psa_generator_abort. This commit does not yet implement any generator algorithm, it only provides the framework. This code may not compile with -Wunused. --- include/psa/crypto.h | 177 +++++++++++++++++++++++++++++++++++- include/psa/crypto_struct.h | 21 +++++ library/psa_crypto.c | 100 +++++++++++++++++++- 3 files changed, 296 insertions(+), 2 deletions(-) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 8ac817a6e..4dbbdedcd 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -285,12 +285,18 @@ typedef int32_t psa_status_t; * depend on the validity of the padding. */ #define PSA_ERROR_INVALID_PADDING ((psa_status_t)16) +/** The generator has insufficient capacity left. + * + * Once a function returns this error, attempts to read from the + * generator will always return this error. */ +#define PSA_ERROR_INSUFFICIENT_CAPACITY ((psa_status_t)17) + /** An error occurred that does not correspond to any defined * failure cause. * * Implementations may use this error code if none of the other standard * error codes are applicable. */ -#define PSA_ERROR_UNKNOWN_ERROR ((psa_status_t)17) +#define PSA_ERROR_UNKNOWN_ERROR ((psa_status_t)18) /** * \brief Library initialization. @@ -2440,6 +2446,175 @@ psa_status_t psa_asymmetric_decrypt(psa_key_slot_t key, /**@}*/ +/** \defgroup generation Generators + * @{ + */ + +/** The type of the state data structure for generators. + * + * Before calling any function on a generator, the application must + * initialize it by any of the following means: + * - Set the structure to all-bits-zero, for example: + * \code + * psa_crypto_generator_t generator; + * memset(&generator, 0, sizeof(generator)); + * \endcode + * - Initialize the structure to logical zero values, for example: + * \code + * psa_crypto_generator_t generator = {0}; + * \endcode + * - Initialize the structure to the initializer #PSA_CRYPTO_GENERATOR_INIT, + * for example: + * \code + * psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + * \endcode + * - Assign the result of the function psa_crypto_generator_init() + * to the structure, for example: + * \code + * psa_crypto_generator_t generator; + * generator = psa_crypto_generator_init(); + * \endcode + * + * This is an implementation-defined \c struct. Applications should not + * make any assumptions about the content of this structure except + * as directed by the documentation of a specific implementation. + */ +typedef struct psa_crypto_generator_s psa_crypto_generator_t; + +/** \def PSA_CRYPTO_GENERATOR_INIT + * + * This macro returns a suitable initializer for a generator object + * of type #psa_crypto_generator_t. + */ +#ifdef __DOXYGEN_ONLY__ +/* This is an example definition for documentation purposes. + * Implementations should define a suitable value in `crypto_struct.h`. + */ +#define PSA_CRYPTO_GENERATOR_INIT {0} +#endif + +/** Return an initial value for a generator object. + */ +static psa_crypto_generator_t psa_crypto_generator_init(void); + +/** Retrieve the current capacity of a generator. + * + * The capacity of a generator is the maximum number of bytes that it can + * return. Reading *N* bytes from a generator reduces its capacity by *N*. + * + * \param[in] generator The generator to query. + * \param[out] capacity On success, the capacity of the generator. + * + * \retval PSA_SUCCESS + * \retval PSA_ERROR_BAD_STATE + * \retval PSA_ERROR_COMMUNICATION_FAILURE + */ +psa_status_t psa_get_generator_capacity(const psa_crypto_generator_t *generator, + size_t *capacity); + +/** Read some data from a generator. + * + * This function reads and returns a sequence of bytes from a generator. + * The data that is read is discarded from the generator. The generator's + * capacity is decreased by the number of bytes read. + * + * \param[in,out] generator The generator object to read from. + * \param[out] output Buffer where the generator output will be + * written. + * \param output_length Number of bytes to output. + * + * \retval PSA_SUCCESS + * \retval PSA_ERROR_INSUFFICIENT_CAPACITY + * There were fewer than \p output_length bytes + * in the generator. Note that in this case, no + * output is written to the output buffer. + * The generator's capacity is set to 0, thus + * subsequent calls to this function will not + * succeed, even with a smaller output buffer. + * \retval PSA_ERROR_BAD_STATE + * \retval PSA_ERROR_INSUFFICIENT_MEMORY + * \retval PSA_ERROR_COMMUNICATION_FAILURE + * \retval PSA_ERROR_HARDWARE_FAILURE + * \retval PSA_ERROR_TAMPERING_DETECTED + */ +psa_status_t psa_generator_read(psa_crypto_generator_t *generator, + uint8_t *output, + size_t output_length); + +/** Create a symmetric key from data read from a generator. + * + * This function reads a sequence of bytes from a generator and imports + * these bytes as a key. + * The data that is read is discarded from the generator. The generator's + * capacity is decreased by the number of bytes read. + * + * This function is equivalent to calling #psa_generator_read and + * passing the resulting output to #psa_import_key, but + * if the implementation provides an isolation boundary then + * the key material is not exposed outside the isolation boundary. + * + * \param key Slot where the key will be stored. This must be a + * valid slot for a key of the chosen type. It must + * be unoccupied. + * \param type Key type (a \c PSA_KEY_TYPE_XXX value). + * This must be a symmetric key type. + * \param bits Key size in bits. + * \param[in,out] generator The generator object to read from. + * + * \retval PSA_SUCCESS + * Success. + * \retval PSA_ERROR_INSUFFICIENT_CAPACITY + * There were fewer than \p output_length bytes + * in the generator. Note that in this case, no + * output is written to the output buffer. + * The generator's capacity is set to 0, thus + * subsequent calls to this function will not + * succeed, even with a smaller output buffer. + * \retval PSA_ERROR_NOT_SUPPORTED + * The key type or key size is not supported, either by the + * implementation in general or in this particular slot. + * \retval PSA_ERROR_BAD_STATE + * \retval PSA_ERROR_INVALID_ARGUMENT + * The key slot is invalid. + * \retval PSA_ERROR_OCCUPIED_SLOT + * There is already a key in the specified slot. + * \retval PSA_ERROR_INSUFFICIENT_MEMORY + * \retval PSA_ERROR_INSUFFICIENT_STORAGE + * \retval PSA_ERROR_COMMUNICATION_FAILURE + * \retval PSA_ERROR_HARDWARE_FAILURE + * \retval PSA_ERROR_TAMPERING_DETECTED + */ +psa_status_t psa_generator_import_key(psa_key_slot_t key, + psa_key_type_t type, + size_t bits, + psa_crypto_generator_t *generator); + +/** Abort a generator. + * + * Once a generator has been aborted, its capacity is zero. + * Aborting a generator frees all associated resources except for the + * \c generator structure itself. + * + * This function may be called at any time as long as the generator + * object has been initialized to #PSA_CRYPTO_GENERATOR_INIT, to + * psa_crypto_generator_init() or a zero value. In particular, it is valid + * to call psa_generator_abort() twice, or to call psa_generator_abort() + * on a generator that has not been set up. + * + * Once aborted, the generator object may be called. + * + * \param[in,out] generator The generator to abort. + * + * \retval PSA_SUCCESS + * \retval PSA_ERROR_BAD_STATE + * \retval PSA_ERROR_COMMUNICATION_FAILURE + * \retval PSA_ERROR_HARDWARE_FAILURE + * \retval PSA_ERROR_TAMPERING_DETECTED + */ +psa_status_t psa_generator_abort(psa_crypto_generator_t *generator); + +/**@}*/ + /** \defgroup generation Key generation * @{ */ diff --git a/include/psa/crypto_struct.h b/include/psa/crypto_struct.h index 85c997485..27a9f1efc 100644 --- a/include/psa/crypto_struct.h +++ b/include/psa/crypto_struct.h @@ -130,6 +130,27 @@ struct psa_cipher_operation_s } ctx; }; +struct psa_crypto_generator_s +{ + psa_algorithm_t alg; + size_t capacity; + union + { + struct + { + uint8_t *data; + size_t size; + } buffer; + } ctx; +}; + +#define PSA_CRYPTO_GENERATOR_INIT {0, 0, {{0, 0}}} +static inline struct psa_crypto_generator_s psa_crypto_generator_init( void ) +{ + const struct psa_crypto_generator_s v = PSA_CRYPTO_GENERATOR_INIT; + return( v ); +} + struct psa_key_policy_s { psa_key_usage_t usage; diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 67536f2ac..6cc42c6ba 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -2988,7 +2988,105 @@ psa_status_t psa_aead_decrypt( psa_key_slot_t key, /****************************************************************/ -/* Key generation */ +/* Generators */ +/****************************************************************/ + +psa_status_t psa_generator_abort( psa_crypto_generator_t *generator ) +{ + psa_status_t status = PSA_SUCCESS; + if( generator->alg == 0 ) + { + /* The object has (apparently) been initialized but it is not + * in use. It's ok to call abort on such an object, and there's + * nothing to do. */ + } + else + { + status = PSA_ERROR_BAD_STATE; + } + memset( generator, 0, sizeof( *generator ) ); + return( status ); +} + + +psa_status_t psa_get_generator_capacity(const psa_crypto_generator_t *generator, + size_t *capacity) +{ + *capacity = generator->capacity; + return( PSA_SUCCESS ); +} + +psa_status_t psa_generator_read( psa_crypto_generator_t *generator, + uint8_t *output, + size_t output_length ) +{ + psa_status_t status; + + if( output_length > generator->capacity ) + { + generator->capacity = 0; + /* Go through the error path to wipe all confidential data now + * that the generator object is useless. */ + status = PSA_ERROR_INSUFFICIENT_CAPACITY; + goto exit; + } + if( output_length == 0 && + generator->capacity == 0 && generator->alg == 0 ) + { + /* Edge case: this is a blank or finished generator, and 0 + * bytes were requested. The right error in this case could + * be either INSUFFICIENT_CAPACITY or BAD_STATE. Return + * INSUFFICIENT_CAPACITY, which is right for a finished + * generator, for consistency with the case when + * output_length > 0. */ + return( PSA_ERROR_INSUFFICIENT_CAPACITY ); + } + generator->capacity -= output_length; + + { + return( PSA_ERROR_BAD_STATE ); + } + +exit: + if( status != PSA_SUCCESS ) + { + psa_generator_abort( generator ); + memset( output, '!', output_length ); + } + return( status ); +} + +psa_status_t psa_generator_import_key( psa_key_slot_t key, + psa_key_type_t type, + size_t bits, + psa_crypto_generator_t *generator ) +{ + uint8_t *data = NULL; + size_t bytes = PSA_BITS_TO_BYTES( bits ); + psa_status_t status; + + if( ! key_type_is_raw_bytes( type ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + if( bits % 8 != 0 ) + return( PSA_ERROR_INVALID_ARGUMENT ); + data = mbedtls_calloc( 1, bytes ); + if( data == NULL ) + return( PSA_ERROR_INSUFFICIENT_MEMORY ); + + status = psa_generator_read( generator, data, bytes ); + if( status != PSA_SUCCESS ) + goto exit; + status = psa_import_key( key, type, data, bytes ); + +exit: + mbedtls_free( data ); + return( status ); +} + + + +/****************************************************************/ +/* Random generation */ /****************************************************************/ psa_status_t psa_generate_random( uint8_t *output, From ea0fb4975c3be493773b9be7d57400ef9d99a107 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:17:20 +0200 Subject: [PATCH 11/18] Add framework for simple key derivation New key type PSA_KEY_TYPE_DERIVE. New usage flag PSA_KEY_USAGE_DERIVE. New function psa_key_derivation. No key derivation algorithm is implemented yet. The code may not compile with -Wunused. Write some unit test code for psa_key_derivation. Most of it cannot be used yet due to the lack of a key derivation algorithm. --- include/psa/crypto.h | 62 +++++++++++ library/psa_crypto.c | 49 ++++++++- tests/suites/test_suite_psa_crypto.data | 4 + tests/suites/test_suite_psa_crypto.function | 111 ++++++++++++++++++++ 4 files changed, 224 insertions(+), 2 deletions(-) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 4dbbdedcd..9165463f5 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -366,6 +366,13 @@ typedef uint32_t psa_key_type_t; * \c alg is the HMAC algorithm or the underlying hash algorithm. */ #define PSA_KEY_TYPE_HMAC ((psa_key_type_t)0x02000001) +/** A secret for key derivation. + * + * The key policy determines which key derivation algorithm the key + * can be used for. + */ +#define PSA_KEY_TYPE_DERIVE ((psa_key_type_t)0x02000101) + /** Key for an cipher, AEAD or MAC algorithm based on the AES block cipher. * * The size of the key can be 16 bytes (AES-128), 24 bytes (AES-192) or @@ -1194,6 +1201,10 @@ typedef uint32_t psa_key_usage_t; */ #define PSA_KEY_USAGE_VERIFY ((psa_key_usage_t)0x00000800) +/** Whether the key may be used to derive other keys. + */ +#define PSA_KEY_USAGE_DERIVE ((psa_key_usage_t)0x00001000) + /** The type of the key policy data structure. * * This is an implementation-defined \c struct. Applications should not @@ -2615,6 +2626,57 @@ psa_status_t psa_generator_abort(psa_crypto_generator_t *generator); /**@}*/ +/** \defgroup derivation Key derivation + * @{ + */ + +/** Set up a key derivation operation. + * + * A key derivation algorithm takes three inputs: a secret input \p key and + * two non-secret inputs \p label and p salt. + * The result of this function is a byte generator which can + * be used to produce keys and other cryptographic material. + * + * The role of \p label and \p salt is as follows: + * + * \param[in,out] generator The generator object to set up. It must + * have been initialized to . + * \param key Slot containing the secret key to use. + * \param alg The key derivation algorithm to compute + * (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_KEY_DERIVATION(\p alg) is true). + * \param[in] salt Salt to use. + * \param salt_length Size of the \p salt buffer in bytes. + * \param[in] label Label to use. + * \param label_length Size of the \p label buffer in bytes. + * \param capacity The maximum number of bytes that the + * generator will be able to provide. + * + * \retval #PSA_SUCCESS + * Success. + * \retval #PSA_ERROR_EMPTY_SLOT + * \retval #PSA_ERROR_NOT_PERMITTED + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \c key is not compatible with \c alg, + * or \p capacity is too large for the specified algorithm and key. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \c alg is not supported or is not a key derivation algorithm. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_COMMUNICATION_FAILURE + * \retval #PSA_ERROR_HARDWARE_FAILURE + * \retval #PSA_ERROR_TAMPERING_DETECTED + */ +psa_status_t psa_key_derivation(psa_crypto_generator_t *generator, + psa_key_type_t key, + psa_algorithm_t alg, + const uint8_t *salt, + size_t salt_length, + const uint8_t *label, + size_t label_length, + size_t capacity); + +/**@}*/ + /** \defgroup generation Key generation * @{ */ diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 6cc42c6ba..cc59ca800 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -498,8 +498,9 @@ static psa_status_t prepare_raw_data_slot( psa_key_type_t type, break; #if defined(MBEDTLS_MD_C) case PSA_KEY_TYPE_HMAC: - break; #endif + case PSA_KEY_TYPE_DERIVE: + break; #if defined(MBEDTLS_AES_C) case PSA_KEY_TYPE_AES: if( bits != 128 && bits != 192 && bits != 256 ) @@ -2651,7 +2652,8 @@ psa_status_t psa_set_key_policy( psa_key_slot_t key, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | PSA_KEY_USAGE_SIGN | - PSA_KEY_USAGE_VERIFY ) ) != 0 ) + PSA_KEY_USAGE_VERIFY | + PSA_KEY_USAGE_DERIVE ) ) != 0 ) return( PSA_ERROR_INVALID_ARGUMENT ); slot->policy = *policy; @@ -3085,6 +3087,49 @@ exit: +/****************************************************************/ +/* Key derivation */ +/****************************************************************/ + +psa_status_t psa_key_derivation( psa_crypto_generator_t *generator, + psa_key_type_t key, + psa_algorithm_t alg, + const uint8_t *salt, + size_t salt_length, + const uint8_t *label, + size_t label_length, + size_t capacity ) +{ + key_slot_t *slot; + psa_status_t status; + + if( generator->alg != 0 ) + return( PSA_ERROR_BAD_STATE ); + + status = psa_get_key_from_slot( key, &slot, PSA_KEY_USAGE_DERIVE, alg ); + if( status != PSA_SUCCESS ) + return( status ); + if( slot->type != PSA_KEY_TYPE_DERIVE ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + if( ! PSA_ALG_IS_KEY_DERIVATION( alg ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + { + return( PSA_ERROR_NOT_SUPPORTED ); + } + + /* Set generator->alg even on failure so that abort knows what to do. */ + generator->alg = alg; + if( status == PSA_SUCCESS ) + generator->capacity = capacity; + else + psa_generator_abort( generator ); + return( status ); +} + + + /****************************************************************/ /* Random generation */ /****************************************************************/ diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index b44c347e2..1113eec81 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -745,6 +745,10 @@ PSA decrypt: RSA PKCS#1 v1.5, input too large depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15 asymmetric_decrypt_fail:PSA_KEY_TYPE_RSA_KEYPAIR:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_ALG_RSA_PKCS1V15_CRYPT:"0099ffde2fcc00c9cc01972ebfa7779b298dbbaf7f50707a7405296dd2783456fc792002f462e760500e02afa25a859ace8701cb5d3b0262116431c43af8eb08f5a88301057cf1c156a2a5193c143e7a5b03fac132b7e89e6dcd8f4c82c9b28452329c260d30bc39b3816b7c46b41b37b4850d2ae74e729f99c6621fbbe2e46872":PSA_ERROR_INVALID_ARGUMENT + +PSA key derivation: not a key derivation algorithm +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HMAC(PSA_ALG_SHA_256):"":"":42:PSA_ERROR_INVALID_ARGUMENT PSA generate random: 0 bytes generate_random:0 diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 37d6aca3f..278554f75 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -357,6 +357,36 @@ exit: return( 0 ); } +static int exercise_key_derivation_key( psa_key_slot_t key, + psa_key_usage_t usage, + psa_algorithm_t alg ) +{ + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + unsigned char label[16] = "This is a label."; + size_t label_length = sizeof( label ); + unsigned char seed[16] = "abcdefghijklmnop"; + size_t seed_length = sizeof( seed ); + unsigned char output[1]; + + if( usage & PSA_KEY_USAGE_DERIVE ) + { + TEST_ASSERT( psa_key_derivation( &generator, + key, alg, + label, label_length, + seed, seed_length, + sizeof( output ) ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_read( &generator, + output, + sizeof( output ) ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_abort( &generator ) == PSA_SUCCESS ); + } + + return( 1 ); + +exit: + return( 0 ); +} + static int exercise_key( psa_key_slot_t slot, psa_key_usage_t usage, psa_algorithm_t alg ) @@ -374,6 +404,8 @@ static int exercise_key( psa_key_slot_t slot, ok = exercise_signature_key( slot, usage, alg ); else if( PSA_ALG_IS_ASYMMETRIC_ENCRYPTION( alg ) ) ok = exercise_asymmetric_encryption_key( slot, usage, alg ); + else if( PSA_ALG_IS_KEY_DERIVATION( alg ) ) + ok = exercise_key_derivation_key( slot, usage, alg ); else { char message[40]; @@ -657,6 +689,7 @@ void import_and_exercise_key( data_t *data, ( PSA_KEY_TYPE_IS_PUBLIC_KEY( type ) ? PSA_KEY_USAGE_ENCRYPT : PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT ) : + PSA_ALG_IS_KEY_DERIVATION( alg ) ? PSA_KEY_USAGE_DERIVE : 0 ); psa_key_policy_t policy; psa_key_type_t got_type; @@ -991,6 +1024,45 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void derive_key_policy( int policy_usage, + int policy_alg, + int key_type, + data_t *key_data, + int exercise_alg ) +{ + int key_slot = 1; + psa_key_policy_t policy; + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + psa_status_t status; + + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, policy_usage, policy_alg ); + TEST_ASSERT( psa_set_key_policy( key_slot, &policy ) == PSA_SUCCESS ); + + TEST_ASSERT( psa_import_key( key_slot, key_type, + key_data->x, key_data->len ) == PSA_SUCCESS ); + + status = psa_key_derivation( &generator, key_slot, + exercise_alg, + NULL, 0, + NULL, 0, + 1 ); + if( policy_alg == exercise_alg && + ( policy_usage & PSA_KEY_USAGE_DERIVE ) != 0 ) + TEST_ASSERT( status == PSA_SUCCESS ); + else + TEST_ASSERT( status == PSA_ERROR_NOT_PERMITTED ); + +exit: + psa_generator_abort( &generator ); + psa_destroy_key( key_slot ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void key_lifetime( int lifetime_arg ) { @@ -2372,6 +2444,45 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void derive_setup( int key_type_arg, + data_t *key_data, + int alg_arg, + data_t *salt, + data_t *label, + int requested_capacity_arg, + int expected_status_arg ) +{ + psa_key_slot_t slot = 1; + size_t key_type = key_type_arg; + psa_algorithm_t alg = alg_arg; + size_t requested_capacity = requested_capacity_arg; + psa_status_t expected_status = expected_status_arg; + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + psa_key_policy_t policy; + + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg ); + TEST_ASSERT( psa_set_key_policy( slot, &policy ) == PSA_SUCCESS ); + + TEST_ASSERT( psa_import_key( slot, key_type, + key_data->x, + key_data->len ) == PSA_SUCCESS ); + + TEST_ASSERT( psa_key_derivation( &generator, slot, alg, + salt->x, salt->len, + label->x, label->len, + requested_capacity ) == expected_status ); + +exit: + psa_generator_abort( &generator ); + psa_destroy_key( slot ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void generate_random( int bytes_arg ) { From bef7f14f8e251cf1f8f82ea520ea0e97a57efaef Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:22:21 +0200 Subject: [PATCH 12/18] Implement HKDF --- include/psa/crypto.h | 32 +++++++++ include/psa/crypto_struct.h | 17 +++++ library/psa_crypto.c | 133 ++++++++++++++++++++++++++++++++++++ 3 files changed, 182 insertions(+) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 9165463f5..47241a68c 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -965,6 +965,36 @@ typedef uint32_t psa_algorithm_t; #define PSA_ALG_IS_RSA_OAEP(alg) \ (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_RSA_OAEP_BASE) +#define PSA_ALG_HKDF_BASE ((psa_algorithm_t)0x30000100) +/** Macro to build an HKDF algorithm. + * + * For example, `PSA_ALG_HKDF(PSA_ALG_SHA256)` is HKDF using HMAC-SHA-256. + * + * \param hash_alg A hash algorithm (\c PSA_ALG_XXX value such that + * #PSA_ALG_IS_HASH(\p hash_alg) is true). + * + * \return The corresponding HKDF algorithm. + * \return Unspecified if \p alg is not a supported + * hash algorithm. + */ +#define PSA_ALG_HKDF(hash_alg) \ + (PSA_ALG_HKDF_BASE | ((hash_alg) & PSA_ALG_HASH_MASK)) +/** Whether the specified algorithm is an HKDF algorithm. + * + * HKDF is a family of key derivation algorithms that are based on a hash + * function and the HMAC construction. + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if \c alg is an HKDF algorithm, 0 otherwise. + * This macro may return either 0 or 1 if \c alg is not a supported + * key derivation algorithm identifier. + */ +#define PSA_ALG_IS_HKDF(alg) \ + (((alg) & ~PSA_ALG_HASH_MASK) == PSA_ALG_HKDF_BASE) +#define PSA_ALG_HKDF_GET_HASH(hkdf_alg) \ + (PSA_ALG_CATEGORY_HASH | ((hkdf_alg) & PSA_ALG_HASH_MASK)) + /**@}*/ /** \defgroup key_management Key management @@ -2638,6 +2668,8 @@ psa_status_t psa_generator_abort(psa_crypto_generator_t *generator); * be used to produce keys and other cryptographic material. * * The role of \p label and \p salt is as follows: + * - For HKDF (#PSA_ALG_HKDF), \p salt is the salt used in the "extract" step + * and \p label is the info string used in the "expand" step. * * \param[in,out] generator The generator object to set up. It must * have been initialized to . diff --git a/include/psa/crypto_struct.h b/include/psa/crypto_struct.h index 27a9f1efc..baf5b1495 100644 --- a/include/psa/crypto_struct.h +++ b/include/psa/crypto_struct.h @@ -130,6 +130,20 @@ struct psa_cipher_operation_s } ctx; }; +typedef struct +{ + uint8_t *info; + size_t info_length; + psa_hmac_internal_data hmac; + uint8_t prk[PSA_HASH_MAX_SIZE]; + uint8_t output_block[PSA_HASH_MAX_SIZE]; +#if PSA_HASH_MAX_SIZE > 0xff +#error "PSA_HASH_MAX_SIZE does not fit in uint8_t" +#endif + uint8_t offset_in_block; + uint8_t block_number; +} psa_hkdf_generator_t; + struct psa_crypto_generator_s { psa_algorithm_t alg; @@ -141,6 +155,9 @@ struct psa_crypto_generator_s uint8_t *data; size_t size; } buffer; +#if defined(MBEDTLS_MD_C) + psa_hkdf_generator_t hkdf; +#endif } ctx; }; diff --git a/library/psa_crypto.c b/library/psa_crypto.c index cc59ca800..9e8f90b54 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -3003,6 +3003,14 @@ psa_status_t psa_generator_abort( psa_crypto_generator_t *generator ) * nothing to do. */ } else +#if defined(MBEDTLS_MD_C) + if( PSA_ALG_IS_HKDF( generator->alg ) ) + { + mbedtls_free( generator->ctx.hkdf.info ); + status = psa_hmac_abort_internal( &generator->ctx.hkdf.hmac ); + } + else +#endif /* MBEDTLS_MD_C */ { status = PSA_ERROR_BAD_STATE; } @@ -3018,6 +3026,66 @@ psa_status_t psa_get_generator_capacity(const psa_crypto_generator_t *generator, return( PSA_SUCCESS ); } +#if defined(MBEDTLS_MD_C) +/* Read some bytes from an HKDF-based generator. This performs a chunk + * of the expand phase of the HKDF algorithm. */ +static psa_status_t psa_generator_hkdf_read( psa_hkdf_generator_t *hkdf, + psa_algorithm_t hash_alg, + uint8_t *output, + size_t output_length ) +{ + uint8_t hash_length = PSA_HASH_SIZE( hash_alg ); + psa_status_t status; + + while( output_length != 0 ) + { + /* Copy what remains of the current block */ + uint8_t n = hash_length - hkdf->offset_in_block; + if( n > output_length ) + n = (uint8_t) output_length; + memcpy( output, hkdf->output_block + hkdf->offset_in_block, n ); + output += n; + output_length -= n; + hkdf->offset_in_block += n; + if( output_length == 0 || hkdf->block_number == 0xff ) + break; + + /* We need a new block */ + ++hkdf->block_number; + hkdf->offset_in_block = 0; + status = psa_hmac_setup_internal( &hkdf->hmac, + hkdf->prk, hash_length, + hash_alg ); + if( status != PSA_SUCCESS ) + return( status ); + if( hkdf->block_number != 1 ) + { + status = psa_hash_update( &hkdf->hmac.hash_ctx, + hkdf->output_block, + hash_length ); + if( status != PSA_SUCCESS ) + return( status ); + } + status = psa_hash_update( &hkdf->hmac.hash_ctx, + hkdf->info, + hkdf->info_length ); + if( status != PSA_SUCCESS ) + return( status ); + status = psa_hash_update( &hkdf->hmac.hash_ctx, + &hkdf->block_number, 1 ); + if( status != PSA_SUCCESS ) + return( status ); + status = psa_hmac_finish_internal( &hkdf->hmac, + hkdf->output_block, + sizeof( hkdf->output_block ) ); + if( status != PSA_SUCCESS ) + return( status ); + } + + return( PSA_SUCCESS ); +} +#endif /* MBEDTLS_MD_C */ + psa_status_t psa_generator_read( psa_crypto_generator_t *generator, uint8_t *output, size_t output_length ) @@ -3045,6 +3113,15 @@ psa_status_t psa_generator_read( psa_crypto_generator_t *generator, } generator->capacity -= output_length; +#if defined(MBEDTLS_MD_C) + if( PSA_ALG_IS_HKDF( generator->alg ) ) + { + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH( generator->alg ); + status = psa_generator_hkdf_read( &generator->ctx.hkdf, hash_alg, + output, output_length ); + } + else +#endif /* MBEDTLS_MD_C */ { return( PSA_ERROR_BAD_STATE ); } @@ -3091,6 +3168,45 @@ exit: /* Key derivation */ /****************************************************************/ +/* Set up an HKDF-based generator. This is exactly the extract phase + * of the HKDF algorithm. */ +static psa_status_t psa_generator_hkdf_setup( psa_hkdf_generator_t *hkdf, + key_slot_t *slot, + psa_algorithm_t hash_alg, + const uint8_t *salt, + size_t salt_length, + const uint8_t *label, + size_t label_length ) +{ + psa_status_t status; + status = psa_hmac_setup_internal( &hkdf->hmac, + salt, salt_length, + PSA_ALG_HMAC_HASH( hash_alg ) ); + if( status != PSA_SUCCESS ) + return( status ); + status = psa_hash_update( &hkdf->hmac.hash_ctx, + slot->data.raw.data, + slot->data.raw.bytes ); + if( status != PSA_SUCCESS ) + return( status ); + status = psa_hmac_finish_internal( &hkdf->hmac, + hkdf->prk, + sizeof( hkdf->prk ) ); + if( status != PSA_SUCCESS ) + return( status ); + hkdf->offset_in_block = PSA_HASH_SIZE( hash_alg ); + hkdf->block_number = 0; + hkdf->info_length = label_length; + if( label_length != 0 ) + { + hkdf->info = mbedtls_calloc( 1, label_length ); + if( hkdf->info == NULL ) + return( PSA_ERROR_INSUFFICIENT_MEMORY ); + memcpy( hkdf->info, label, label_length ); + } + return( PSA_SUCCESS ); +} + psa_status_t psa_key_derivation( psa_crypto_generator_t *generator, psa_key_type_t key, psa_algorithm_t alg, @@ -3115,6 +3231,23 @@ psa_status_t psa_key_derivation( psa_crypto_generator_t *generator, if( ! PSA_ALG_IS_KEY_DERIVATION( alg ) ) return( PSA_ERROR_INVALID_ARGUMENT ); +#if defined(MBEDTLS_MD_C) + if( PSA_ALG_IS_HKDF( alg ) ) + { + psa_algorithm_t hash_alg = PSA_ALG_HKDF_GET_HASH( alg ); + size_t hash_size = PSA_HASH_SIZE( hash_alg ); + if( hash_size == 0 ) + return( PSA_ERROR_NOT_SUPPORTED ); + if( capacity > 255 * hash_size ) + return( PSA_ERROR_INVALID_ARGUMENT ); + status = psa_generator_hkdf_setup( &generator->ctx.hkdf, + slot, + hash_alg, + salt, salt_length, + label, label_length ); + } + else +#endif { return( PSA_ERROR_NOT_SUPPORTED ); } From 96ee5c70b96652b4f7c0a97a2e7a76dedf1286bc Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:24:54 +0200 Subject: [PATCH 13/18] HKDF: positive tests --- tests/suites/test_suite_psa_crypto.data | 48 ++++++++++ tests/suites/test_suite_psa_crypto.function | 100 ++++++++++++++++++++ 2 files changed, 148 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 1113eec81..3d7031b8c 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -749,6 +749,54 @@ asymmetric_decrypt_fail:PSA_KEY_TYPE_RSA_KEYPAIR:"3082025e02010002818100af057d39 PSA key derivation: not a key derivation algorithm depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HMAC(PSA_ALG_SHA_256):"":"":42:PSA_ERROR_INVALID_ARGUMENT + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 42+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865":"" + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 32+10 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf":"34007208d5b887185865" + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 0+42 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"":"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 1+41 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3c":"b25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865" + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 41+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b8871858":"" + +PSA key derivation: HKDF SHA-256, RFC5869 #1, output 1+40 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3c":"b25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b8871858" + +PSA key derivation: HKDF SHA-256, RFC5869 #2, output 82+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f":"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf":"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":82:"b11e398dc80327a1c8e7f78c596a49344f012eda2d4efad8a050cc4c19afa97c59045a99cac7827271cb41c65e590e09da3275600c2f09b8367793a9aca3db71cc30c58179ec3e87c14c01d5c1f3434f1d87":"" + +PSA key derivation: HKDF SHA-256, RFC5869 #3, output 42+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"":"":42:"8da4e775a563c18f715f802a063c5a31b8a11f5c5ee1879ec3454e5f3c738d2d9d201395faa4b61a96c8":"" + +PSA key derivation: HKDF SHA-1, RFC5869 #4, output 42+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"085a01ea1b10f36933068b56efa5ad81a4f14b822f5b091568a9cdd4f155fda2c22e422478d305f3f896":"" + +PSA key derivation: HKDF SHA-1, RFC5869 #5, output 82+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"000102030405060708090a0b0c0d0e0f101112131415161718191a1b1c1d1e1f202122232425262728292a2b2c2d2e2f303132333435363738393a3b3c3d3e3f404142434445464748494a4b4c4d4e4f":"606162636465666768696a6b6c6d6e6f707172737475767778797a7b7c7d7e7f808182838485868788898a8b8c8d8e8f909192939495969798999a9b9c9d9e9fa0a1a2a3a4a5a6a7a8a9aaabacadaeaf":"b0b1b2b3b4b5b6b7b8b9babbbcbdbebfc0c1c2c3c4c5c6c7c8c9cacbcccdcecfd0d1d2d3d4d5d6d7d8d9dadbdcdddedfe0e1e2e3e4e5e6e7e8e9eaebecedeeeff0f1f2f3f4f5f6f7f8f9fafbfcfdfeff":82:"0bd770a74d1160f7c9f12cd5912a06ebff6adcae899d92191fe4305673ba2ffe8fa3f1a4e5ad79f3f334b3b202b2173c486ea37ce3d397ed034c7f9dfeb15c5e927336d0441f4c4300e2cff0d0900b52d3b4":"" + +PSA key derivation: HKDF SHA-1, RFC5869 #6, output 42+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"":"":42:"0ac1af7002b3d761d1e55298da9d0506b9ae52057220a306e07b6b87e8df21d0ea00033de03984d34918":"" + +PSA key derivation: HKDF SHA-1, RFC5869 #7, output 42+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c":"":"":42:"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48":"" PSA generate random: 0 bytes generate_random:0 diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 278554f75..cf72e489e 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -8,6 +8,8 @@ #include "mbedtls/asn1write.h" #include "psa/crypto.h" +#define ARRAY_LENGTH( array ) ( sizeof( array ) / sizeof( *( array ) ) ) + #if(UINT32_MAX > SIZE_MAX) #define PSA_CRYPTO_TEST_SIZE_T_RANGE( x ) ( ( x ) <= SIZE_MAX ) #else @@ -2483,6 +2485,104 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void derive_output( int alg_arg, + data_t *key_data, + data_t *salt, + data_t *label, + int requested_capacity_arg, + data_t *expected_output1, + data_t *expected_output2 ) +{ + psa_key_slot_t slot = 1; + psa_algorithm_t alg = alg_arg; + size_t requested_capacity = requested_capacity_arg; + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + uint8_t *expected_outputs[2] = + {expected_output1->x, expected_output2->x}; + size_t output_sizes[2] = + {expected_output1->len, expected_output2->len}; + size_t output_buffer_size = 0; + uint8_t *output_buffer = NULL; + size_t expected_capacity; + size_t current_capacity; + psa_key_policy_t policy; + psa_status_t status; + unsigned i; + + for( i = 0; i < ARRAY_LENGTH( expected_outputs ); i++ ) + { + if( output_sizes[i] > output_buffer_size ) + output_buffer_size = output_sizes[i]; + if( output_sizes[i] == 0 ) + expected_outputs[i] = NULL; + } + output_buffer = mbedtls_calloc( 1, output_buffer_size ); + TEST_ASSERT( output_buffer != NULL ); + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg ); + TEST_ASSERT( psa_set_key_policy( slot, &policy ) == PSA_SUCCESS ); + + TEST_ASSERT( psa_import_key( slot, PSA_KEY_TYPE_DERIVE, + key_data->x, + key_data->len ) == PSA_SUCCESS ); + + /* Extraction phase. */ + TEST_ASSERT( psa_key_derivation( &generator, slot, alg, + salt->x, salt->len, + label->x, label->len, + requested_capacity ) == PSA_SUCCESS ); + TEST_ASSERT( psa_get_generator_capacity( &generator, + ¤t_capacity ) == + PSA_SUCCESS ); + TEST_ASSERT( current_capacity == requested_capacity ); + expected_capacity = requested_capacity; + + /* Expansion phase. */ + for( i = 0; i < ARRAY_LENGTH( expected_outputs ); i++ ) + { + /* Read some bytes. */ + status = psa_generator_read( &generator, + output_buffer, output_sizes[i] ); + if( expected_capacity == 0 && output_sizes[i] == 0 ) + { + /* Reading 0 bytes when 0 bytes are available can go either way. */ + TEST_ASSERT( status == PSA_SUCCESS || + status == PSA_ERROR_INSUFFICIENT_CAPACITY ); + continue; + } + else if( expected_capacity == 0 || + output_sizes[i] > expected_capacity ) + { + /* Capacity exceeded. */ + TEST_ASSERT( status == PSA_ERROR_INSUFFICIENT_CAPACITY ); + expected_capacity = 0; + continue; + } + /* Success. Check the read data. */ + TEST_ASSERT( status == PSA_SUCCESS ); + if( output_sizes[i] != 0 ) + TEST_ASSERT( memcmp( output_buffer, expected_outputs[i], + output_sizes[i] ) == 0 ); + /* Check the generator status. */ + expected_capacity -= output_sizes[i]; + TEST_ASSERT( psa_get_generator_capacity( &generator, + ¤t_capacity ) == + PSA_SUCCESS ); + TEST_ASSERT( expected_capacity == current_capacity ); + } + TEST_ASSERT( psa_generator_abort( &generator ) == PSA_SUCCESS ); + +exit: + mbedtls_free( output_buffer ); + psa_generator_abort( &generator ); + psa_destroy_key( slot ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void generate_random( int bytes_arg ) { From f24af9602f7e5e88dc5e9d4db1617c06e4ed4bb1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:29:05 +0200 Subject: [PATCH 14/18] Key derivation with HKDF: add a few negative tests --- tests/suites/test_suite_psa_crypto.data | 55 +++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 3d7031b8c..f906874ed 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -252,6 +252,18 @@ PSA key policy: asymmetric signature, neither sign nor verify depends_on:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15 asymmetric_signature_key_policy:0:PSA_ALG_RSA_PKCS1V15_SIGN_RAW:PSA_KEY_TYPE_RSA_KEYPAIR:"3082013b020100024100ee2b131d6b1818a94ca8e91c42387eb15a7c271f57b89e7336b144d4535b16c83097ecdefbbb92d1b5313b5a37214d0e8f25922dca778b424b25295fc8a1a7070203010001024100978ac8eadb0dc6035347d6aba8671215ff21283385396f7897c04baf5e2a835f3b53ef80a82ed36ae687a925380b55a0c73eb85656e989dcf0ed7fb4887024e1022100fdad8e1c6853563f8b921d2d112462ae7d6b176082d2ba43e87e1a37fc1a8b33022100f0592cf4c55ba44307b18981bcdbda376c51e590ffa5345ba866f6962dca94dd02201995f1a967d44ff4a4cd1de837bc65bf97a2bf7eda730a9a62cea53254591105022027f96cf4b8ee68ff8d04062ec1ce7f18c0b74e4b3379b29f9bfea3fc8e592731022100cefa6d220496b43feb83194255d8fb930afcf46f36606e3aa0eb7a93ad88c10c":PSA_ALG_RSA_PKCS1V15_SIGN_RAW +PSA key policy: derive, permitted +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_policy:PSA_KEY_USAGE_DERIVE:PSA_ALG_HKDF(PSA_ALG_SHA_256):PSA_KEY_TYPE_DERIVE:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ALG_HKDF(PSA_ALG_SHA_256) + +PSA key policy: derive, not permitted +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_policy:0:PSA_ALG_HKDF(PSA_ALG_SHA_256):PSA_KEY_TYPE_DERIVE:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ALG_HKDF(PSA_ALG_SHA_256) + +PSA key policy: derive, wrong algorithm +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_policy:PSA_KEY_USAGE_DERIVE:PSA_ALG_HKDF(PSA_ALG_SHA_256):PSA_KEY_TYPE_DERIVE:"aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa":PSA_ALG_HKDF(PSA_ALG_SHA_224) + PSA key lifetime: set and get volatile key_lifetime:PSA_KEY_LIFETIME_VOLATILE @@ -745,11 +757,22 @@ PSA decrypt: RSA PKCS#1 v1.5, input too large depends_on:MBEDTLS_PK_PARSE_C:MBEDTLS_RSA_C:MBEDTLS_PKCS1_V15 asymmetric_decrypt_fail:PSA_KEY_TYPE_RSA_KEYPAIR:"3082025e02010002818100af057d396ee84fb75fdbb5c2b13c7fe5a654aa8aa2470b541ee1feb0b12d25c79711531249e1129628042dbbb6c120d1443524ef4c0e6e1d8956eeb2077af12349ddeee54483bc06c2c61948cd02b202e796aebd94d3a7cbf859c2c1819c324cb82b9cd34ede263a2abffe4733f077869e8660f7d6834da53d690ef7985f6bc3020301000102818100874bf0ffc2f2a71d14671ddd0171c954d7fdbf50281e4f6d99ea0e1ebcf82faa58e7b595ffb293d1abe17f110b37c48cc0f36c37e84d876621d327f64bbe08457d3ec4098ba2fa0a319fba411c2841ed7be83196a8cdf9daa5d00694bc335fc4c32217fe0488bce9cb7202e59468b1ead119000477db2ca797fac19eda3f58c1024100e2ab760841bb9d30a81d222de1eb7381d82214407f1b975cbbfe4e1a9467fd98adbd78f607836ca5be1928b9d160d97fd45c12d6b52e2c9871a174c66b488113024100c5ab27602159ae7d6f20c3c2ee851e46dc112e689e28d5fcbbf990a99ef8a90b8bb44fd36467e7fc1789ceb663abda338652c3c73f111774902e840565927091024100b6cdbd354f7df579a63b48b3643e353b84898777b48b15f94e0bfc0567a6ae5911d57ad6409cf7647bf96264e9bd87eb95e263b7110b9a1f9f94acced0fafa4d024071195eec37e8d257decfc672b07ae639f10cbb9b0c739d0c809968d644a94e3fd6ed9287077a14583f379058f76a8aecd43c62dc8c0f41766650d725275ac4a1024100bb32d133edc2e048d463388b7be9cb4be29f4b6250be603e70e3647501c97ddde20a4e71be95fd5e71784e25aca4baf25be5738aae59bbfe1c997781447a2b24":PSA_ALG_RSA_PKCS1V15_CRYPT:"0099ffde2fcc00c9cc01972ebfa7779b298dbbaf7f50707a7405296dd2783456fc792002f462e760500e02afa25a859ace8701cb5d3b0262116431c43af8eb08f5a88301057cf1c156a2a5193c143e7a5b03fac132b7e89e6dcd8f4c82c9b28452329c260d30bc39b3816b7c46b41b37b4850d2ae74e729f99c6621fbbe2e46872":PSA_ERROR_INVALID_ARGUMENT +PSA key derivation: HKDF-SHA-256, good case +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HKDF(PSA_ALG_SHA_256):"":"":42:PSA_SUCCESS + +PSA key derivation: bad key type +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_setup:PSA_KEY_TYPE_RAW_DATA:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HKDF(PSA_ALG_SHA_256):"":"":42:PSA_ERROR_INVALID_ARGUMENT PSA key derivation: not a key derivation algorithm depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HMAC(PSA_ALG_SHA_256):"":"":42:PSA_ERROR_INVALID_ARGUMENT +PSA key derivation: unsupported key derivation algorithm +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HKDF(PSA_ALG_CATEGORY_HASH):"":"":42:PSA_ERROR_NOT_SUPPORTED + PSA key derivation: HKDF SHA-256, RFC5869 #1, output 42+0 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865":"" @@ -797,6 +820,38 @@ derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0 PSA key derivation: HKDF SHA-1, RFC5869 #7, output 42+0 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c":"":"":42:"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48":"" + +PSA key derivation: HKDF SHA-256, request maximum capacity +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":255 * 32:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865":"" + +PSA key derivation: HKDF SHA-1, request maximum capacity +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_1):"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c":"":"":255 * 20:"2c91117204d745f3500d636a62f64f0ab3bae548aa53d423b0d1f27ebba6f5e5673a081d70cce7acfc48":"" + +PSA key derivation: HKDF SHA-256, request too much capacity +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_setup:PSA_KEY_TYPE_DERIVE:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_ALG_HKDF(PSA_ALG_SHA_256):"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":255 * 32 + 1:PSA_ERROR_INVALID_ARGUMENT + +PSA key derivation: HKDF SHA-1, request too much capacity +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA1_C +derive_setup:PSA_KEY_TYPE_DERIVE:"0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c0c":PSA_ALG_HKDF(PSA_ALG_SHA_1):"":"":255 * 20 + 1:PSA_ERROR_INVALID_ARGUMENT + +PSA key derivation: over capacity 42: output 42+1 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865":"ff" + +PSA key derivation: over capacity 42: output 41+2 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b8871858":"65ff" + +PSA key derivation: over capacity 42: output 43+0 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865ff":"" + +PSA key derivation: over capacity 42: output 43+1 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865ff":"ff" PSA generate random: 0 bytes generate_random:0 From 0386fbaa70883c9ef7f8390c2a264f11ef8596ec Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:29:22 +0200 Subject: [PATCH 15/18] Key derivation: test deriving a key from the KDF output --- tests/suites/test_suite_psa_crypto.data | 17 +++ tests/suites/test_suite_psa_crypto.function | 143 ++++++++++++++++++++ 2 files changed, 160 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index f906874ed..43b964730 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -852,6 +852,23 @@ derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0 PSA key derivation: over capacity 42: output 43+1 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865ff":"ff" + +PSA key derivation: HKDF SHA-256, exercise HMAC-SHA-256 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_HMAC:256:PSA_KEY_USAGE_SIGN:PSA_ALG_HMAC(PSA_ALG_SHA_256) + +PSA key derivation: HKDF SHA-256, exercise HKDF-SHA-256 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_DERIVE:400:PSA_KEY_USAGE_DERIVE:PSA_ALG_HKDF(PSA_ALG_SHA_256) + +PSA key derivation: HKDF SHA-256, derive key, 16+32 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_export:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":16:32 + +PSA key derivation: HKDF SHA-256, derive key, 1+41 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_key_export:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":1:41 + PSA generate random: 0 bytes generate_random:0 diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index cf72e489e..1f1732e65 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -2583,6 +2583,149 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void derive_key_exercise( int alg_arg, + data_t *key_data, + data_t *salt, + data_t *label, + int derived_type_arg, + int derived_bits_arg, + int derived_usage_arg, + int derived_alg_arg ) +{ + psa_key_slot_t base_key = 1; + psa_key_slot_t derived_key = 2; + psa_algorithm_t alg = alg_arg; + psa_key_type_t derived_type = derived_type_arg; + size_t derived_bits = derived_bits_arg; + psa_key_usage_t derived_usage = derived_usage_arg; + psa_algorithm_t derived_alg = derived_alg_arg; + size_t capacity = PSA_BITS_TO_BYTES( derived_bits ); + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + psa_key_policy_t policy; + psa_key_type_t got_type; + size_t got_bits; + + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg ); + TEST_ASSERT( psa_set_key_policy( base_key, &policy ) == PSA_SUCCESS ); + TEST_ASSERT( psa_import_key( base_key, PSA_KEY_TYPE_DERIVE, + key_data->x, + key_data->len ) == PSA_SUCCESS ); + + /* Derive a key. */ + TEST_ASSERT( psa_key_derivation( &generator, base_key, alg, + salt->x, salt->len, + label->x, label->len, + capacity ) == PSA_SUCCESS ); + psa_key_policy_set_usage( &policy, derived_usage, derived_alg ); + TEST_ASSERT( psa_set_key_policy( derived_key, &policy ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_import_key( derived_key, + derived_type, + derived_bits, + &generator ) == PSA_SUCCESS ); + + /* Test the key information */ + TEST_ASSERT( psa_get_key_information( derived_key, + &got_type, + &got_bits ) == PSA_SUCCESS ); + TEST_ASSERT( got_type == derived_type ); + TEST_ASSERT( got_bits == derived_bits ); + + /* Exercise the derived key. */ + if( ! exercise_key( derived_key, derived_usage, derived_alg ) ) + goto exit; + +exit: + psa_generator_abort( &generator ); + psa_destroy_key( base_key ); + psa_destroy_key( derived_key ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void derive_key_export( int alg_arg, + data_t *key_data, + data_t *salt, + data_t *label, + int bytes1_arg, + int bytes2_arg ) +{ + psa_key_slot_t base_key = 1; + psa_key_slot_t derived_key = 2; + psa_algorithm_t alg = alg_arg; + size_t bytes1 = bytes1_arg; + size_t bytes2 = bytes2_arg; + size_t capacity = bytes1 + bytes2; + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + uint8_t *output_buffer = mbedtls_calloc( 1, capacity ); + uint8_t *export_buffer = mbedtls_calloc( 1, capacity ); + psa_key_policy_t policy; + size_t length; + + TEST_ASSERT( output_buffer != NULL ); + TEST_ASSERT( export_buffer != NULL ); + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg ); + TEST_ASSERT( psa_set_key_policy( base_key, &policy ) == PSA_SUCCESS ); + TEST_ASSERT( psa_import_key( base_key, PSA_KEY_TYPE_DERIVE, + key_data->x, + key_data->len ) == PSA_SUCCESS ); + + /* Derive some material and output it. */ + TEST_ASSERT( psa_key_derivation( &generator, base_key, alg, + salt->x, salt->len, + label->x, label->len, + capacity ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_read( &generator, + output_buffer, + capacity ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_abort( &generator ) == PSA_SUCCESS ); + + /* Derive the same output again, but this time store it in key objects. */ + TEST_ASSERT( psa_key_derivation( &generator, base_key, alg, + salt->x, salt->len, + label->x, label->len, + capacity ) == PSA_SUCCESS ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_EXPORT, 0 ); + TEST_ASSERT( psa_set_key_policy( derived_key, &policy ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_import_key( derived_key, + PSA_KEY_TYPE_RAW_DATA, + PSA_BYTES_TO_BITS( bytes1 ), + &generator ) == PSA_SUCCESS ); + TEST_ASSERT( psa_export_key( derived_key, + export_buffer, bytes1, + &length ) == PSA_SUCCESS ); + TEST_ASSERT( length == bytes1 ); + TEST_ASSERT( psa_destroy_key( derived_key ) == PSA_SUCCESS ); + TEST_ASSERT( psa_set_key_policy( derived_key, &policy ) == PSA_SUCCESS ); + TEST_ASSERT( psa_generator_import_key( derived_key, + PSA_KEY_TYPE_RAW_DATA, + PSA_BYTES_TO_BITS( bytes2 ), + &generator ) == PSA_SUCCESS ); + TEST_ASSERT( psa_export_key( derived_key, + export_buffer + bytes1, bytes2, + &length ) == PSA_SUCCESS ); + TEST_ASSERT( length == bytes2 ); + + /* Compare the outputs from the two runs. */ + TEST_ASSERT( memcmp( output_buffer, export_buffer, capacity ) == 0 ); + +exit: + mbedtls_free( output_buffer ); + mbedtls_free( export_buffer ); + psa_generator_abort( &generator ); + psa_destroy_key( base_key ); + psa_destroy_key( derived_key ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void generate_random( int bytes_arg ) { From d54931c7c4d199ec709d7f6f7516d361cdff4b4b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Tue, 17 Jul 2018 21:06:59 +0200 Subject: [PATCH 16/18] HKDF: be more robust if we reach the maximum ouptut length In psa_generator_hkdf_read, return BAD_STATE if we're trying to construct more output than the algorithm allows. This can't happen through the API due to the capacity limit, but it could potentially happen in an internal call. Also add a test case that verifies that we can set up HKDF with its maximum capacity and read up to the maximum capacity. --- library/psa_crypto.c | 9 ++- tests/suites/test_suite_psa_crypto.data | 8 +++ tests/suites/test_suite_psa_crypto.function | 66 +++++++++++++++++++++ 3 files changed, 82 insertions(+), 1 deletion(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 9e8f90b54..ef99403e3 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -3047,8 +3047,15 @@ static psa_status_t psa_generator_hkdf_read( psa_hkdf_generator_t *hkdf, output += n; output_length -= n; hkdf->offset_in_block += n; - if( output_length == 0 || hkdf->block_number == 0xff ) + if( output_length == 0 ) break; + /* We can't be wanting more output after block 0xff, otherwise + * the capacity check in psa_generator_read() would have + * prevented this call. It could happen only if the generator + * object was corrupted or if this function is called directly + * inside the library. */ + if( hkdf->block_number == 0xff ) + return( PSA_ERROR_BAD_STATE ); /* We need a new block */ ++hkdf->block_number; diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 43b964730..8ffaa8779 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -853,6 +853,14 @@ PSA key derivation: over capacity 42: output 43+1 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_output:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":42:"3cb25f25faacd57a90434f64d0362f2a2d2d0a90cf1a5a4c5db02d56ecc4c5bf34007208d5b887185865ff":"ff" +PSA key derivation: HKDF SHA-256, read maximum capacity minus 1 +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_full:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":255 * 32 - 1 + +PSA key derivation: HKDF SHA-256, read maximum capacity +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C +derive_full:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":255 * 32 + PSA key derivation: HKDF SHA-256, exercise HMAC-SHA-256 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_HMAC:256:PSA_KEY_USAGE_SIGN:PSA_ALG_HMAC(PSA_ALG_SHA_256) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 1f1732e65..27bc4e1d5 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -2583,6 +2583,72 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void derive_full( int alg_arg, + data_t *key_data, + data_t *salt, + data_t *label, + int requested_capacity_arg ) +{ + psa_key_slot_t slot = 1; + psa_algorithm_t alg = alg_arg; + size_t requested_capacity = requested_capacity_arg; + psa_crypto_generator_t generator = PSA_CRYPTO_GENERATOR_INIT; + unsigned char output_buffer[16]; + size_t expected_capacity = requested_capacity; + size_t current_capacity; + psa_key_policy_t policy; + + TEST_ASSERT( psa_crypto_init( ) == PSA_SUCCESS ); + + psa_key_policy_init( &policy ); + psa_key_policy_set_usage( &policy, PSA_KEY_USAGE_DERIVE, alg ); + TEST_ASSERT( psa_set_key_policy( slot, &policy ) == PSA_SUCCESS ); + + TEST_ASSERT( psa_import_key( slot, PSA_KEY_TYPE_DERIVE, + key_data->x, + key_data->len ) == PSA_SUCCESS ); + + /* Extraction phase. */ + TEST_ASSERT( psa_key_derivation( &generator, slot, alg, + salt->x, salt->len, + label->x, label->len, + requested_capacity ) == PSA_SUCCESS ); + TEST_ASSERT( psa_get_generator_capacity( &generator, + ¤t_capacity ) == + PSA_SUCCESS ); + TEST_ASSERT( current_capacity == expected_capacity ); + + /* Expansion phase. */ + while( current_capacity > 0 ) + { + size_t read_size = sizeof( output_buffer ); + if( read_size > current_capacity ) + read_size = current_capacity; + TEST_ASSERT( psa_generator_read( &generator, + output_buffer, + read_size ) == PSA_SUCCESS ); + expected_capacity -= read_size; + TEST_ASSERT( psa_get_generator_capacity( &generator, + ¤t_capacity ) == + PSA_SUCCESS ); + TEST_ASSERT( current_capacity == expected_capacity ); + } + + /* Check that the generator refuses to go over capacity. */ + TEST_ASSERT( psa_generator_read( &generator, + output_buffer, + 1 ) == PSA_ERROR_INSUFFICIENT_CAPACITY ); + + TEST_ASSERT( psa_generator_abort( &generator ) == PSA_SUCCESS ); + +exit: + psa_generator_abort( &generator ); + psa_destroy_key( slot ); + mbedtls_psa_crypto_free( ); +} +/* END_CASE */ + /* BEGIN_CASE */ void derive_key_exercise( int alg_arg, data_t *key_data, From 9fb0e011771b03c15466e6b0f86dcc62ac057048 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 19 Jul 2018 15:51:49 +0200 Subject: [PATCH 17/18] Fix nonstandard whitespace --- include/psa/crypto.h | 44 ++++++++++++++++++++++---------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 47241a68c..63be86b3e 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -2192,17 +2192,17 @@ psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation); * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED */ -psa_status_t psa_aead_encrypt( psa_key_slot_t key, - psa_algorithm_t alg, - const uint8_t *nonce, - size_t nonce_length, - const uint8_t *additional_data, - size_t additional_data_length, - const uint8_t *plaintext, - size_t plaintext_length, - uint8_t *ciphertext, - size_t ciphertext_size, - size_t *ciphertext_length ); +psa_status_t psa_aead_encrypt(psa_key_slot_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *plaintext, + size_t plaintext_length, + uint8_t *ciphertext, + size_t ciphertext_size, + size_t *ciphertext_length); /** Process an authenticated decryption operation. * @@ -2245,17 +2245,17 @@ psa_status_t psa_aead_encrypt( psa_key_slot_t key, * \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_TAMPERING_DETECTED */ -psa_status_t psa_aead_decrypt( psa_key_slot_t key, - psa_algorithm_t alg, - const uint8_t *nonce, - size_t nonce_length, - const uint8_t *additional_data, - size_t additional_data_length, - const uint8_t *ciphertext, - size_t ciphertext_length, - uint8_t *plaintext, - size_t plaintext_size, - size_t *plaintext_length ); +psa_status_t psa_aead_decrypt(psa_key_slot_t key, + psa_algorithm_t alg, + const uint8_t *nonce, + size_t nonce_length, + const uint8_t *additional_data, + size_t additional_data_length, + const uint8_t *ciphertext, + size_t ciphertext_length, + uint8_t *plaintext, + size_t plaintext_size, + size_t *plaintext_length); /**@}*/ From 08542d80376b46999dbaada5dde810733b9ea547 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 19 Jul 2018 17:05:42 +0200 Subject: [PATCH 18/18] Fix psa_generator_import_key for DES In psa_generator_import_key, if generating a DES or 3DES key, set the parity bits. Add tests for deriving a DES key. Also test deriving an AES key while I'm at it. --- library/psa_crypto.c | 25 ++++++++++++++++++------- tests/suites/test_suite_psa_crypto.data | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+), 7 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index ef99403e3..a532bd358 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -3142,6 +3142,18 @@ exit: return( status ); } +#if defined(MBEDTLS_DES_C) +static void psa_des_set_key_parity( uint8_t *data, size_t data_size ) +{ + if( data_size >= 8 ) + mbedtls_des_key_set_parity( data ); + if( data_size >= 16 ) + mbedtls_des_key_set_parity( data + 8 ); + if( data_size >= 24 ) + mbedtls_des_key_set_parity( data + 16 ); +} +#endif /* MBEDTLS_DES_C */ + psa_status_t psa_generator_import_key( psa_key_slot_t key, psa_key_type_t type, size_t bits, @@ -3162,6 +3174,10 @@ psa_status_t psa_generator_import_key( psa_key_slot_t key, status = psa_generator_read( generator, data, bytes ); if( status != PSA_SUCCESS ) goto exit; +#if defined(MBEDTLS_DES_C) + if( type == PSA_KEY_TYPE_DES ) + psa_des_set_key_parity( data, bytes ); +#endif /* MBEDTLS_DES_C */ status = psa_import_key( key, type, data, bytes ); exit: @@ -3312,13 +3328,8 @@ psa_status_t psa_generate_key( psa_key_slot_t key, } #if defined(MBEDTLS_DES_C) if( type == PSA_KEY_TYPE_DES ) - { - mbedtls_des_key_set_parity( slot->data.raw.data ); - if( slot->data.raw.bytes >= 16 ) - mbedtls_des_key_set_parity( slot->data.raw.data + 8 ); - if( slot->data.raw.bytes == 24 ) - mbedtls_des_key_set_parity( slot->data.raw.data + 16 ); - } + psa_des_set_key_parity( slot->data.raw.data, + slot->data.raw.bytes ); #endif /* MBEDTLS_DES_C */ } else diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 8ffaa8779..4904dff21 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -861,6 +861,26 @@ PSA key derivation: HKDF SHA-256, read maximum capacity depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_full:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":255 * 32 +PSA key derivation: HKDF SHA-256, exercise AES128-CTR +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CTR +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_AES:128:PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CTR + +PSA key derivation: HKDF SHA-256, exercise AES256-CTR +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C:MBEDTLS_AES_C:MBEDTLS_CIPHER_MODE_CTR +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_AES:256:PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CTR + +PSA key derivation: HKDF SHA-256, exercise DES-CBC +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_DES:64:PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CBC_BASE | PSA_ALG_BLOCK_CIPHER_PAD_PKCS7 + +PSA key derivation: HKDF SHA-256, exercise 2-key 3DES-CBC +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_DES:128:PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CBC_BASE | PSA_ALG_BLOCK_CIPHER_PAD_PKCS7 + +PSA key derivation: HKDF SHA-256, exercise 3-key 3DES-CBC +depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C:MBEDTLS_DES_C:MBEDTLS_CIPHER_MODE_CBC +derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_DES:192:PSA_KEY_USAGE_ENCRYPT:PSA_ALG_CBC_BASE | PSA_ALG_BLOCK_CIPHER_PAD_PKCS7 + PSA key derivation: HKDF SHA-256, exercise HMAC-SHA-256 depends_on:MBEDTLS_MD_C:MBEDTLS_SHA256_C derive_key_exercise:PSA_ALG_HKDF(PSA_ALG_SHA_256):"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":"000102030405060708090a0b0c":"f0f1f2f3f4f5f6f7f8f9":PSA_KEY_TYPE_HMAC:256:PSA_KEY_USAGE_SIGN:PSA_ALG_HMAC(PSA_ALG_SHA_256)