New function psa_copy_key

Copy a key from one slot to another.

Implemented and smoke-tested.
This commit is contained in:
Gilles Peskine 2019-01-19 13:40:11 +01:00
parent 0ce26e35d6
commit 4cb9dde84a
4 changed files with 406 additions and 123 deletions

View file

@ -93,6 +93,140 @@ psa_status_t psa_crypto_init(void);
/**@}*/ /**@}*/
/** \defgroup policy Key policies
* @{
*/
/** The type of the key policy data structure.
*
* Before calling any function on a key policy, the application must initialize
* it by any of the following means:
* - Set the structure to all-bits-zero, for example:
* \code
* psa_key_policy_t policy;
* memset(&policy, 0, sizeof(policy));
* \endcode
* - Initialize the structure to logical zero values, for example:
* \code
* psa_key_policy_t policy = {0};
* \endcode
* - Initialize the structure to the initializer #PSA_KEY_POLICY_INIT,
* for example:
* \code
* psa_key_policy_t policy = PSA_KEY_POLICY_INIT;
* \endcode
* - Assign the result of the function psa_key_policy_init()
* to the structure, for example:
* \code
* psa_key_policy_t policy;
* policy = psa_key_policy_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_key_policy_s psa_key_policy_t;
/** \def PSA_KEY_POLICY_INIT
*
* This macro returns a suitable initializer for a key policy object of type
* #psa_key_policy_t.
*/
#ifdef __DOXYGEN_ONLY__
/* This is an example definition for documentation purposes.
* Implementations should define a suitable value in `crypto_struct.h`.
*/
#define PSA_KEY_POLICY_INIT {0}
#endif
/** Return an initial value for a key policy that forbids all usage of the key.
*/
static psa_key_policy_t psa_key_policy_init(void);
/** \brief Set the standard fields of a policy structure.
*
* Note that this function does not make any consistency check of the
* parameters. The values are only checked when applying the policy to
* a key slot with psa_set_key_policy().
*
* \param[in,out] policy The key policy to modify. It must have been
* initialized as per the documentation for
* #psa_key_policy_t.
* \param usage The permitted uses for the key.
* \param alg The algorithm that the key may be used for.
*/
void psa_key_policy_set_usage(psa_key_policy_t *policy,
psa_key_usage_t usage,
psa_algorithm_t alg);
/** \brief Retrieve the usage field of a policy structure.
*
* \param[in] policy The policy object to query.
*
* \return The permitted uses for a key with this policy.
*/
psa_key_usage_t psa_key_policy_get_usage(const psa_key_policy_t *policy);
/** \brief Retrieve the algorithm field of a policy structure.
*
* \param[in] policy The policy object to query.
*
* \return The permitted algorithm for a key with this policy.
*/
psa_algorithm_t psa_key_policy_get_algorithm(const psa_key_policy_t *policy);
/** \brief Set the usage policy on a key slot.
*
* This function must be called on an empty key slot, before importing,
* generating or creating a key in the slot. Changing the policy of an
* existing key is not permitted.
*
* Implementations may set restrictions on supported key policies
* depending on the key type and the key slot.
*
* \param handle Handle to the key whose policy is to be changed.
* \param[in] policy The policy object to query.
*
* \retval #PSA_SUCCESS
* Success.
* If the key is persistent, it is implementation-defined whether
* the policy has been saved to persistent storage. Implementations
* may defer saving the policy until the key material is created.
* \retval #PSA_ERROR_INVALID_HANDLE
* \retval #PSA_ERROR_OCCUPIED_SLOT
* \retval #PSA_ERROR_NOT_SUPPORTED
* \retval #PSA_ERROR_INVALID_ARGUMENT
* \retval #PSA_ERROR_COMMUNICATION_FAILURE
* \retval #PSA_ERROR_HARDWARE_FAILURE
* \retval #PSA_ERROR_TAMPERING_DETECTED
* \retval #PSA_ERROR_BAD_STATE
* The library has not been previously initialized by psa_crypto_init().
* It is implementation-dependent whether a failure to initialize
* results in this error code.
*/
psa_status_t psa_set_key_policy(psa_key_handle_t handle,
const psa_key_policy_t *policy);
/** \brief Get the usage policy for a key slot.
*
* \param handle Handle to the key slot whose policy is being queried.
* \param[out] policy On success, the key's policy.
*
* \retval #PSA_SUCCESS
* \retval #PSA_ERROR_INVALID_HANDLE
* \retval #PSA_ERROR_COMMUNICATION_FAILURE
* \retval #PSA_ERROR_HARDWARE_FAILURE
* \retval #PSA_ERROR_TAMPERING_DETECTED
* \retval #PSA_ERROR_BAD_STATE
* The library has not been previously initialized by psa_crypto_init().
* It is implementation-dependent whether a failure to initialize
* results in this error code.
*/
psa_status_t psa_get_key_policy(psa_key_handle_t handle,
psa_key_policy_t *policy);
/**@}*/
/** \defgroup key_management Key management /** \defgroup key_management Key management
* @{ * @{
*/ */
@ -545,139 +679,70 @@ psa_status_t psa_export_public_key(psa_key_handle_t handle,
size_t data_size, size_t data_size,
size_t *data_length); size_t *data_length);
/**@}*/ /** Make a copy of a key.
/** \defgroup policy Key policies
* @{
*/
/** The type of the key policy data structure.
* *
* Before calling any function on a key policy, the application must initialize * Copy key material from one location to another.
* it by any of the following means:
* - Set the structure to all-bits-zero, for example:
* \code
* psa_key_policy_t policy;
* memset(&policy, 0, sizeof(policy));
* \endcode
* - Initialize the structure to logical zero values, for example:
* \code
* psa_key_policy_t policy = {0};
* \endcode
* - Initialize the structure to the initializer #PSA_KEY_POLICY_INIT,
* for example:
* \code
* psa_key_policy_t policy = PSA_KEY_POLICY_INIT;
* \endcode
* - Assign the result of the function psa_key_policy_init()
* to the structure, for example:
* \code
* psa_key_policy_t policy;
* policy = psa_key_policy_init();
* \endcode
* *
* This is an implementation-defined \c struct. Applications should not * This function is primarily useful to copy a key from one lifetime
* make any assumptions about the content of this structure except * to another. The target key retains its lifetime and location.
* as directed by the documentation of a specific implementation. */
typedef struct psa_key_policy_s psa_key_policy_t;
/** \def PSA_KEY_POLICY_INIT
* *
* This macro returns a suitable initializer for a key policy object of type * In an implementation where slots have different ownerships,
* #psa_key_policy_t. * this functin may be used to share a key with a different party,
*/ * subject to implementation-defined restrictions on key sharing.
#ifdef __DOXYGEN_ONLY__ * In this case \p constraint would typically prevent the recipient
/* This is an example definition for documentation purposes. * from exporting the key.
* Implementations should define a suitable value in `crypto_struct.h`.
*/
#define PSA_KEY_POLICY_INIT {0}
#endif
/** Return an initial value for a key policy that forbids all usage of the key.
*/
static psa_key_policy_t psa_key_policy_init(void);
/** \brief Set the standard fields of a policy structure.
* *
* Note that this function does not make any consistency check of the * The resulting key may only be used in a way that conforms to all
* parameters. The values are only checked when applying the policy to * three of: the policy of the source key, the policy previously set
* a key slot with psa_set_key_policy(). * on the target, and the \p constraint parameter passed when calling
* this function.
* - The usage flags on the resulting key are the bitwise-and of the
* usage flags on the source policy, the previously-set target policy
* and the policy constraint.
* - If all three policies allow the same algorithm or wildcard-based
* algorithm policy, the resulting key has the same algorithm policy.
* - If one of the policies allows an algorithm and all the other policies
* either allow the same algorithm or a wildcard-based algorithm policy
* that includes this algorithm, the resulting key allows the same
* algorithm.
* *
* \param[in,out] policy The key policy to modify. It must have been * The effect of this function on implementation-defined metadata is
* initialized as per the documentation for * implementation-defined.
* #psa_key_policy_t.
* \param usage The permitted uses for the key.
* \param alg The algorithm that the key may be used for.
*/
void psa_key_policy_set_usage(psa_key_policy_t *policy,
psa_key_usage_t usage,
psa_algorithm_t alg);
/** \brief Retrieve the usage field of a policy structure.
* *
* \param[in] policy The policy object to query. * \param source_handle The key to copy. It must be a handle to an
* * occupied slot.
* \return The permitted uses for a key with this policy. * \param target_handle A handle to the target slot. It must not contain
*/ * key material yet.
psa_key_usage_t psa_key_policy_get_usage(const psa_key_policy_t *policy); * \param[in] constraint An optional policy constraint. If this parameter
* is non-null then the resulting key will conform
/** \brief Retrieve the algorithm field of a policy structure. * to this policy in addition to the source policy
* * and the policy already present on the target
* \param[in] policy The policy object to query. * slot. If this parameter is null then the
* * function behaves in the same way as if it was
* \return The permitted algorithm for a key with this policy. * the target policy, i.e. only the source and
*/ * target policies apply.
psa_algorithm_t psa_key_policy_get_algorithm(const psa_key_policy_t *policy);
/** \brief Set the usage policy on a key slot.
*
* This function must be called on an empty key slot, before importing,
* generating or creating a key in the slot. Changing the policy of an
* existing key is not permitted.
*
* Implementations may set restrictions on supported key policies
* depending on the key type and the key slot.
*
* \param handle Handle to the key whose policy is to be changed.
* \param[in] policy The policy object to query.
* *
* \retval #PSA_SUCCESS * \retval #PSA_SUCCESS
* Success.
* If the key is persistent, it is implementation-defined whether
* the policy has been saved to persistent storage. Implementations
* may defer saving the policy until the key material is created.
* \retval #PSA_ERROR_INVALID_HANDLE * \retval #PSA_ERROR_INVALID_HANDLE
* \retval #PSA_ERROR_OCCUPIED_SLOT * \retval #PSA_ERROR_OCCUPIED_SLOT
* \retval #PSA_ERROR_NOT_SUPPORTED * \p target already contains key material.
* \retval #PSA_ERROR_EMPTY_SLOT
* \p source does not contain key material.
* \retval #PSA_ERROR_INVALID_ARGUMENT * \retval #PSA_ERROR_INVALID_ARGUMENT
* The policy constraints on the source, on the target and
* \p constraints are incompatible.
* \retval #PSA_ERROR_NOT_PERMITTED
* The source key is not exportable and its lifetime does not
* allow copying it to the target's lifetime.
* \retval #PSA_ERROR_INSUFFICIENT_MEMORY
* \retval #PSA_ERROR_INSUFFICIENT_STORAGE
* \retval #PSA_ERROR_COMMUNICATION_FAILURE * \retval #PSA_ERROR_COMMUNICATION_FAILURE
* \retval #PSA_ERROR_HARDWARE_FAILURE * \retval #PSA_ERROR_HARDWARE_FAILURE
* \retval #PSA_ERROR_TAMPERING_DETECTED * \retval #PSA_ERROR_TAMPERING_DETECTED
* \retval #PSA_ERROR_BAD_STATE
* The library has not been previously initialized by psa_crypto_init().
* It is implementation-dependent whether a failure to initialize
* results in this error code.
*/ */
psa_status_t psa_set_key_policy(psa_key_handle_t handle, psa_status_t psa_copy_key(psa_key_handle_t source_handle,
const psa_key_policy_t *policy); psa_key_handle_t target_handle,
const psa_key_policy_t *constraint);
/** \brief Get the usage policy for a key slot.
*
* \param handle Handle to the key slot whose policy is being queried.
* \param[out] policy On success, the key's policy.
*
* \retval #PSA_SUCCESS
* \retval #PSA_ERROR_INVALID_HANDLE
* \retval #PSA_ERROR_COMMUNICATION_FAILURE
* \retval #PSA_ERROR_HARDWARE_FAILURE
* \retval #PSA_ERROR_TAMPERING_DETECTED
* \retval #PSA_ERROR_BAD_STATE
* The library has not been previously initialized by psa_crypto_init().
* It is implementation-dependent whether a failure to initialize
* results in this error code.
*/
psa_status_t psa_get_key_policy(psa_key_handle_t handle,
psa_key_policy_t *policy);
/**@}*/ /**@}*/

