From eab56e4159cdfb7ca7a033735025aadb0ad1a627 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Jul 2018 17:12:33 +0200 Subject: [PATCH] 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 8ac817a6e2..4dbbdedcd4 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 85c997485c..27a9f1efc9 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 67536f2ac4..6cc42c6ba7 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,