From e8efa3911ce287a2bee25d7356869e9efab93a17 Mon Sep 17 00:00:00 2001 From: gabor-mezei-arm Date: Wed, 14 Apr 2021 21:14:28 +0200 Subject: [PATCH] Implement psa_sign_message and psa_verify_message functions Signed-off-by: gabor-mezei-arm --- include/psa/crypto.h | 119 ++++++++++++++++- include/psa/crypto_values.h | 37 ++++++ library/psa_crypto.c | 136 ++++++++++++++++++++ programs/psa/psa_constant_names_generated.c | 14 ++ 4 files changed, 305 insertions(+), 1 deletion(-) diff --git a/include/psa/crypto.h b/include/psa/crypto.h index 94b8f9916..478c0c073 100644 --- a/include/psa/crypto.h +++ b/include/psa/crypto.h @@ -2889,6 +2889,123 @@ psa_status_t psa_aead_abort(psa_aead_operation_t *operation); * @{ */ +/** + * \brief Sign a message with a private key. For hash-and-sign algorithms, + * this includes the hashing step. + * + * \note To perform a multi-part hash-and-sign signature algorithm, first use + * a multi-part hash operation and then pass the resulting hash to + * psa_sign_hash(). PSA_ALG_GET_HASH(\p alg) can be used to determine the + * hash algorithm to use. + * + * \param[in] key Identifier of the key to use for the operation. + * It must be an asymmetric key pair. The key must + * allow the usage #PSA_KEY_USAGE_SIGN_MESSAGE. + * \param[in] alg An asymmetric signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg) + * is true), that is compatible with the type of + * \p key. + * \param[in] input The input message to sign. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] signature Buffer where the signature is to be written. + * \param[in] signature_size Size of the \p signature buffer in bytes. This + * must be appropriate for the selected + * algorithm and key: + * - The required signature size is + * #PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, \p alg) + * where key_type and key_bits are the type and + * bit-size respectively of key. + * - #PSA_SIGNATURE_MAX_SIZE evaluates to the + * maximum signature size of any supported + * signature algorithm. + * \param[out] signature_length On success, the number of bytes that make up + * the returned signature value. + * + * \retval #PSA_SUCCESS + * \retval #PSA_ERROR_INVALID_HANDLE + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag, + * or it does not permit the requested algorithm. + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + * The size of the \p signature buffer is too small. You can + * determine a sufficient buffer size by calling + * #PSA_SIGN_OUTPUT_SIZE(\c key_type, \c key_bits, \p alg) + * where \c key_type and \c key_bits are the type and bit-size + * respectively of \p key. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_COMMUNICATION_FAILURE + * \retval #PSA_ERROR_HARDWARE_FAILURE + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_STORAGE_FAILURE + * \retval #PSA_ERROR_DATA_CORRUPT + * \retval #PSA_ERROR_DATA_INVALID + * \retval #PSA_ERROR_INSUFFICIENT_ENTROPY + * \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_sign_message( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t * input, + size_t input_length, + uint8_t * signature, + size_t signature_size, + size_t * signature_length ); + +/** \brief Verify the signature of a message with a public key, using + * a hash-and-sign verification algorithm. + * + * \note To perform a multi-part hash-and-sign signature verification + * algorithm, first use a multi-part hash operation to hash the message + * and then pass the resulting hash to psa_verify_hash(). + * PSA_ALG_GET_HASH(\p alg) can be used to determine the hash algorithm + * to use. + * + * \param[in] key Identifier of the key to use for the operation. + * It must be a public key or an asymmetric key + * pair. The key must allow the usage + * #PSA_KEY_USAGE_VERIFY_MESSAGE. + * \param[in] alg An asymmetric signature algorithm (PSA_ALG_XXX + * value such that #PSA_ALG_IS_SIGN_MESSAGE(\p alg) + * is true), that is compatible with the type of + * \p key. + * \param[in] input The message whose signature is to be verified. + * \param[in] input_length Size of the \p input buffer in bytes. + * \param[out] signature Buffer containing the signature to verify. + * \param[in] signature_length Size of the \p signature buffer in bytes. + * + * \retval #PSA_SUCCESS + * \retval #PSA_ERROR_INVALID_HANDLE + * \retval #PSA_ERROR_NOT_PERMITTED + * The key does not have the #PSA_KEY_USAGE_SIGN_MESSAGE flag, + * or it does not permit the requested algorithm. + * \retval #PSA_ERROR_INVALID_SIGNATURE + * The calculation was performed successfully, but the passed signature + * is not a valid signature. + * \retval #PSA_ERROR_NOT_SUPPORTED + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_COMMUNICATION_FAILURE + * \retval #PSA_ERROR_HARDWARE_FAILURE + * \retval #PSA_ERROR_CORRUPTION_DETECTED + * \retval #PSA_ERROR_STORAGE_FAILURE + * \retval #PSA_ERROR_DATA_CORRUPT + * \retval #PSA_ERROR_DATA_INVALID + * \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_verify_message( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t * input, + size_t input_length, + const uint8_t * signature, + size_t signature_length ); + /** * \brief Sign a hash or short message with a private key. * @@ -2942,7 +3059,7 @@ psa_status_t psa_sign_hash(mbedtls_svc_key_id_t key, size_t *signature_length); /** - * \brief Verify the signature a hash or short message using a public key. + * \brief Verify the signature of a hash or short message using a public key. * * Note that to perform a hash-and-sign signature algorithm, you must * first calculate the hash by calling psa_hash_setup(), psa_hash_update() diff --git a/include/psa/crypto_values.h b/include/psa/crypto_values.h index 9bfd5ab1c..5de21de9e 100644 --- a/include/psa/crypto_values.h +++ b/include/psa/crypto_values.h @@ -1487,6 +1487,23 @@ PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_HASH_EDDSA(alg) || \ PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg)) + +/** Whether the specified algorithm is a signature algorithm that can be used + * with psa_sign_message() and psa_verify_message(). + * + * \param alg An algorithm identifier (value of type #psa_algorithm_t). + * + * \return 1 if alg is a signature algorithm that can be used to sign a + * message. 0 if alg is a signature algorithm that can only be used + * to sign an already-calculated hash. 0 if alg is not a signature + * algorithm. This macro can return either 0 or 1 if alg is not a + * supported algorithm identifier. + */ +#define PSA_ALG_IS_SIGN_MESSAGE(alg) \ + (PSA_ALG_IS_RSA_PSS(alg) || PSA_ALG_IS_RSA_PKCS1V15_SIGN(alg) || \ + PSA_ALG_IS_ECDSA(alg) || PSA_ALG_IS_HASH_EDDSA(alg) || \ + PSA_ALG_IS_VENDOR_HASH_AND_SIGN(alg)) + /** Get the hash used by a hash-and-sign signature algorithm. * * A hash-and-sign algorithm is a signature algorithm which is @@ -2063,6 +2080,26 @@ static inline int mbedtls_svc_key_id_is_null( mbedtls_svc_key_id_t key ) */ #define PSA_KEY_USAGE_DECRYPT ((psa_key_usage_t)0x00000200) +/** Whether the key may be used to sign a message. + * + * This flag allows the key to be used for a MAC calculation operation or for + * an asymmetric message signature operation, if otherwise permitted by the + * key’s type and policy. + * + * For a key pair, this concerns the private key. + */ +#define PSA_KEY_USAGE_SIGN_MESSAGE ((psa_key_usage_t)0x00000400) + +/** Whether the key may be used to verify a message. + * + * This flag allows the key to be used for a MAC verification operation or for + * an asymmetric message signature verification operation, if otherwise + * permitted by the key’s type and policy. + * + * For a key pair, this concerns the public key. + */ +#define PSA_KEY_USAGE_VERIFY_MESSAGE ((psa_key_usage_t)0x00000800) + /** Whether the key may be used to sign a message. * * This flag allows the key to be used for a MAC calculation operation diff --git a/library/psa_crypto.c b/library/psa_crypto.c index f58df4aef..c21e03bef 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -1556,6 +1556,8 @@ static psa_status_t psa_validate_key_policy( const psa_key_policy_t *policy ) PSA_KEY_USAGE_COPY | PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT | + PSA_KEY_USAGE_SIGN_MESSAGE | + PSA_KEY_USAGE_VERIFY_MESSAGE | PSA_KEY_USAGE_SIGN_HASH | PSA_KEY_USAGE_VERIFY_HASH | PSA_KEY_USAGE_DERIVE ) ) != 0 ) @@ -2840,6 +2842,140 @@ cleanup: /* Asymmetric cryptography */ /****************************************************************/ +psa_status_t psa_sign_message( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t * input, + size_t input_length, + uint8_t * signature, + size_t signature_size, + size_t * signature_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + size_t hash_length; + uint8_t hash[PSA_HASH_MAX_SIZE]; + + *signature_length = 0; + + if( ! PSA_ALG_IS_SIGN_MESSAGE( alg ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + if ( ! PSA_ALG_IS_HASH( PSA_ALG_SIGN_GET_HASH( alg ) ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + /* Immediately reject a zero-length signature buffer. This guarantees + * that signature must be a valid pointer. (On the other hand, the hash + * buffer can in principle be empty since it doesn't actually have + * to be a hash.) */ + if( signature_size == 0 ) + return( PSA_ERROR_BUFFER_TOO_SMALL ); + + status = psa_get_and_lock_key_slot_with_policy( key, &slot, + PSA_KEY_USAGE_SIGN_MESSAGE, + alg ); + if( status != PSA_SUCCESS ) + goto exit; + + if( ! PSA_KEY_TYPE_IS_KEY_PAIR( slot->attr.type ) ) + { + status = PSA_ERROR_INVALID_ARGUMENT; + goto exit; + } + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_driver_wrapper_hash_compute( PSA_ALG_SIGN_GET_HASH( alg ), + input, input_length, + hash, sizeof( hash ), + &hash_length ); + + if( status != PSA_SUCCESS ) + { + memset( hash, 0, sizeof( hash ) ); + goto exit; + } + + status = psa_driver_wrapper_sign_hash( + &attributes, slot->key.data, slot->key.bytes, + alg, hash, hash_length, + signature, signature_size, signature_length ); + + memset( hash, 0, hash_length ); + +exit: + /* Fill the unused part of the output buffer (the whole buffer on error, + * the trailing part on success) with something that isn't a valid signature + * (barring an attack on the signature and deliberately-crafted input), + * in case the caller doesn't check the return status properly. */ + if( status == PSA_SUCCESS ) + memset( signature + *signature_length, '!', + signature_size - *signature_length ); + else + memset( signature, '!', signature_size ); + /* If signature_size is 0 then we have nothing to do. We must not call + * memset because signature may be NULL in this case. */ + + unlock_status = psa_unlock_key_slot( slot ); + + return( ( status == PSA_SUCCESS ) ? unlock_status : status ); +} + +psa_status_t psa_verify_message( mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t * input, + size_t input_length, + const uint8_t * signature, + size_t signature_length ) +{ + psa_status_t status = PSA_ERROR_CORRUPTION_DETECTED; + psa_status_t unlock_status = PSA_ERROR_CORRUPTION_DETECTED; + psa_key_slot_t *slot; + size_t hash_length; + uint8_t hash[PSA_HASH_MAX_SIZE]; + + if( ! PSA_ALG_IS_SIGN_MESSAGE( alg ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + if ( ! PSA_ALG_IS_HASH( PSA_ALG_SIGN_GET_HASH( alg ) ) ) + return( PSA_ERROR_INVALID_ARGUMENT ); + + status = psa_get_and_lock_key_slot_with_policy( key, &slot, + PSA_KEY_USAGE_VERIFY_MESSAGE, + alg ); + if( status != PSA_SUCCESS ) + return( status ); + + psa_key_attributes_t attributes = { + .core = slot->attr + }; + + status = psa_driver_wrapper_hash_compute( PSA_ALG_SIGN_GET_HASH( alg ), + input, input_length, + hash, sizeof( hash ), + &hash_length ); + + if( status != PSA_SUCCESS ) + { + memset( hash, 0, sizeof( hash ) ); + goto exit; + } + + status = psa_driver_wrapper_verify_hash( + &attributes, slot->key.data, slot->key.bytes, + alg, hash, hash_length, + signature, signature_length ); + + memset( hash, 0, hash_length ); + +exit: + unlock_status = psa_unlock_key_slot( slot ); + + return( ( status == PSA_SUCCESS ) ? unlock_status : status ); +} + psa_status_t psa_sign_hash_internal( const psa_key_attributes_t *attributes, const uint8_t *key_buffer, size_t key_buffer_size, diff --git a/programs/psa/psa_constant_names_generated.c b/programs/psa/psa_constant_names_generated.c index dcbe87ff0..bebb97c5c 100644 --- a/programs/psa/psa_constant_names_generated.c +++ b/programs/psa/psa_constant_names_generated.c @@ -394,6 +394,13 @@ static int psa_snprint_key_usage(char *buffer, size_t buffer_size, append(&buffer, buffer_size, &required_size, "PSA_KEY_USAGE_SIGN_HASH", 23); usage ^= PSA_KEY_USAGE_SIGN_HASH; } + if (usage & PSA_KEY_USAGE_SIGN_MESSAGE) { + if (required_size != 0) { + append(&buffer, buffer_size, &required_size, " | ", 3); + } + append(&buffer, buffer_size, &required_size, "PSA_KEY_USAGE_SIGN_MESSAGE", 26); + usage ^= PSA_KEY_USAGE_SIGN_MESSAGE; + } if (usage & PSA_KEY_USAGE_VERIFY_HASH) { if (required_size != 0) { append(&buffer, buffer_size, &required_size, " | ", 3); @@ -401,6 +408,13 @@ static int psa_snprint_key_usage(char *buffer, size_t buffer_size, append(&buffer, buffer_size, &required_size, "PSA_KEY_USAGE_VERIFY_HASH", 25); usage ^= PSA_KEY_USAGE_VERIFY_HASH; } + if (usage & PSA_KEY_USAGE_VERIFY_MESSAGE) { + if (required_size != 0) { + append(&buffer, buffer_size, &required_size, " | ", 3); + } + append(&buffer, buffer_size, &required_size, "PSA_KEY_USAGE_VERIFY_MESSAGE", 28); + usage ^= PSA_KEY_USAGE_VERIFY_MESSAGE; + } if (usage != 0) { if (required_size != 0) { append(&buffer, buffer_size, &required_size, " | ", 3);