View file

@ -713,6 +713,32 @@ static psa_status_t psa_get_empty_key_slot( psa_key_handle_t handle,
return( status ); return( status );
} }
/** Calculate the intersection of two algorithm usage policies.
*
* Return 0 (which allows no operation) on incompatibility.
*/
static psa_algorithm_t psa_key_policy_algorithm_intersection(
psa_algorithm_t alg1,
psa_algorithm_t alg2 )
{
/* Common case: the policy only allows alg. */
if( alg1 == alg2 )
return( alg1 );
/* If the policies are from the same hash-and-sign family, check
* if one is a wildcard. */
if( PSA_ALG_IS_HASH_AND_SIGN( alg1 ) &&
PSA_ALG_IS_HASH_AND_SIGN( alg2 ) &&
( alg1 & ~PSA_ALG_HASH_MASK ) == ( alg2 & ~PSA_ALG_HASH_MASK ) )
{
if( PSA_ALG_SIGN_GET_HASH( alg1 ) == PSA_ALG_ANY_HASH )
return( alg2 );
if( PSA_ALG_SIGN_GET_HASH( alg1 ) == PSA_ALG_ANY_HASH )
return( alg1 );
}
/* If the policies are incompatible, allow nothing. */
return( 0 );
}
/** Test whether a policy permits an algorithm. /** Test whether a policy permits an algorithm.
* *
* The caller must test usage flags separately. * The caller must test usage flags separately.
@ -736,6 +762,18 @@ static int psa_key_policy_permits( const psa_key_policy_t *policy,
return( 0 ); return( 0 );
} }
static psa_status_t psa_restrict_key_policy(
psa_key_policy_t *policy,
const psa_key_policy_t *constraint )
{
psa_algorithm_t intersection_alg =
psa_key_policy_algorithm_intersection( policy->alg, constraint->alg );
if( intersection_alg == 0 && policy->alg != 0 && constraint->alg != 0 )
return( PSA_ERROR_INVALID_ARGUMENT );
policy->usage &= constraint->usage;
return( PSA_SUCCESS );
}
/** Retrieve a slot which must contain a key. The key must have allow all the /** Retrieve a slot which must contain a key. The key must have allow all the
* usage flags set in \p usage. If \p alg is nonzero, the key must allow * usage flags set in \p usage. If \p alg is nonzero, the key must allow
* operations with this algorithm. */ * operations with this algorithm. */
@ -923,11 +961,11 @@ psa_status_t psa_get_key_information( psa_key_handle_t handle,
return( PSA_SUCCESS ); return( PSA_SUCCESS );
} }
static psa_status_t psa_internal_export_key( psa_key_slot_t *slot, static psa_status_t psa_internal_export_key( const psa_key_slot_t *slot,
uint8_t *data, uint8_t *data,
size_t data_size, size_t data_size,
size_t *data_length, size_t *data_length,
int export_public_key ) int export_public_key )
{ {
*data_length = 0; *data_length = 0;
@ -1110,6 +1148,65 @@ exit:
} }
#endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */ #endif /* defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) */
static psa_status_t psa_copy_key_material( const psa_key_slot_t *source,
psa_key_handle_t target )
{
psa_status_t status;
uint8_t *buffer = NULL;
size_t buffer_size = 0;
size_t length;
buffer_size = PSA_KEY_EXPORT_MAX_SIZE( source->type,
psa_get_key_bits( source ) );
buffer = mbedtls_calloc( 1, buffer_size );
if( buffer == NULL )
{
status = PSA_ERROR_INSUFFICIENT_MEMORY;
goto exit;
}
status = psa_internal_export_key( source, buffer, buffer_size, &length, 0 );
if( status != PSA_SUCCESS )
goto exit;
status = psa_import_key( target, source->type, buffer, length );
exit:
return( status );
}
psa_status_t psa_copy_key(psa_key_handle_t source_handle,
psa_key_handle_t target_handle,
const psa_key_policy_t *constraint)
{
psa_key_slot_t *source_slot = NULL;
psa_key_slot_t *target_slot = NULL;
psa_key_policy_t new_policy;
psa_status_t status;
status = psa_get_key_from_slot( source_handle, &source_slot, 0, 0 );
if( status != PSA_SUCCESS )
return( status );
status = psa_get_empty_key_slot( target_handle, &target_slot );
if( status != PSA_SUCCESS )
return( status );
new_policy = target_slot->policy;
status = psa_restrict_key_policy( &new_policy, &source_slot->policy );
if( status != PSA_SUCCESS )
return( status );
if( constraint != NULL )
{
status = psa_restrict_key_policy( &new_policy, constraint );
if( status != PSA_SUCCESS )
return( status );
}
status = psa_copy_key_material( source_slot, target_handle );
if( status != PSA_SUCCESS )
return( status );
target_slot->policy = new_policy;
return( PSA_SUCCESS );
}
/****************************************************************/ /****************************************************************/

