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.
diff --git a/include/psa/crypto.h b/include/psa/crypto.h
index 8ac817a..4dbbded 100644
--- a/include/psa/crypto.h
+++ b/include/psa/crypto.h
@@ -285,12 +285,18 @@
* 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 @@
/**@}*/
+/** \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 85c9974..27a9f1e 100644
--- a/include/psa/crypto_struct.h
+++ b/include/psa/crypto_struct.h
@@ -130,6 +130,27 @@
} 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 67536f2..6cc42c6 100644
--- a/library/psa_crypto.c
+++ b/library/psa_crypto.c
@@ -2988,7 +2988,105 @@
/****************************************************************/
-/* 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,