diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index bd6ba5e5..c22bf274 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -6,7 +6,10 @@ typedef uint32_t psa_algorithm_t; typedef /* implementation-defined type */ psa_cipher_operation_t; typedef uint8_t psa_dh_family_t; typedef uint8_t psa_ecc_family_t; +typedef /* implementation-defined type */ psa_export_public_key_iop_t; +typedef /* implementation-defined type */ psa_generate_key_iop_t; typedef /* implementation-defined type */ psa_hash_operation_t; +typedef /* implementation-defined type */ psa_key_agreement_iop_t; typedef /* implementation-defined type */ psa_key_attributes_t; typedef /* implementation-defined type */ psa_key_derivation_operation_t; typedef uint16_t psa_key_derivation_step_t; @@ -24,6 +27,8 @@ typedef uint32_t psa_pake_primitive_t; typedef uint8_t psa_pake_primitive_type_t; typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; +typedef /* implementation-defined type */ psa_sign_iop_t; +typedef /* implementation-defined type */ psa_verify_iop_t; typedef struct psa_custom_key_parameters_t { uint32_t flags; } psa_custom_key_parameters_t; @@ -232,9 +237,11 @@ typedef struct psa_custom_key_parameters_t { #define PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ #define PSA_EXPORT_KEY_PAIR_MAX_SIZE /* implementation-defined value */ +#define PSA_EXPORT_PUBLIC_KEY_IOP_INIT /* implementation-defined value */ #define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE /* implementation-defined value */ #define PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ +#define PSA_GENERATE_KEY_IOP_INIT /* implementation-defined value */ #define PSA_HASH_BLOCK_LENGTH(alg) /* implementation-defined value */ #define PSA_HASH_LENGTH(alg) /* implementation-defined value */ #define PSA_HASH_MAX_SIZE /* implementation-defined value */ @@ -246,6 +253,8 @@ typedef struct psa_custom_key_parameters_t { /* specification-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_MAX_SIZE /* implementation-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_SIZE(alg) /* specification-defined value */ +#define PSA_IOP_MAX_OPS_UNLIMITED UINT32_MAX +#define PSA_KEY_AGREEMENT_IOP_INIT /* implementation-defined value */ #define PSA_KEY_ATTRIBUTES_INIT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_CONTEXT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_COST /* implementation-defined value */ @@ -376,10 +385,12 @@ typedef struct psa_custom_key_parameters_t { #define PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ #define PSA_SIGNATURE_MAX_SIZE /* implementation-defined value */ +#define PSA_SIGN_IOP_INIT /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ #define PSA_TLS12_ECJPAKE_TO_PMS_OUTPUT_SIZE 32 #define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE /* implementation-defined value */ +#define PSA_VERIFY_IOP_INIT /* implementation-defined value */ psa_status_t psa_aead_abort(psa_aead_operation_t * operation); psa_status_t psa_aead_decrypt(psa_key_id_t key, psa_algorithm_t alg, @@ -525,6 +536,15 @@ psa_status_t psa_export_public_key(psa_key_id_t key, uint8_t * data, size_t data_size, size_t * data_length); +psa_status_t psa_export_public_key_iop_abort(psa_export_public_key_iop_t * operation); +psa_status_t psa_export_public_key_iop_complete(psa_export_public_key_iop_t * operation, + uint8_t * data, + size_t data_size, + size_t * data_length); +uint32_t psa_export_public_key_iop_get_num_ops(psa_export_public_key_iop_t * operation); +psa_export_public_key_iop_t psa_export_public_key_iop_init(void); +psa_status_t psa_export_public_key_iop_setup(psa_export_public_key_iop_t * operation, + psa_key_id_t key); psa_status_t psa_generate_key(const psa_key_attributes_t * attributes, psa_key_id_t * key); psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, @@ -532,6 +552,13 @@ psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, const uint8_t * custom_data, size_t custom_data_length, mbedtls_svc_key_id_t * key); +psa_status_t psa_generate_key_iop_abort(psa_generate_key_iop_t * operation); +psa_status_t psa_generate_key_iop_complete(psa_generate_key_iop_t * operation, + psa_key_id_t * key); +uint32_t psa_generate_key_iop_get_num_ops(psa_generate_key_iop_t * operation); +psa_generate_key_iop_t psa_generate_key_iop_init(void); +psa_status_t psa_generate_key_iop_setup(psa_generate_key_iop_t * operation, + const psa_key_attributes_t * attributes); psa_status_t psa_generate_random(uint8_t * output, size_t output_size); psa_algorithm_t psa_get_key_algorithm(const psa_key_attributes_t * attributes); @@ -580,12 +607,25 @@ psa_status_t psa_import_key(const psa_key_attributes_t * attributes, const uint8_t * data, size_t data_length, psa_key_id_t * key); +uint32_t psa_iop_get_max_ops(void); +void psa_iop_set_max_ops(uint32_t max_ops); psa_status_t psa_key_agreement(psa_key_id_t private_key, const uint8_t * peer_key, size_t peer_key_length, psa_algorithm_t alg, const psa_key_attributes_t * attributes, psa_key_id_t * key); +psa_status_t psa_key_agreement_iop_abort(psa_key_agreement_iop_t * operation); +psa_status_t psa_key_agreement_iop_complete(psa_key_agreement_iop_t * operation, + psa_key_id_t * key); +uint32_t psa_key_agreement_iop_get_num_ops(psa_key_agreement_iop_t * operation); +psa_key_agreement_iop_t psa_key_agreement_iop_init(void); +psa_status_t psa_key_agreement_iop_setup(psa_key_agreement_iop_t * operation, + psa_key_id_t private_key, + const uint8_t * peer_key, + size_t peer_key_length, + psa_algorithm_t alg, + const psa_key_attributes_t * attributes); psa_key_attributes_t psa_key_attributes_init(void); psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t * operation); psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t * operation, @@ -724,6 +764,23 @@ psa_status_t psa_sign_hash(psa_key_id_t key, uint8_t * signature, size_t signature_size, size_t * signature_length); +psa_status_t psa_sign_iop_abort(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_complete(psa_sign_iop_t * operation, + uint8_t * signature, + size_t signature_size, + size_t * signature_length); +uint32_t psa_sign_iop_get_num_ops(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_hash(psa_sign_iop_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_sign_iop_t psa_sign_iop_init(void); +psa_status_t psa_sign_iop_setup(psa_sign_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); +psa_status_t psa_sign_iop_setup_complete(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_update(psa_sign_iop_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_sign_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, @@ -737,6 +794,22 @@ psa_status_t psa_verify_hash(psa_key_id_t key, size_t hash_length, const uint8_t * signature, size_t signature_length); +psa_status_t psa_verify_iop_abort(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_complete(psa_verify_iop_t * operation); +uint32_t psa_verify_iop_get_num_ops(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_hash(psa_verify_iop_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_verify_iop_t psa_verify_iop_init(void); +psa_status_t psa_verify_iop_setup(psa_verify_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t * signature, + size_t signature_length); +psa_status_t psa_verify_iop_setup_complete(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_update(psa_verify_iop_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_verify_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 202230e7..3c1249f3 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -322,6 +322,9 @@ When creating a key, the attributes for the new key are specified in a `psa_key_ See the documentation of `psa_custom_key_parameters_t` for a list of non-default production parameters. See the key type definitions in :secref:`key-types` for details of the custom production parameters used for key generation. + If an application requires bounded execution when generating a key, the implementation might provide support for interruptible key generation. + See :secref:`interruptible-generate-key`. + .. function:: psa_copy_key .. summary:: @@ -602,6 +605,10 @@ Key export Exporting a public-key object or the public part of a key pair is always permitted, regardless of the key's usage flags. + If an application requires bounded execution when exporting a public key, it can use an interruptible public-key export operation. + See :secref:`interruptible-export-key`. + + .. macro:: PSA_EXPORT_KEY_OUTPUT_SIZE :definition: /* implementation-defined value */ @@ -709,3 +716,491 @@ Key export This value must be a sufficient buffer size when calling `psa_export_key()` or `psa_export_public_key()` to export any asymmetric key pair or public key that is supported by the implementation, regardless of the exact key type and key size. See also `PSA_EXPORT_KEY_PAIR_MAX_SIZE`, `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE`, and `PSA_EXPORT_KEY_OUTPUT_SIZE()`. + +.. _interruptible-generate-key: + +Interruptible key generation +---------------------------- + +Generation of some key types can be computationally expensive. +For example, RSA keys, and elliptic curve public keys. + +For such keys, an interruptible key-generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. + +.. note:: + An implementation of the |API| does not need to provide incremental generation for all key types supported by the implementation. + Use `psa_generate_key()` to create keys for types that do not need to be incrementally generated. + +An interruptible key-generation operation is used as follows: + +1. Allocate an interruptible key-generation operation object, of type `psa_generate_key_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_generate_key_iop_t`, for example, `PSA_GENERATE_KEY_IOP_INIT`. +#. Call `psa_generate_key_iop_setup()` to specify the key attributes. +#. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_generate_key_iop_abort()`. + +.. typedef:: /* implementation-defined type */ psa_generate_key_iop_t + + .. summary:: + The type of the state data structure for an interruptible key-generation operation. + + .. versionadded:: 1.x + + Before calling any function on an interruptible key-generation operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_generate_key_iop_t operation; + + * Initialize the object to the initializer `PSA_GENERATE_KEY_IOP_INIT`, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation = PSA_GENERATE_KEY_IOP_INIT; + + * Assign the result of the function `psa_generate_key_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation; + operation = psa_generate_key_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_GENERATE_KEY_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible key-generation operation object of type `psa_generate_key_iop_t`. + + .. versionadded:: 1.x + +.. function:: psa_generate_key_iop_init + + .. summary:: + Return an initial value for an interruptible key-generation operation object. + + .. versionadded:: 1.x + + .. return:: psa_generate_key_iop_t + +.. function:: psa_generate_key_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible key-generation operation has taken so far. + + .. versionadded:: 1.x + + .. param:: psa_generate_key_iop_t * operation + The interruptible key-generation operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_generate_key_iop_setup()` or `psa_generate_key_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_generate_key_iop_setup + + .. summary:: + Start an interruptible operation to generate a key or key pair. + + .. versionadded:: 1.x + + .. param:: psa_generate_key_iop_t * operation + The interruptible key-generation operation to set up. + It must have been initialized as per the documentation for `psa_generate_key_iop_t`, and be inactive. + .. param:: const psa_key_attributes_t * attributes + The attributes for the new key. + + The following attributes are required for all keys: + + * The key type. It must not be an asymmetric public key. + * The key size. It must be a valid size for the key type. + + The following attributes must be set for keys used in cryptographic operations: + + * The key permitted-algorithm policy, see :secref:`permitted-algorithms`. + * The key usage flags, see :secref:`key-usage-flags`. + + The following attributes must be set for keys that do not use the default volatile lifetime: + + * The key lifetime, see :secref:`key-lifetimes`. + * The key identifier is required for a key with a persistent lifetime, see :secref:`key-identifiers`. + + .. note:: + This is an input parameter: it is not updated with the final key attributes. + The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_generate_key_iop_complete()`. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The implementation does not support incremental generation of the requested key type. + * The key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The key type is invalid, or is an asymmetric public key type. + * The key size is not valid for the key type. + * The key lifetime is invalid. + * The key identifier is not valid for the key lifetime. + * The key usage flags include invalid values. + * The key's permitted-usage algorithm is invalid. + * The key attributes, as a whole, are invalid. + .. retval:: PSA_ERROR_NOT_PERMITTED + The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + This function sets up the random generation of a new key. + The location, policy, type, and size of the key are taken from ``attributes``. + + Implementations must reject an attempt to generate a key of size ``0``. + + The following type-specific considerations apply: + + * For RSA keys (`PSA_KEY_TYPE_RSA_KEY_PAIR`), the public exponent is 65537. + The modulus is a product of two probabilistic primes between :math:`2^{n-1}` and :math:`2^n` where :math:`n` is the bit size specified in the attributes. + + After a successful call to `psa_generate_key_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_generate_key_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_generate_key_iop_complete()`. + * A call to `psa_generate_key_iop_abort()`. + + If `psa_generate_key_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_generate_key_iop_complete + + .. summary:: + Attempt to finish the interruptible generation of a key. + + .. versionadded:: 1.x + + .. param:: psa_generate_key_iop_t * operation + The interruptible key-generation operation to use. + The operation must be active. + .. param:: psa_key_id_t * key + On success, an identifier for the newly created key. + `PSA_KEY_ID_NULL` on failure. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + If the key is persistent, the key material and the key's metadata have been saved to persistent storage. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the new key is returned in ``key``, and the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_generate_key_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_generate_key_iop_abort + + .. summary:: + Abort an interruptible key-generation operation. + + .. versionadded:: 1.x + + .. param:: psa_generate_key_iop_t * operation + The interruptible key-generation operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. + Once aborted, the operation object can be reused for another operation by calling `psa_generate_key_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_generate_key_iop_t`. + + In particular, it is valid to call `psa_generate_key_iop_abort()` twice, or to call `psa_generate_key_iop_abort()` on an operation that has not been set up. + +.. _interruptible-export-key: + +Interruptible public-key export +------------------------------- + +Extracting a public key from an asymmetric key pair can be computationally expensive. +For example, computing an elliptic curve public key from the private key. + +An interruptible public-key export operation can be used instead of calling `psa_export_public_key()`, in applications that have bounded execution requirements for use cases that require public-key export. + +An interruptible public-key export operation is used as follows: + +1. Allocate an interruptible public-key export operation object, of type `psa_export_public_key_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_export_public_key_iop_t`, for example, `PSA_EXPORT_PUBLIC_KEY_IOP_INIT`. +#. Call `psa_export_public_key_iop_setup()` to specify the key to export. +#. Call `psa_export_public_key_iop_complete()` to finish exporting the key data, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_export_public_key_iop_abort()`. + +.. typedef:: /* implementation-defined type */ psa_export_public_key_iop_t + + .. summary:: + The type of the state data structure for an interruptible public-key export operation. + + .. versionadded:: 1.x + + Before calling any function on an interruptible public-key export operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_export_public_key_iop_t operation; + + * Initialize the object to the initializer `PSA_EXPORT_PUBLIC_KEY_IOP_INIT`, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation = PSA_EXPORT_PUBLIC_KEY_IOP_INIT; + + * Assign the result of the function `psa_export_public_key_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation; + operation = psa_export_public_key_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_EXPORT_PUBLIC_KEY_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible public-key export operation object of type `psa_export_public_key_iop_t`. + + .. versionadded:: 1.x + +.. function:: psa_export_public_key_iop_init + + .. summary:: + Return an initial value for an interruptible public-key export operation object. + + .. versionadded:: 1.x + + .. return:: psa_export_public_key_iop_t + +.. function:: psa_export_public_key_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible public-key export operation has taken so far. + + .. versionadded:: 1.x + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_export_public_key_iop_setup()` or `psa_export_public_key_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_export_public_key_iop_setup + + .. summary:: + Start an interruptible operation to export a public key or the public part of a key pair in binary format. + + .. versionadded:: 1.x + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to set up. + It must have been initialized as per the documentation for `psa_export_public_key_iop_t`, and be inactive. + .. param:: psa_key_id_t key + Identifier of the key to export. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_export_public_key_iop_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The key is neither a public key nor a key pair. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The key's storage location does not support export of the key. + * The implementation does not support export of keys with this key type. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_COMMUNICATION_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_MEMORY + + This function sets up the export of a public key in binary format. + For standard key types, the output format is defined in the relevant *Key format* section in :secref:`key-types`. + + Exporting a public key object or the public part of a key pair is always permitted, regardless of the key's usage flags. + + After a successful call to `psa_export_public_key_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_export_public_key_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. + The following events terminate an operation: + + * A successful call to `psa_export_public_key_iop_complete()`. + * A call to `psa_export_public_key_iop_abort()`. + + If `psa_export_public_key_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_export_public_key_iop_complete + + .. summary:: + Attempt to finish the interruptible export of a public key. + + .. versionadded:: 1.x + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to use. + The operation must be active. + .. param:: uint8_t * data + Buffer where the key data is to be written. + .. param:: size_t data_size + Size of the ``data`` buffer in bytes. + This must be appropriate for the key: + + * The required output size is :code:`PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits)` where ``type`` is the key type and ``bits`` is the key size in bits. + * `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE` evaluates to the maximum output size of any supported public key or public part of a key pair. + * `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` evaluates to the maximum output size of any supported public key or key pair. + .. param:: size_t * data_length + On success, the number of bytes that make up the key data. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The first ``(*data_length)`` bytes of ``data`` contain the exported public key. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BUFFER_TOO_SMALL + The size of the ``data`` buffer is too small. + `PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE()`, `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE`, or `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` can be used to determine a sufficient buffer size. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_COMMUNICATION_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_MEMORY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the public key data is returned in ``data``, and the operation becomes inactive. + The output of this function can be passed to `psa_import_key()` to create a new key that is equivalent to the public key. + + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_export_public_key_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + + .. note:: + + If the implementation of `psa_import_key()` supports other formats beyond the format specified here, the output from `psa_export_public_key_iop_complete()` must use the representation specified in :secref:`key-types`, not the originally imported representation. + +.. function:: psa_export_public_key_iop_abort + + .. summary:: + Abort an interruptible public-key export operation. + + .. versionadded:: 1.x + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. + Once aborted, the operation object can be reused for another operation by calling `psa_export_public_key_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_export_public_key_iop_t`. + + In particular, it is valid to call `psa_export_public_key_iop_abort()` twice, or to call `psa_export_public_key_iop_abort()` on an operation that has not been set up. diff --git a/doc/crypto/api/keys/policy.rst b/doc/crypto/api/keys/policy.rst index 274acf34..1b6b6061 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -192,6 +192,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_compute()` * `psa_mac_sign_setup()` * `psa_sign_message()` + * `psa_sign_iop_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -206,6 +207,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_verify()` * `psa_mac_verify_setup()` * `psa_verify_message()` + * `psa_verify_iop_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -218,6 +220,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to sign a pre-computed message hash in an asymmetric signature operation. The flag must be present on keys used with the following APIs: * `psa_sign_hash()` + * `psa_sign_iop_setup()` when signing a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_SIGN_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_SIGN_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_SIGN_MESSAGE`, and the flag `PSA_KEY_USAGE_SIGN_MESSAGE` will also be present when the application queries the usage flags of the key. @@ -232,6 +235,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to verify a pre-computed message hash in an asymmetric signature verification operation. The flag must be present on keys used with the following APIs: * `psa_verify_hash()` + * `psa_verify_iop_setup()`, when verifying the signature of a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_VERIFY_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_VERIFY_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_VERIFY_MESSAGE`, and the flag `PSA_KEY_USAGE_VERIFY_MESSAGE` will also be present when the application queries the usage flags of the key. diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index 2d734f67..97e293da 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -73,3 +73,60 @@ Library initialization .. warning:: The set of functions that depend on successful initialization of the library is :scterm:`IMPLEMENTATION DEFINED`. Applications that rely on calling functions before initializing the library might not be portable to other implementations. + + +Interruptible operation limit +----------------------------- + +Using an interruptible operation, an application can perform an expensive cryptographic computation while limiting the execution time of each function call. The execution limit is controlled via the *maximum ops* value. + +See :secref:`interruptible-operations`. + +.. function:: psa_iop_set_max_ops + + .. summary:: + Set the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. versionadded:: 1.x + + .. param:: uint32_t max_ops + The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_IOP_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. + + .. return:: void + + Interruptible functions use this value to limit the computation that is done in any single call to the function. If this limit is reached, the function will return :code:`PSA_OPERATION_INCOMPLETE`, and the caller must repeat the function call until a different status code is returned, or abort the operation. + + After initialization of the implementation, the maximum *ops* defaults to `PSA_IOP_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_iop_set_max_ops()` to set a different limit. + + .. note:: + + The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_iop_get_num_ops()` and `psa_verify_iop_get_num_ops()` are provided to help with tuning this value. + + .. admonition:: Implementation note + + The interpretation of this maximum number is obviously also implementation defined. On a hard real-time system, this can indicate a hard deadline, which is good, as a real-time system needs a guarantee of not spending more than X time, however care must be taken to avoid the situation whereby calls just return, not being able to do any actual work within the allotted time. On a non-real-time system, the implementation can be more relaxed, but again whether this number should be interpreted as as hard or soft limit or even whether a less than or equals as regards to ops executed in a single call is implementation defined. + + .. warning:: + With implementations that interpret this number as a hard limit, setting this number too small can result in an infinite loop, whereby each call results in immediate return with no computation done. + +.. function:: psa_iop_get_max_ops + + .. summary:: + Get the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. versionadded:: 1.x + + .. return:: uint32_t + Maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + This returns the value last set in a call to `psa_iop_set_max_ops()`. + +.. macro:: PSA_IOP_MAX_OPS_UNLIMITED + :definition: UINT32_MAX + + .. summary:: + Maximum value for use with `psa_iop_set_max_ops()`. + + .. versionadded:: 1.x + + Using this value in a call to `psa_iop_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/library/status.rst b/doc/crypto/api/library/status.rst index a23e58e8..d6cf48f3 100644 --- a/doc/crypto/api/library/status.rst +++ b/doc/crypto/api/library/status.rst @@ -36,6 +36,8 @@ The following elements are defined in :file:`psa/error.h` from :cite-title:`PSA- #define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152) #define PSA_ERROR_DATA_INVALID ((psa_status_t)-153) + #define PSA_OPERATION_INCOMPLETE ((psa_status_t)-248) + These definitions must be available to an application that includes the :file:`psa/crypto.h` header file. .. admonition:: Implementation note diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 9f9e2e95..c17941f2 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -9,7 +9,7 @@ Key agreement ============= -Three functions are provided for a Diffie-Hellman-style key agreement where each party combines its own private key with the peer’s public key, to produce a shared secret value: +Three functions are provided for a Diffie-Hellman-style key agreement where each party combines its own private key with the peer's public key, to produce a shared secret value: * A call to `psa_key_agreement()` will compute the shared secret and store the result in a new derivation key. @@ -17,7 +17,10 @@ Three functions are provided for a Diffie-Hellman-style key agreement where each * Where an application needs direct access to the shared secret, it can call `psa_raw_key_agreement()` instead. -Using `psa_key_agreement()` or `psa_key_derivation_key_agreement()` is recommended, as these do not expose the shared secret to the application. +If an application requires bounded execution during a key agreement, it can use an interruptible key-agreement operation. +See :secref:`interruptible-key-agreement`. + +Using `psa_key_agreement()`, `psa_key_derivation_key_agreement()`, or an interruptible key-agreement operation is recommended, as these do not expose the shared secret to the application. .. note:: @@ -173,7 +176,7 @@ Standalone key agreement .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The new key contains the share secret. + The new key contains the shared secret. If the key is persistent, the key material and the key's metadata have been saved to persistent storage. .. retval:: PSA_ERROR_INVALID_HANDLE ``private_key`` is not a valid key identifier. @@ -228,6 +231,9 @@ Standalone key agreement .. warning:: The shared secret resulting from a key-agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key-derivation algorithm is applied to the result, to derive unbiased cryptographic keys. + If an application requires bounded execution during key agreement, it can use an interruptible key-agreement operation. + See :secref:`interruptible-key-agreement`. + .. function:: psa_raw_key_agreement .. summary:: @@ -352,6 +358,290 @@ Combining key agreement and key derivation Instead, the application can call `psa_key_agreement()` to obtain the shared secret as a derivation key. This key can be used as input to as many key-derivation operations as required. +.. _interruptible-key-agreement: + +Interruptible key agreement +--------------------------- + +Most key-agreement algorithms are computationally expensive. + +An interruptible key-agreement operation can be used instead of calling `psa_key_agreement()`, in applications that have bounded execution requirements for use cases involving key agreement. + +An interruptible key-agreement operation is used as follows: + +1. Allocate an interruptible key-agreement operation object, of type `psa_key_agreement_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_key_agreement_iop_t`, for example, `PSA_KEY_AGREEMENT_IOP_INIT`. +#. Call `psa_key_agreement_iop_setup()` to specify the algorithm, and provide the private key and the peer public key. +#. Call `psa_key_agreement_iop_complete()` to finish the key agreement and output the shared secret, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_key_agreement_iop_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_key_agreement_iop_t + + .. summary:: + The type of the state data structure for an interruptible key-agreement operation. + + .. versionadded:: 1.x + + Before calling any function on an interruptible key-agreement operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_key_agreement_iop_t operation; + + * Initialize the object to the initializer `PSA_KEY_AGREEMENT_IOP_INIT`, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation = PSA_KEY_AGREEMENT_IOP_INIT; + + * Assign the result of the function `psa_key_agreement_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation; + operation = psa_key_agreement_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_KEY_AGREEMENT_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible key-agreement operation object of type `psa_key_agreement_iop_t`. + + .. versionadded:: 1.x + +.. function:: psa_key_agreement_iop_init + + .. summary:: + Return an initial value for an interruptible key-agreement operation object. + + .. versionadded:: 1.x + + .. return:: psa_key_agreement_iop_t + +.. function:: psa_key_agreement_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible key-agreement operation has taken so far. + + .. versionadded:: 1.x + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key-agreement operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_key_agreement_iop_setup()` or `psa_key_agreement_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_key_agreement_iop_setup + + .. summary:: + Start an interruptible operation to perform a key agreement. + + .. versionadded:: 1.x + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key-agreement operation to set up. + It must have been initialized as per the documentation for `psa_key_agreement_iop_t`, and be inactive. + .. param:: psa_key_id_t private_key + Identifier of the private key to use. + It must permit the usage `PSA_KEY_USAGE_DERIVE`. + .. param:: const uint8_t * peer_key + Public key of the peer. + The peer key data is parsed with the type :code:`PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type)` where ``type`` is the type of ``private_key``, and with the same bit-size as ``private_key``. + The peer key must be in the format that `psa_import_key()` accepts for this public key type. + These formats are described with the public key type in :secref:`key-types`. + .. param:: size_t peer_key_length + Size of ``peer_key`` in bytes. + .. param:: psa_algorithm_t alg + The standalone key-agreement algorithm to compute: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_STANDALONE_KEY_AGREEMENT(alg)` is true. + .. param:: const psa_key_attributes_t * attributes + The attributes for the key to be output on completion. + + The following attributes are required for all keys: + + * The key type, which must be one of `PSA_KEY_TYPE_DERIVE`, `PSA_KEY_TYPE_RAW_DATA`, `PSA_KEY_TYPE_HMAC`, or `PSA_KEY_TYPE_PASSWORD`. + + Implementations must support the `PSA_KEY_TYPE_DERIVE` and `PSA_KEY_TYPE_RAW_DATA` key types. + + The following attributes must be set for keys used in cryptographic operations: + + * The key permitted-algorithm policy, see :secref:`permitted-algorithms`. + * The key usage flags, see :secref:`key-usage-flags`. + + The following attributes must be set for keys that do not use the default volatile lifetime: + + * The key lifetime, see :secref:`key-lifetimes`. + * The key identifier is required for a key with a persistent lifetime, see :secref:`key-identifiers`. + + The following attributes are optional: + + * If the key size is nonzero, it must be equal to the output size of the key agreement, in bits. + + The output size, in bits, of the key agreement is :code:`8 * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(type, bits)`, where ``type`` and ``bits`` are the type and bit-size of ``private_key``. + + .. note:: + This is an input parameter: it is not updated with the final key attributes. + The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_key_agreement_iop_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``private_key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * ``private_key`` does not have the `PSA_KEY_USAGE_DERIVE` flag, or it does not permit the requested algorithm. + * The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not a key-agreement algorithm. + * ``private_key`` is not compatible with ``alg``. + * ``peer_key`` is not a valid public key corresponding to ``private_key``. + * The output key attributes in ``attributes`` are not valid : + + - The key type is not valid for key-agreement output. + - The key size is nonzero, and is not the size of the shared secret. + - The key lifetime is invalid. + - The key identifier is not valid for the key lifetime. + - The key usage flags include invalid values. + - The key's permitted-usage algorithm is invalid. + - The key attributes, as a whole, are invalid. + + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not a key-agreement algorithm. + * ``private_key`` is not supported for use with ``alg``. + * The output key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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_STORAGE + + This function sets up an interruptible operation to perform a key-agreement. + A key-agreement algorithm takes two inputs: a private key ``private_key``, and a public key ``peer_key``. + + When the interruptible operation completes, the shared secret is output in a key. The key's location, policy, and type are taken from ``attributes``. The size of the key is always the bit-size of the shared secret, rounded up to a whole number of bytes. + + After a successful call to `psa_key_agreement_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_key_agreement_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. + The following events terminate an operation: + + * A successful call to `psa_key_agreement_iop_complete()`. + * A call to `psa_key_agreement_iop_abort()`. + + If `psa_key_agreement_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_key_agreement_iop_complete + + .. summary:: + Attempt to finish a key agreement and return the shared secret. + + .. versionadded:: 1.x + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key-agreement operation to use. + The operation must be active. + .. param:: psa_key_id_t * key + On success, an identifier for the newly created key. + `PSA_KEY_ID_NULL` on failure. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The new key contains the shared secret. + If the key is persistent, the key material and the key's metadata have been saved to persistent storage. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the shared secret is returned as a derivation key in ``key``, and the operation becomes inactive. + The attributes of the new key are specified in the call to `psa_key_agreement_iop_setup()` used to set up this operation. + This key can be input to a key derivation operation using `psa_key_derivation_input_key()`. + + .. warning:: + The shared secret resulting from a key-agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key derivation algorithm is applied to the result, to derive unbiased cryptographic keys. + + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_key_agreement_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_key_agreement_iop_abort + + .. summary:: + Abort an interruptible key-agreement operation. + + .. versionadded:: 1.x + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key-agreement operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_key_agreement_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_key_agreement_iop_t`. + + In particular, it is valid to call `psa_key_agreement_iop_abort()` twice, or to call `psa_key_agreement_iop_abort()` on an operation that has not been set up. + Support macros -------------- diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 4a74d74d..d316db0f 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -78,7 +78,7 @@ The |API| provides several functions for calculating and verifying signatures: These functions can also be used on the specialized signature algorithms, with a hash or encoded-hash as input. See also `PSA_ALG_IS_SIGN_HASH()`. -See :secref:`single-part-signature`. +* The pair of `interruptible operations `, `psa_sign_iop_t` and `psa_verify_iop_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible-sign` and :secref:`interruptible-verify` for details on how to use these operations. .. _rsa-sign-algorithms: @@ -116,7 +116,7 @@ RSA signature algorithms .. summary:: The raw RSA PKCS#1 v1.5 signature algorithm, without hashing. - This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -334,7 +334,7 @@ ECDSA signature algorithms .. summary:: The randomized ECDSA signature scheme, without hashing. - This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -457,7 +457,7 @@ EdDSA signature algorithms .. versionadded:: 1.1 - This message signature algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions. + This message signature algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions, or with the interruptible asymmetric signature and verification operations. This is the PureEdDSA digital signature algorithm defined by :RFC-title:`8032`, using standard parameters. @@ -467,6 +467,11 @@ EdDSA signature algorithms * Edwards448: the Ed448 algorithm is computed with an empty string as the context. The output signature is a 114-byte string: the concatenation of :math:`R` and :math:`S` as defined by :RFC:`8032#5.2.6`. + .. note:: + When using an interruptible asymmetric signature operation with this algorithm, it is not possible to fragment the message data when calculating the signature. The message must be passed in a single call to `psa_sign_iop_update()`. + + However, it is possible to fragment the message data when verifying a signature using an interruptible asymmetric verification operation. + .. note:: To sign or verify the pre-computed hash of a message using EdDSA, the HashEdDSA algorithms (`PSA_ALG_ED25519PH` and `PSA_ALG_ED448PH`) can be used. @@ -565,8 +570,8 @@ EdDSA signature algorithms .. _single-part-signature: -Asymmetric signature functions ------------------------------- +Single-part asymmetric signature functions +------------------------------------------ .. function:: psa_sign_message @@ -801,6 +806,744 @@ Asymmetric signature functions Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. +.. _interruptible-sign: + +Interruptible asymmetric signature +---------------------------------- + +The interruptible asymmetric signature operation calculates the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. + +An interruptible asymmetric signature operation is used as follows: + +1. Allocate an interruptible asymmetric signature operation object, of type `psa_sign_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_sign_iop_t`, for example, `PSA_SIGN_IOP_INIT`. +#. Call `psa_sign_iop_setup()` to specify the algorithm and key. +#. Call `psa_sign_iop_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_sign_iop_hash()` with a pre-computed hash of the message to sign; or + 2. Call `psa_sign_iop_update()` one or more times, passing a fragment of the message each time. The signature that is calculated will that be of the concatenation of these fragments, in order. +#. Call `psa_sign_iop_complete()` to finish calculating the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_sign_iop_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_sign_iop_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + Before calling any function on an interruptible asymmetric signature operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_sign_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_sign_iop_t operation; + + * Initialize the object to the initializer `PSA_SIGN_IOP_INIT`, for example: + + .. code-block:: xref + + psa_sign_iop_t operation = PSA_SIGN_IOP_INIT; + + * Assign the result of the function `psa_sign_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_sign_iop_t operation; + operation = psa_sign_iop_init(); + + This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_SIGN_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_iop_t`. + + .. versionadded:: 1.x + +.. function:: psa_sign_iop_init + + .. summary:: + Return an initial value for an interruptible asymmetric signature operation object. + + .. versionadded:: 1.x + + .. return:: psa_sign_iop_t + +.. function:: psa_sign_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_sign_iop_setup()` or `psa_sign_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_sign_iop_setup + + .. summary:: + Begin the setup of an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_iop_t` and not yet in use. + .. param:: psa_key_id_t key + Identifier of the key to use for the operation. It must be an asymmetric key pair. The key must either permit the usage `PSA_KEY_USAGE_SIGN_HASH` or `PSA_KEY_USAGE_SIGN_MESSAGE`. + .. param:: psa_algorithm_t alg + An asymmetric signature algorithm: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN(alg)` is true. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation setup must now be completed by calling `psa_sign_iop_setup_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * The key has neither the `PSA_KEY_USAGE_SIGN_HASH` nor the `PSA_KEY_USAGE_SIGN_MESSAGE` usage flag. + * The key does not permit the requested algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``key`` is not supported for use with ``alg``. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not an asymmetric signature algorithm. + * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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 + + This function sets up the calculation of an asymmetric signature of a message or pre-computed hash. To verify an asymmetric signature against an expected value, use an interruptible asymmetric verification operation, see :secref:`interruptible-verify`. + + After a successful call to `psa_sign_iop_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_iop_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_sign_iop_complete()`. + * A call to `psa_sign_iop_abort()`. + + If `psa_sign_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_sign_iop_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to use. The operation must be in the process of being set up. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for input of data to sign. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must have started, but not yet finished. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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 + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation is ready for data input using a call to `psa_sign_iop_hash()` or `psa_sign_iop_update()`. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_sign_iop_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input. + .. param:: const uint8_t * hash + The input to sign. This is usually the hash of a message. + + See the description of this function, or the description of individual signature algorithms, for details of the acceptable inputs. + .. param:: size_t hash_length + Size of the ``hash`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for completion. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no data input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_SIGN_HASH` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow signing of a pre-computed hash. + * ``hash_length`` is not valid for the algorithm and key type. + * ``hash`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The implementation does not support signing of a pre-computed hash. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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 + + The application must complete the setup of the operation before calling this function. + + For hash-and-sign signature algorithms, the ``hash`` input to this function is the hash of the message to sign. The algorithm used to calculate this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output: :code:`hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg))`. + + Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. + + After input of the hash, the signature operation can be completed by calling `psa_sign_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. + +.. function:: psa_sign_iop_update + + .. summary:: + Add a message fragment to an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to use. The operation must have been set up, with no hash value input. + .. param:: const uint8_t * input + Buffer containing the message fragment to add to the signature calculation. + .. param:: size_t input_length + Size of the ``input`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no pre-computed hash value input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_SIGN_MESSAGE` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow signing of a message. + * The total input for the operation is too large for the signature algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The implementation does not support signing of a message. + * The total input for the operation is too large for the implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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 + + The application must complete the setup of the operation before calling this function. + + For message-signature algorithms that process the message data multiple times when computing a signature, `psa_sign_iop_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_sign_iop_update()`. + + After input of the message, the signature operation can be completed by calling `psa_sign_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. + + .. note:: + + To sign the zero-length message using an interruptible operation, call `psa_sign_iop_update()` once with a zero-length message fragment before calling `psa_sign_iop_complete()`. + +.. function:: psa_sign_iop_complete + + .. summary:: + Attempt to finish the interruptible calculation of an asymmetric signature. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to use. The operation must have hash or message data input, or be in the process of finishing. + .. param:: uint8_t * signature + Buffer where the signature is to be written. + .. param:: size_t signature_size + Size of the ``signature`` buffer in bytes. This must be appropriate for the selected algorithm and key: + + * The required signature size is :code:`PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg)` where ``key_type`` and ``key_bits`` are attributes of the key, and ``alg`` is the algorithm used to calculate the signature. + * `PSA_SIGNATURE_MAX_SIZE` evaluates to the maximum signature size of any supported signature algorithm. + .. param:: size_t * signature_length + On success, the number of bytes that make up the returned signature value. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The first ``(*signature_length)`` bytes of ``signature`` contain the signature value. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_sign_iop_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_BUFFER_TOO_SMALL + The size of the ``signature`` buffer is too small. + `PSA_SIGN_OUTPUT_SIZE()` or `PSA_SIGNATURE_MAX_SIZE` can be used to determine a sufficient buffer size. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_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 + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the signature is returned in ``signature``, and the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no signature is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_sign_iop_abort + + .. summary:: + Abort an interruptible asymmetric signature operation. + + .. versionadded:: 1.x + + .. param:: psa_sign_iop_t * operation + The interruptible signature operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_sign_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_sign_iop_t`. + + In particular, it is valid to call `psa_sign_iop_abort()` twice, or to call `psa_sign_iop_abort()` on an operation that has not been set up. + + +.. _interruptible-verify: + +Interruptible asymmetric verification +------------------------------------- + +The interruptible asymmetric verification operation verifies the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. + +An interruptible asymmetric verification operation is used as follows: + +1. Allocate an interruptible asymmetric verification operation object, of type `psa_verify_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_verify_iop_t`, for example, `PSA_VERIFY_IOP_INIT`. +#. Call `psa_verify_iop_setup()` to specify the algorithm, key, and the signature to verify. +#. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_verify_iop_hash()` with a pre-computed hash of the message to verify; or + 2. Call `psa_verify_iop_update()` one or more times, passing a fragment of the message each time. The signature is verified against the concatenation of these fragments, in order. +#. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_verify_iop_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + Before calling any function on an interruptible asymmetric verification operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_verify_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_verify_iop_t operation; + + * Initialize the object to the initializer `PSA_VERIFY_IOP_INIT`, for example: + + .. code-block:: xref + + psa_verify_iop_t operation = PSA_VERIFY_IOP_INIT; + + * Assign the result of the function `psa_verify_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_verify_iop_t operation; + operation = psa_verify_iop_init(); + + This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_VERIFY_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_iop_t`. + + .. versionadded:: 1.x + +.. function:: psa_verify_iop_init + + .. summary:: + Return an initial value for an interruptible asymmetric verification operation object. + + .. versionadded:: 1.x + + .. return:: psa_verify_iop_t + +.. function:: psa_verify_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible asymmetric verification operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_verify_iop_setup()` or `psa_verify_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_verify_iop_setup + + .. summary:: + Begin the setup of an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_iop_t` and not yet in use. + .. param:: psa_key_id_t key + Identifier of the key to use for the operation. It must be an asymmetric key pair or asymmetric public key. The key must either permit the usage `PSA_KEY_USAGE_VERIFY_HASH` or `PSA_KEY_USAGE_VERIFY_MESSAGE`. + .. param:: psa_algorithm_t alg + An asymmetric signature algorithm: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN(alg)` is true. + .. param:: const uint8_t * signature + Buffer containing the signature to verify. + .. param:: size_t signature_length + Size of the ``signature`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation setup must now be completed by calling `psa_verify_iop_setup_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * The key has neither the `PSA_KEY_USAGE_VERIFY_HASH` nor the `PSA_KEY_USAGE_VERIFY_MESSAGE` usage flag. + * The key does not permit the requested algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``key`` is not supported for use with ``alg``. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not an asymmetric signature algorithm. + * ``key`` is not an asymmetric key pair, or asymmetric public key, that is compatible with ``alg``. + * ``signature`` is not a valid signature for the algorithm and key. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + ``signature`` is not a valid signature for the algorithm and key. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + This function sets up the verification of an asymmetric signature of a message or pre-computed hash. To calculate an asymmetric signature, use an interruptible asymmetric signature operation, see :secref:`interruptible-sign`. + + After a successful call to `psa_verify_iop_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_iop_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_verify_iop_complete()`. + * A call to `psa_verify_iop_abort()`. + + If `psa_verify_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_verify_iop_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to use. The operation must be in the process of being set up. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for input of data to verify. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must have started, but not yet finished. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + The signature is not a valid signature for the algorithm and key. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation is ready for data input using a call to `psa_verify_iop_hash()` or `psa_verify_iop_update()`. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_verify_iop_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to use. The operation must have been set up, with no data input. + .. param:: const uint8_t * hash + The input whose signature is to be verified. This is usually the hash of a message. + + See the description of this function, or the description of individual signature algorithms, for details of the acceptable inputs. + .. param:: size_t hash_length + Size of the ``hash`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for completion. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no data input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_VERIFY_HASH` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow verification of a pre-computed hash. + * ``hash_length`` is not valid for the algorithm and key type. + * ``hash`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The implementation does not support verification of a pre-computed hash. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + The application must complete the setup of the operation before calling this function. + + For hash-and-sign signature algorithms, the ``hash`` input to this function is the hash of the message to verify. The algorithm used to calculate this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output: :code:`hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg))`. + + Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. + + After input of the hash, the verification operation can be completed by calling `psa_verify_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. + + +.. function:: psa_verify_iop_update + + .. summary:: + Add a message fragment to an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to use. The operation must have been set up, with no hash value input. + .. param:: const uint8_t * input + Buffer containing the message fragment to add to the verification. + .. param:: size_t input_length + Size of the ``input`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no pre-computed hash value input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_VERIFY_MESSAGE` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow verification of a message. + * The total input for the operation is too large for the signature algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The implementation does not support signing of a message. + * The total input for the operation is too large for the implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + The application must complete the setup of the operation before calling this function. + + For message-signature algorithms that process the message data multiple times when verifying a signature, `psa_verify_iop_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_verify_iop_update()`. + + After input of the message, the verification operation can be completed by calling `psa_verify_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. + + .. note:: + + To verify the signature of the zero-length message using an interruptible operation, call `psa_verify_iop_update()` once with a zero-length message fragment before calling `psa_verify_iop_complete()` + +.. function:: psa_verify_iop_complete + + .. summary:: + Attempt to finish the interruptible verification of an asymmetric signature. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to use. The operation must have hash or message data input, or be in the process of finishing. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The signature is valid. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_verify_iop_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + The signature is not the result of signing the input message, or hash value, with the requested algorithm, using the private key corresponding to the key provided to the operation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_verify_iop_abort + + .. summary:: + Abort an interruptible asymmetric verification operation. + + .. versionadded:: 1.x + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_verify_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_verify_iop_t`. + + In particular, it is valid to call `psa_verify_iop_abort()` twice, or to call `psa_verify_iop_abort()` on an operation that has not been set up. + Support macros -------------- diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index 187b0747..9c037c93 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -20,6 +20,14 @@ Changes to the API ~~~~~~~~~~~~~~~~~~ * Added `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` to evaluate the export buffer size for any asymmetric key pair or public key. +* Added interruptible operations for asymmetric signature and key agreement use cases. + See :secref:`interruptible-operations` and :secref:`interruptible-multi-part-operations`. + + - :secref:`interruptible-sign` + - :secref:`interruptible-verify` + - :secref:`interruptible-generate-key` + - :secref:`interruptible-export-key` + - :secref:`interruptible-key-agreement` * Add extended key-generation and key-derivation functions, `psa_generate_key_custom()` and `psa_key_derivation_output_key_custom()`, that accept additional parameters to control the key creation process. * Define a key production parameter to select a non-default exponent for RSA key generation. diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf new file mode 100644 index 00000000..49bd4536 Binary files /dev/null and b/doc/crypto/figure/interruptible_operation.pdf differ diff --git a/doc/crypto/figure/interruptible_operation.pdf.license b/doc/crypto/figure/interruptible_operation.pdf.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation.puml new file mode 100644 index 00000000..2a49b993 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.puml @@ -0,0 +1,32 @@ +' SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml +!include atg-spec.pumh + +skinparam LegendFontSize 12 + +legend bottom + --""———""-- Solid lines show successful operation + ""---"" Dashed lines show error flows + ""………"" Dotted lines show operation cancellation +end legend + +state inactive as "//inactive//" +state active as "//active//" +state error as "//error//" ##darkred + +[*] --> inactive: **Initialize** +note as N1 + Operation object starts as + uninitialised memory +end note +inactive --> active: **Setup** +active --> active: **Complete**\n//incomplete// +active --> inactive: **Complete**\n//success// +error -[#darkred,dashed]r-> inactive: **Abort** +inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +active -[#darkred,dashed]-> error: **Complete**\n//fails// +active -[#blue,dotted]l-> inactive: **Abort** + +@enduml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg new file mode 100644 index 00000000..1efea1a0 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg @@ -0,0 +1,9 @@ +inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupCompletesuccessAbortCompleteincompleteAbortSetupfailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/interruptible_operation.svg.license b/doc/crypto/figure/interruptible_operation.svg.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation_complex.pdf b/doc/crypto/figure/interruptible_operation_complex.pdf new file mode 100644 index 00000000..b2bd387c Binary files /dev/null and b/doc/crypto/figure/interruptible_operation_complex.pdf differ diff --git a/doc/crypto/figure/interruptible_operation_complex.pdf.license b/doc/crypto/figure/interruptible_operation_complex.pdf.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation_complex.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation_complex.puml b/doc/crypto/figure/interruptible_operation_complex.puml new file mode 100644 index 00000000..00a2c339 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation_complex.puml @@ -0,0 +1,44 @@ +' SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml +!include atg-spec.pumh + +skinparam LegendFontSize 12 + +legend bottom + --""———""-- Solid lines show successful operation + ""---"" Dashed lines show error flows + ""………"" Dotted lines show operation cancellation +end legend + +state inactive as "//inactive//" +state setup as "//setup//" +state input as "//input//" +state completing as "//completing//" +state error as "//error//" ##darkred + +[*] --> inactive: **Initialize** +note as N1 + Operation object starts as + uninitialised memory +end note +inactive --> setup: **Setup**\n//success// +setup --> setup: **Setup-complete**\n//incomplete// +setup --> input: **Setup-complete**\n//success// +input --> input: **Update**\n//success// +input --> completing: **Complete**\n//incomplete// +input -u-> inactive: **Complete**\n//success// +completing --> completing: **Complete**\n//incomplete// +completing -u-> inactive: **Complete**\n//success// +error -[#darkred,dashed]r-> inactive: **Abort** +inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +setup -[#darkred,dashed]-> error: **Setup-complete**\n//fails// +input -[#darkred,dashed]-> error: **Update**\n//fails// +input -[#darkred,dashed]-> error: **Complete**\n//fails// +completing -[#darkred,dashed]-> error: **Complete**\n//fails// +setup -[#blue,dotted]u-> inactive: **Abort** +input -[#blue,dotted]u-> inactive: **Abort** +completing -[#blue,dotted]u-> inactive: **Abort** + +@enduml diff --git a/doc/crypto/figure/interruptible_operation_complex.svg b/doc/crypto/figure/interruptible_operation_complex.svg new file mode 100644 index 00000000..83d741c6 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation_complex.svg @@ -0,0 +1,19 @@ +inactivesetupinputcompletingerrorOperation object starts asuninitialised memoryInitializeSetupsuccessAbortSetup-completeincompleteSetup-completesuccessUpdatesuccessCompleteincompleteCompletesuccessAbortCompleteincompleteCompletesuccessAbortAbortSetupfailsSetup-completefailsUpdatefailsCompletefailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/interruptible_operation_complex.svg.license b/doc/crypto/figure/interruptible_operation_complex.svg.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation_complex.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/multi_part_operation.pdf b/doc/crypto/figure/multi_part_operation.pdf index 2d02cf0e..72b0443d 100644 Binary files a/doc/crypto/figure/multi_part_operation.pdf and b/doc/crypto/figure/multi_part_operation.pdf differ diff --git a/doc/crypto/figure/multi_part_operation.puml b/doc/crypto/figure/multi_part_operation.puml index 28ea4b26..d7840140 100644 --- a/doc/crypto/figure/multi_part_operation.puml +++ b/doc/crypto/figure/multi_part_operation.puml @@ -24,10 +24,10 @@ end note inactive --> active: **Setup** active --> active: **Update** active --> inactive: **Finish** -error -[#darkred,dashed]-> inactive: **Abort** +error -[#darkred,dashed]r-> inactive: **Abort** inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// active -[#darkred,dashed]-> error: **Update**\n//fails// active -[#darkred,dashed]-> error: **Finish**\n//fails// -active -[#blue,dotted]-> inactive: **Abort** +active -[#blue,dotted]l-> inactive: **Abort** @enduml diff --git a/doc/crypto/figure/multi_part_operation.svg b/doc/crypto/figure/multi_part_operation.svg index cfb52f79..c3e57314 100644 --- a/doc/crypto/figure/multi_part_operation.svg +++ b/doc/crypto/figure/multi_part_operation.svg @@ -1,10 +1,10 @@ -inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupFinishAbortUpdateAbortSetupfailsUpdatefailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file +inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupFinishAbortUpdateAbortSetupfailsUpdatefailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 90f084a2..9c7ede61 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -120,7 +120,7 @@ The API supports cryptographic operations through two kinds of interfaces: * A *multi-part operation* is a set of functions that work with a stored operation state. This provides more control over operation configuration, piecewise processing of large input data, or handling for multi-step processes. See :secref:`multi-part-operations`. -Depending on the mechanism, one or both kind of interfaces may be provided. +* An *interruptible operation* is also a set of functions that work with a stored operation state. However, these operations are for computationally expensive algorithms (for example, digital signatures), and enable the application to limit the computation performed in a single function call. See :secref:`interruptible-operations`. .. _single-part-functions: @@ -133,6 +133,7 @@ Single-part functions do not meet the needs of all use cases: * Some use cases involve messages that are too large to be assembled in memory, or require non-default configuration of the algorithm. These use cases require the use of a `multi-part operation `. +* Some use cases require that the time spent in a single function call is bounded. For some algorithms, this result can be achieved using a multi-part operation. For algorithms that involve computationally expensive steps, the use case requires the use of an `interruptible operation `. .. _multi-part-operations: @@ -221,6 +222,170 @@ It is safe to move a multi-part operation object to a different memory location, Each type of multi-part operation can have multiple *active* states. Documentation for the specific operation describes the configuration and update functions, and any requirements about their usage and ordering. +See :secref:`hash-mp` for an example of a multi-part operation. + +.. _interruptible-operations: + +Interruptible operations +~~~~~~~~~~~~~~~~~~~~~~~~ + +Interruptible operations are APIs which split a single computationally-expensive operation into a sequence of separate function calls, each of which has a bounded execution time. Interruptible operations are useful in application contexts where responsiveness is critical, and techniques such a multi-threading are not available. This is achieved by limiting the amount of computational progress that is made for each function call. + +For some use cases, this can be achieved by controlling the amount of data processed by the function. For example, a hash can be computed using a multi-part operation to break up the computation into smaller blocks. For other use cases, the execution time is a consequence of the computational complexity of the algorithm, and a multi-part operation does not enable a caller to bound the runtime of each function. For example, verifying an asymmetric signature. + +.. note:: + + Unlike multi-part operations, an interruptible operation does not provide additional control over the inputs to an algorithm. If an application does not need to interrupt the operation, it is recommended that the appropriate single-part function is used. + +Use cases for which the |API| defines interruptible operations include: + +* Asymmetric signature generation and verification. +* Key exchange protocols, including the use of ephemeral key-pairs. + +There are three components in an interruptible operation: + +* A specific object type to maintain the state of the operation, in a similar way to multi-part operations. These types are implementation-defined. +* A non-error status code, :code:`PSA_OPERATION_INCOMPLETE`, that is returned by some interruptible operation functions to indicate that the computation is incomplete. The same function must be called repeatedly until it returns either a success or an error status. +* The concept of a unit of work --- called *ops* --- that can be carried out by an interruptible operation function. The amount of computation done, or time duration, for one *op* is implementation- and function- specific, and can depend on the algorithm inputs, for example, the key size. + + An application can set an overall *maximum ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *maximum ops* value can also be queried. If the *maximum ops* is not set by an application, interruptible functions will not return until the operation is complete. + + Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation is aborted, or when an operation object is set up for a new operation. This permits the final value to be queried after an operation has finished successfully. + +Interruptible operations follow a common pattern of use, which is shown in :numref:`fig-interruptible`. + +.. figure:: /figure/interruptible_operation.* + :name: fig-interruptible + + General state model for an interruptible operation + +The typical sequence of actions with a interruptible operation is as follows: + +1. **Allocate:** Allocate memory for an operation object of the appropriate type. + The application can use any allocation strategy: stack, heap, static, etc. + +#. **Initialize:** Initialize or assign the interruptible operation object by one of the following methods: + + - Set it to logical zero. + This is automatic for static and global variables. + Explicit initialization must use the associated ``PSA_xxx_IOP_INIT`` macro as the type is implementation-defined. + - Set it to all-bits zero. + This is automatic if the object was allocated with ``calloc()``. + - Assign the value of the associated macro ``PSA_xxx_IOP_INIT``. + - Assign the result of calling the associated function ``psa_xxx_iop_init()``. + + The resulting object is now *inactive*. + + It is an error to initialize an interruptible operation object that is in *active* or *error* states. This can leak memory or other resources. + +#. **Setup:** Start a new interruptible operation on an *inactive* operation object. + Each interruptible operation object will define one or more setup functions to start a specific operation. + + The accumulated *ops* value for the operation is reset to zero. + + On success, a setup function will put an interruptible operation object into an *active* state. + On failure, the operation object will remain *inactive*. + +#. **Complete:** To end an interruptible operation, call the applicable completion function. + This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. + + If the computation cannot be completed within the *maximum ops*, the interruptible operation is left in an *active* state. + If the operation completes successfully, the operation enters an *inactive* state. + On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the completion function returns with a success or an error status. + +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_iop_abort()`` function. + This will release any resources associated with the operation, return the operation object to the *inactive* state, and reset the accumulated *ops* value to zero. + + Any error that occurs to an operation while it is in an *active* state will result in the operation entering an *error* state. + The application must call the associated ``psa_xxx_iop_abort()`` function to release the operation resources and return the object to the *inactive* state. + + ``psa_xxx_iop_abort()`` can be called on an *inactive* interruptible operation, and this has no effect. + +Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the setup functions again. + +If an interruptible operation object is not initialized before use, the behavior is undefined. + +If an interruptible operation function determines that the operation object is not in any valid state, it can return :code:`PSA_ERROR_CORRUPTION_DETECTED`. + +If an interruptible operation function is called with an operation object in the wrong state, the function will return :code:`PSA_ERROR_BAD_STATE` and the operation object will enter the *error* state. + +It is safe to move an interruptible operation object to a different memory location, for example, using a bitwise copy, and then to use the object in the new location. +For example, an application can allocate an operation object on the stack and return it, or the operation object can be allocated within memory managed by a garbage collector. +However, this does not permit the following behaviors: + +* Moving the object while a function is being called on the object. See also :secref:`concurrency`. +* Working with both the original and the copied operation objects. + +Each type of interruptible operation can have multiple *active* states. +Documentation for the specific operation describes the setup and completion functions, and any requirements about their usage and ordering. + +See :secref:`interruptible-generate-key` for an example of an interruptible operation. + +.. _interruptible-multi-part-operations: + +Interruptible multi-part operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some operations combine the behaviors of multi-part operations and interruptible operations. +They enable both the incremental provision of data input, and the interruption of computationally expensive function calls. + +In these interruptible operations, there can be more than one step at which the same function is called repeatedly until it no longer returns :code:`PSA_OPERATION_INCOMPLETE`. + +:numref:`fig-interruptible-mp` shows the interruptible multi-part operation flow that is used for asymmetric signature. + +.. figure:: /figure/interruptible_operation_complex.* + :name: fig-interruptible-mp + + State model for an interruptible multi-part operation + +The sequence of actions is an integration of the multi-part and interruptible flows. +In the following sequence, detail is only provided where actions behave differently: + +1. **Allocate** + +#. **Initialize** + +#. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. + Each operation object will define one or more setup functions to start a specific operation. + + The accumulated *ops* value for the operation is reset to zero. + + On success, an operation object enters a *setup* state. + On failure, the operation object will remain *inactive*. + +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is in *setup* state. + + If the setup computation is interrupted, the operation remains in *setup* state. + If setup completes successfully, the operation enters an *input* state. + On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the setup completes with success or an error status. + +#. **Update:** Update an interruptible operation object in *input* state. + The update function can provide additional parameters, supply data for processing or generate outputs. + + On success, the operation object remains in *input* state. + On failure, the operation object will enter an *error* state. + +#. **Complete:** To end an interruptible operation, call the applicable completion function. + This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. + + If the finishing computation is interrupted, a the operation is left in *completing* state. + If the operation completes successfully, the operation enters an *inactive* state. + On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the completion function completes with success or an error status. + +#. **Abort** + +The rules for use of an interruptible operation or multi-part operation also apply to an interruptible multi-part operation. See :secref:`multi-part-operations`. + +Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. + +See :secref:`interruptible-sign` for an example of using an interruptible multi-part operation. + Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ @@ -251,7 +416,7 @@ Here is an example of a use case where a master key is used to generate both a m #. Populate a `psa_key_attributes_t` object with the derived message encryption key’s attributes. #. Call `psa_key_derivation_output_key()` to create the derived message key. #. Call `psa_key_derivation_output_bytes()` to generate the derived IV. - #. Call `psa_key_derivation_abort()` to release the key-derivation operation memory. + #. Call `psa_key_derivation_abort()` to release the key derivation operation memory. #. Encrypt the message with the derived material. @@ -276,9 +441,9 @@ This specification defines interfaces for the following types of asymmetric cryp For asymmetric encryption, the API provides *single-part* functions. -For asymmetric signature, the API provides single-part functions. +For asymmetric signature, the API provides single-part functions and *interruptible multi-part operations* (see :secref:`interruptible-multi-part-operations`). -For key agreement, the API provides single-part functions and an additional input method for a key-derivation operation. +For key agreement, the API provides single-part functions, an *interruptible operation* (see :secref:`interruptible-operations`), and an additional input method for a key derivation operation. For key encapsulation, the API provides single-part functions.