View file

@ -65,6 +65,18 @@ Create not supported
depends_on:!MBEDTLS_PSA_CRYPTO_STORAGE_C depends_on:!MBEDTLS_PSA_CRYPTO_STORAGE_C
create_fail:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_ERROR_NOT_SUPPORTED create_fail:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_ERROR_NOT_SUPPORTED
Copy volatile to volatile
copy_across_lifetimes:PSA_KEY_LIFETIME_VOLATILE:0:PSA_KEY_USAGE_EXPORT:0:PSA_KEY_TYPE_RAW_DATA:"4142434445":PSA_KEY_LIFETIME_VOLATILE:0:PSA_KEY_USAGE_EXPORT:0:-1:-1:PSA_KEY_USAGE_EXPORT:0
Copy volatile to persistent
copy_across_lifetimes:PSA_KEY_LIFETIME_VOLATILE:0:PSA_KEY_USAGE_EXPORT:0:PSA_KEY_TYPE_RAW_DATA:"4142434445":PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_USAGE_EXPORT:0:-1:-1:PSA_KEY_USAGE_EXPORT:0
Copy persistent to volatile
copy_across_lifetimes:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_USAGE_EXPORT:0:PSA_KEY_TYPE_RAW_DATA:"4142434445":PSA_KEY_LIFETIME_VOLATILE:0:PSA_KEY_USAGE_EXPORT:0:-1:-1:PSA_KEY_USAGE_EXPORT:0
Copy persistent to persistent
copy_across_lifetimes:PSA_KEY_LIFETIME_PERSISTENT:1:PSA_KEY_USAGE_EXPORT:0:PSA_KEY_TYPE_RAW_DATA:"4142434445":PSA_KEY_LIFETIME_PERSISTENT:2:PSA_KEY_USAGE_EXPORT:0:-1:-1:PSA_KEY_USAGE_EXPORT:0
Close/destroy invalid handle Close/destroy invalid handle
invalid_handle: invalid_handle:

