From bef7f14f8e251cf1f8f82ea520ea0e97a57efaef Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:22:21 +0200 Subject: [PATCH] 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 ); }