View file

@ -293,6 +293,115 @@ exit:
} }
/* END_CASE */ /* END_CASE */
/* BEGIN_CASE */
void copy_across_lifetimes( int source_lifetime_arg, int source_id_arg,
int source_usage_arg, int source_alg_arg,
int type_arg, data_t *material,
int target_lifetime_arg, int target_id_arg,
int target_usage_arg, int target_alg_arg,
int constraint_usage_arg, int constraint_alg_arg,
int expected_usage_arg, int expected_alg_arg )
{
psa_key_lifetime_t source_lifetime = source_lifetime_arg;
psa_key_id_t source_id = source_id_arg;
psa_key_usage_t source_usage = source_usage_arg;
psa_algorithm_t source_alg = source_alg_arg;
psa_key_handle_t source_handle = 0;
psa_key_policy_t source_policy = PSA_KEY_POLICY_INIT;
psa_key_type_t source_type = type_arg;
size_t source_bits;
psa_key_lifetime_t target_lifetime = target_lifetime_arg;
psa_key_id_t target_id = target_id_arg;
psa_key_usage_t target_usage = target_usage_arg;
psa_algorithm_t target_alg = target_alg_arg;
psa_key_handle_t target_handle = 0;
psa_key_policy_t target_policy = PSA_KEY_POLICY_INIT;
psa_key_type_t target_type;
size_t target_bits;
psa_key_usage_t constraint_usage = constraint_usage_arg;
psa_algorithm_t constraint_alg = constraint_alg_arg;
psa_key_policy_t constraint = PSA_KEY_POLICY_INIT;
psa_key_policy_t *p_constraint = NULL;
psa_key_usage_t expected_usage = expected_usage_arg;
psa_algorithm_t expected_alg = expected_alg_arg;
uint8_t *export_buffer = NULL;
if( constraint_usage_arg != -1 )
{
p_constraint = &constraint;
psa_key_policy_set_usage( p_constraint,
constraint_usage, constraint_alg );
}
TEST_MAX_KEY_ID( source_id );
TEST_MAX_KEY_ID( target_id );
PSA_ASSERT( psa_crypto_init( ) );
/* Populate the source slot. */
if( source_lifetime == PSA_KEY_LIFETIME_VOLATILE )
PSA_ASSERT( psa_allocate_key( &source_handle ) );
else
PSA_ASSERT( psa_create_key( source_lifetime, source_id,
&source_handle ) );
psa_key_policy_set_usage( &source_policy, source_usage, source_alg );
PSA_ASSERT( psa_set_key_policy( source_handle, &source_policy ) );
PSA_ASSERT( psa_import_key( source_handle, source_type,
material->x, material->len ) );
PSA_ASSERT( psa_get_key_information( source_handle, NULL, &source_bits ) );
/* Prepare the target slot. */
if( target_lifetime == PSA_KEY_LIFETIME_VOLATILE )
PSA_ASSERT( psa_allocate_key( &target_handle ) );
else
PSA_ASSERT( psa_create_key( target_lifetime, target_id,
&target_handle ) );
psa_key_policy_set_usage( &target_policy, target_usage, target_alg );
PSA_ASSERT( psa_set_key_policy( target_handle, &target_policy ) );
target_policy = psa_key_policy_init();
/* Copy the key. */
PSA_ASSERT( psa_copy_key( source_handle, target_handle, p_constraint ) );
/* Destroy the source to ensure that this doesn't affect the target. */
PSA_ASSERT( psa_destroy_key( source_handle ) );
/* If the target key is persistent, restart the system to make
* sure that the material is still alive. */
if( target_lifetime != PSA_KEY_LIFETIME_VOLATILE )
{
mbedtls_psa_crypto_free( );
PSA_ASSERT( psa_crypto_init( ) );
PSA_ASSERT( psa_open_key( target_lifetime, target_id,
&target_handle ) );
}
/* Test that the target slot has the expected content. */
PSA_ASSERT( psa_get_key_information( target_handle,
&target_type, &target_bits ) );
TEST_ASSERT( source_type == target_type );
TEST_ASSERT( source_bits == target_bits );
PSA_ASSERT( psa_get_key_policy( target_handle, &target_policy ) );
TEST_ASSERT( expected_usage == psa_key_policy_get_usage( &target_policy ) );
TEST_ASSERT( expected_alg == psa_key_policy_get_algorithm( &target_policy ) );
if( expected_usage & PSA_KEY_USAGE_EXPORT )
{
size_t length;
ASSERT_ALLOC( export_buffer, material->len );
PSA_ASSERT( psa_export_key( target_handle, export_buffer,
material->len, &length ) );
ASSERT_COMPARE( material->x, material->len,
export_buffer, length );
}
exit:
mbedtls_psa_crypto_free( );
mbedtls_free( export_buffer );
#if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C)
psa_purge_key_storage( );
#endif
}
/* END_CASE */
/* BEGIN_CASE */ /* BEGIN_CASE */
void invalid_handle( ) void invalid_handle( )
{ {