From 96590b2470c10c255e4c9b9385949b61de41d7fe Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 24 Jul 2026 00:19:23 +0100 Subject: [PATCH 01/22] Add interruptible Crypto API operations --- doc/crypto/api.db/psa/crypto.h | 73 ++ doc/crypto/api/keys/management.rst | 497 ++++++++++ doc/crypto/api/keys/policy.rst | 5 + doc/crypto/api/library/library.rst | 57 ++ doc/crypto/api/library/status.rst | 2 + doc/crypto/api/ops/key-agreement.rst | 296 +++++- doc/crypto/api/ops/signature.rst | 849 +++++++++++++++++- doc/crypto/appendix/history.rst | 24 +- doc/crypto/figure/interruptible_operation.pdf | Bin 0 -> 32413 bytes .../figure/interruptible_operation.puml | 32 + doc/crypto/figure/interruptible_operation.svg | 9 + .../interruptible_operation_complex.pdf | Bin 0 -> 34722 bytes .../interruptible_operation_complex.puml | 44 + .../interruptible_operation_complex.svg | 19 + doc/crypto/overview/functionality.rst | 172 +++- 15 files changed, 2027 insertions(+), 52 deletions(-) create mode 100644 doc/crypto/figure/interruptible_operation.pdf create mode 100644 doc/crypto/figure/interruptible_operation.puml create mode 100644 doc/crypto/figure/interruptible_operation.svg create mode 100644 doc/crypto/figure/interruptible_operation_complex.pdf create mode 100644 doc/crypto/figure/interruptible_operation_complex.puml create mode 100644 doc/crypto/figure/interruptible_operation_complex.svg diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 685d2af8..e48a7002 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,8 +27,10 @@ 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_sign_operation_t; typedef uint8_t psa_slh_dsa_family_t; +typedef /* implementation-defined type */ psa_verify_iop_t; typedef /* implementation-defined type */ psa_verify_operation_t; typedef /* implementation-defined type */ psa_xof_operation_t; typedef struct psa_custom_key_parameters_t { @@ -294,9 +299,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 */ @@ -308,6 +315,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 */ @@ -473,6 +482,7 @@ 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_OPERATION_INIT /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ @@ -482,6 +492,7 @@ typedef struct psa_custom_key_parameters_t { #define PSA_SLH_DSA_FAMILY_SHAKE_S ((psa_slh_dsa_family_t) 0x0b) #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 */ #define PSA_VERIFY_OPERATION_INIT /* implementation-defined value */ #define PSA_WRAP_KEY_OUTPUT_SIZE(wrap_key_type, alg, key_type, key_bits) \ /* implementation-defined value */ @@ -639,6 +650,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, @@ -646,6 +666,13 @@ psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, const uint8_t * custom_data, size_t custom_data_length, psa_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); @@ -694,12 +721,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, @@ -852,6 +892,23 @@ psa_status_t psa_sign_hash_with_context(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, @@ -900,6 +957,22 @@ psa_status_t psa_verify_hash_with_context(psa_key_id_t key, size_t context_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 b482a2bb..3780461c 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -237,6 +237,9 @@ When creating a key, the attributes for the new key are specified in a `psa_key_ This function is equivalent to calling `psa_generate_key_custom()` with the production parameters `PSA_CUSTOM_KEY_PARAMETERS_INIT` and ``custom_data_length == 0`` (``custom_data`` is ignored). + 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_generate_key_custom .. summary:: @@ -723,6 +726,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 */ @@ -830,3 +837,493 @@ 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. + +This operation uses the default production parameters of `psa_generate_key()`. It does not support the custom production parameters accepted by `psa_generate_key_custom()`. + +.. 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.6 + + 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.6 + +.. function:: psa_generate_key_iop_init + + .. summary:: + Return an initial value for an interruptible key-generation operation object. + + .. versionadded:: 1.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + 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.6 + +.. function:: psa_export_public_key_iop_init + + .. summary:: + Return an initial value for an interruptible public-key export operation object. + + .. versionadded:: 1.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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 afad2bfb..fc1e1741 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -210,6 +210,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_sign_message()` * `psa_sign_message_with_context()` * `psa_sign_setup()` + * `psa_sign_iop_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -226,6 +227,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_verify_message()` * `psa_verify_message_with_context()` * `psa_verify_setup()` + * `psa_verify_iop_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -239,6 +241,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_sign_hash()` * `psa_sign_hash_with_context()` + * `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. @@ -254,6 +257,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_verify_hash()` * `psa_verify_hash_with_context()` + * `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. @@ -270,6 +274,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag must be present on keys used with the following APIs: * `psa_key_agreement()` + * `psa_key_agreement_iop_setup()` * `psa_key_derivation_key_agreement()` * `psa_raw_key_agreement()` diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index c101e650..59e17514 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -74,3 +74,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 +----------------------------- + +An interruptible operation lets an application limit the computation performed by an individual function call. The limit is controlled by 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.6 + + .. param:: uint32_t max_ops + The maximum number of *ops* to execute in a single call. This can be a value from ``0`` to `PSA_IOP_MAX_OPS_UNLIMITED`. + + .. return:: void + + Interruptible functions use this value to limit the computation in a single call. If the limit is reached before the operation is complete, the function returns :code:`PSA_OPERATION_INCOMPLETE`. The application must call the function again until it returns a different status, or abort the operation. + + After implementation initialization, the maximum *ops* defaults to `PSA_IOP_MAX_OPS_UNLIMITED`. This permits an interruptible function to finish its calculation before returning. Call `psa_iop_set_max_ops()` to set a limit. + + .. note:: + + The computation and time represented by an *op* are implementation- and function-specific. They can depend on the hardware, algorithm, key type, curve, and current stage of the operation. Successive *ops* in one operation can take different amounts of time. The ``psa_xxx_iop_get_num_ops()`` functions can help tune this value. + + .. admonition:: Implementation note + + The interpretation of the maximum is implementation-defined. An implementation intended for a hard real-time system can interpret it as a hard execution limit. An implementation for a non-real-time system can use a less strict interpretation. In either case, the implementation documentation should describe how it applies the limit. + + .. warning:: + With implementations that interpret this value as a hard limit, setting it too low can prevent any useful computation. Repeated calls can then return :code:`PSA_OPERATION_INCOMPLETE` without completing the operation. + +.. 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.6 + + .. 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 by `psa_iop_set_max_ops()`, or `PSA_IOP_MAX_OPS_UNLIMITED` if the application has not set a value. + +.. macro:: PSA_IOP_MAX_OPS_UNLIMITED + :definition: UINT32_MAX + + .. summary:: + Maximum value for use with `psa_iop_set_max_ops()`. + + .. versionadded:: 1.6 + + Using this value in a call to `psa_iop_set_max_ops()` permits 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 7dbebd26..adc2c008 100644 --- a/doc/crypto/api/library/status.rst +++ b/doc/crypto/api/library/status.rst @@ -37,6 +37,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 8ac4148c..7cefeaaa 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.6 + + 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.6 + +.. function:: psa_key_agreement_iop_init + + .. summary:: + Return an initial value for an interruptible key-agreement operation object. + + .. versionadded:: 1.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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 04f5254c..b1869c49 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -129,6 +129,16 @@ For large or streamed messages, it might be necessary to compute or verify a sig See :secref:`multi-part-signature`. +.. rubric:: Signature interruptible operations + +Some applications need to bound the expensive computation performed in an individual signature API call. + +* Version 1.6 of the |API| introduces optional interruptible signature and verification operations, `psa_sign_iop_t` and `psa_verify_iop_t`. They limit the computation performed in a call and can return :code:`PSA_OPERATION_INCOMPLETE` when further calls are required. + + These operations are distinct from the multi-part operations and are intended for applications that require bounded execution time. The interruptible operations use a zero-length context when the algorithm has a context parameter. + +See :secref:`interruptible-sign` and :secref:`interruptible-verify`. + .. _rsa-sign-algorithms: RSA signature algorithms @@ -151,7 +161,7 @@ RSA signature algorithms This hash-and-sign signature algorithm can be used with both the message and hash signature functions. RSA PKCS#1 v1.5 does not have a context parameter. However, the sign or verify with context functions can be used with a zero-length context. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -206,9 +216,8 @@ RSA signature algorithms .. summary:: The raw RSA PKCS#1 v1.5 signature algorithm, without hashing. - This specialized signature algorithm can only be used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can only be used with the `psa_sign_hash()` and `psa_verify_hash()` functions, their context variants with a zero-length context, or the interruptible signature and verification operations. RSA PKCS#1 v1.5 does not have a context parameter. - However, `psa_sign_hash_with_context()` or `psa_verify_hash_with_context()` can be used with a zero-length context. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -245,7 +254,7 @@ RSA signature algorithms This hash-and-sign signature algorithm can be used with both the message and hash signature functions. RSA PSS does not have a context parameter. However, the sign or verify with context functions can be used with a zero-length context. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -287,7 +296,7 @@ RSA signature algorithms This hash-and-sign signature algorithm can be used with both the message and hash signature functions. RSA PSS does not have a context parameter. However, the sign or verify with context functions can be used with a zero-length context. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -401,7 +410,7 @@ ECDSA signature algorithms This hash-and-sign signature algorithm can be used with both the message and hash signature functions. ECDSA does not have a context parameter. However, the sign or verify with context functions can be used with a zero-length context. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. When used with `psa_sign_hash()` or `psa_verify_hash()`, the provided ``hash`` parameter is the message digest, computed using the ``hash_alg`` hash algorithm. @@ -441,9 +450,8 @@ ECDSA signature algorithms .. summary:: The randomized ECDSA signature scheme, without hashing. - This specialized signature algorithm can only be used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can only be used with the `psa_sign_hash()` and `psa_verify_hash()` functions, their context variants with a zero-length context, or the interruptible signature and verification operations. ECDSA does not have a context parameter. - However, `psa_sign_hash_with_context()` or `psa_verify_hash_with_context()` can be used with a zero-length context. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -484,7 +492,7 @@ ECDSA signature algorithms This hash-and-sign signature algorithm can be used with both the message and hash signature functions. ECDSA does not have a context parameter. However, the sign or verify with context functions can be used with a zero-length context. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. When used with `psa_sign_hash()` or `psa_verify_hash()`, the provided ``hash`` parameter is the message digest, computed using the ``hash_alg`` hash algorithm. @@ -580,6 +588,7 @@ They are used with the Edwards25519 and Edwards448 elliptic curve keys, see `PSA Both PureEdDSA and HashEdDSA can be used with contexts, which enables domain-separation when signatures are made of different message structures with the same key. For EdDSA, the context is an arbitrary byte string between zero and 255 bytes in length. +Interruptible signature operations use a zero-length context. The development of EdDSA resulted in a total of five distinct algorithms: @@ -617,7 +626,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: This message-signature algorithm can be used with the `psa_sign_message()` and `psa_verify_message()` functions. With a zero-length context, `PSA_ALG_PURE_EDDSA` can also be used with the `psa_sign_message_with_context()` and `psa_verify_message_with_context()` functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. + This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. It can also be used with the interruptible signature and verification operations. This is the PureEdDSA digital signature algorithm defined by :RFC-title:`8032`, with zero-length context. @@ -630,6 +639,11 @@ The development of EdDSA resulted in a total of five distinct algorithms: * Edwards448: the Ed448 algorithm is computed, with a zero-length 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. The signature produced by HashEdDSA is distinct from that produced by PureEdDSA. @@ -654,7 +668,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: This message-signature algorithm can be used with both the message and message with context signature functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. + This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. It can also be used with the interruptible signature and verification operations. This is the PureEdDSA digital signature algorithm defined by :RFC-title:`8032`, with a context parameter. The context parameter can be between zero and 255 bytes in length. @@ -671,6 +685,9 @@ The development of EdDSA resulted in a total of five distinct algorithms: To use a non-zero-length context, use the message-signature functions that accept a context parameter, :code:`psa_sign_message_with_context()` and :code:`psa_verify_message_with_context()` The `psa_sign_message()` and `psa_verify_message()` functions use a zero-length context when computing or verifying signatures. + .. note:: + An interruptible signature operation for this algorithm must receive the complete message in one call to `psa_sign_iop_update()`. An interruptible verification operation can receive the message in fragments. + .. 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. The signature produced by HashEdDSA is distinct from that produced by PureEdDSA. @@ -701,7 +718,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: .. versionadded:: 1.1 This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This calculates the Ed25519ph algorithm as specified in :RFC-title:`8032#5.1`, and requires an Edwards25519 curve key. @@ -751,7 +768,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: .. versionadded:: 1.1 This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This calculates the Ed448ph algorithm as specified in :RFC-title:`8032#5.2`, and requires an Edwards448 curve key. @@ -899,6 +916,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. * The signature functions without a context parameter provide a zero-length context when computing or verifying SLH-DSA signatures. * To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`. +* Interruptible signature operations use a zero-length context. .. macro:: PSA_ALG_SLH_DSA :definition: ((psa_algorithm_t) 0x06004000) @@ -910,7 +928,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. This message-signature algorithm can be used with both the message and message with context signature functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. + This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. It can also be used with the interruptible signature and verification operations. This is the pure SLH-DSA digital signature algorithm, defined by :cite-title:`FIPS205`, using hedging. SLH-DSA requires an SLH-DSA key, which determines the SLH-DSA parameter set for the operation. @@ -921,10 +939,13 @@ Context values are arbitrary strings between zero and 255 bytes in length. This algorithm has a context parameter. See the `notes on SLH-DSA contexts `_. + .. note:: + An interruptible signature operation for this algorithm must receive the complete message in one call to `psa_sign_iop_update()`. An interruptible verification operation can receive the message in fragments. + When `PSA_ALG_SLH_DSA` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_SLH_DSA` as the algorithm in a call to any single-part message signing function. - * `PSA_ALG_SLH_DSA` or `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_SLH_DSA` as the algorithm in a call to any single-part message signing function or when setting up an interruptible signature operation. + * `PSA_ALG_SLH_DSA` or `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: To sign or verify the pre-computed hash of a message using SLH-DSA, the HashSLH-DSA algorithms (`PSA_ALG_HASH_SLH_DSA()` and `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()`) can also be used with :code:`psa_sign_hash()` and :code:`psa_verify_hash()`. @@ -946,7 +967,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. This message-signature algorithm can be used with both the message and message with context signature functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. + This algorithm can be used with the multi-part verify operation, but not with the multi-part sign operation. It can also be used with the interruptible signature and verification operations. This is the pure SLH-DSA digital signature algorithm, defined by `[FIPS205]`, without hedging. SLH-DSA requires an SLH-DSA key, which determines the SLH-DSA parameter set for the operation. @@ -960,10 +981,13 @@ Context values are arbitrary strings between zero and 255 bytes in length. This algorithm has a context parameter. See the `notes on SLH-DSA contexts `_. + .. note:: + An interruptible signature operation for this algorithm must receive the complete message in one call to `psa_sign_iop_update()`. An interruptible verification operation can receive the message in fragments. + When `PSA_ALG_DETERMINISTIC_SLH_DSA` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any single-part message signing function. - * `PSA_ALG_SLH_DSA` or `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any single-part message signing function or when setting up an interruptible signature operation. + * `PSA_ALG_SLH_DSA` or `PSA_ALG_DETERMINISTIC_SLH_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: To sign or verify the pre-computed hash of a message using SLH-DSA, the HashSLH-DSA algorithms (`PSA_ALG_HASH_SLH_DSA()` and `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()`) can also be used with :code:`psa_sign_hash()` and :code:`psa_verify_hash()`. @@ -993,7 +1017,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. Unspecified if ``hash_alg`` is not a supported hash algorithm. This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pre-hashed SLH-DSA digital signature algorithm, defined by `[FIPS205]`, using hedging. SLH-DSA requires an SLH-DSA key, which determines the SLH-DSA parameter set for the operation. @@ -1011,8 +1035,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_HASH_SLH_DSA()` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_HASH_SLH_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation. - * `PSA_ALG_HASH_SLH_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_HASH_SLH_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_HASH_SLH_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: The signature produced by HashSLH-DSA is distinct from that produced by SLH-DSA. @@ -1054,7 +1078,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. Unspecified if ``hash_alg`` is not a supported hash algorithm. This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pre-hashed SLH-DSA digital signature algorithm, defined by `[FIPS205]`, without hedging. SLH-DSA requires an SLH-DSA key, which determines the SLH-DSA parameter set for the operation. @@ -1075,8 +1099,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation. - * `PSA_ALG_HASH_SLH_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_HASH_SLH_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_SLH_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: The signature produced by HashSLH-DSA is distinct from that produced by SLH-DSA. @@ -1244,6 +1268,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. * The signature functions without a context parameter provide a zero-length context when computing or verifying ML-DSA signatures. * To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`. +* Interruptible signature operations use a zero-length context. .. macro:: PSA_ALG_ML_DSA :definition: ((psa_algorithm_t) 0x06004400) @@ -1255,7 +1280,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. This message-signature algorithm can be used with both the message and message with context signature functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pure ML-DSA digital signature algorithm, defined by :cite-title:`FIPS204`, using hedging. ML-DSA requires an ML-DSA key, which determines the ML-DSA parameter set for the operation. @@ -1268,8 +1293,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_ML_DSA` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_ML_DSA` as the algorithm in a call to any message signing function or when setting up a multi-part sign operation. - * `PSA_ALG_ML_DSA` or `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_ML_DSA` as the algorithm in a call to any message signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_ML_DSA` or `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: To sign or verify the pre-computed hash of a message using ML-DSA, the HashML-DSA algorithms (`PSA_ALG_HASH_ML_DSA()` and `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()`) can also be used with :code:`psa_sign_hash()` and :code:`psa_verify_hash()`. @@ -1291,7 +1316,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. This message-signature algorithm can be used with both the message and message with context signature functions. It cannot be used to sign hashes. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pure ML-DSA digital signature algorithm, defined by :cite-title:`FIPS204`, without hedging. ML-DSA requires an ML-DSA key, which determines the ML-DSA parameter set for the operation. @@ -1307,8 +1332,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_DETERMINISTIC_ML_DSA` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signing function or when setting up a multi-part sign operation. - * `PSA_ALG_ML_DSA` or `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_ML_DSA` or `PSA_ALG_DETERMINISTIC_ML_DSA` as the algorithm in a call to any message signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: To sign or verify the pre-computed hash of a message using ML-DSA, the HashML-DSA algorithms (`PSA_ALG_HASH_ML_DSA()` and `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()`) can also be used with :code:`psa_sign_hash()` and :code:`psa_verify_hash()`. @@ -1338,7 +1363,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. Unspecified if ``hash_alg`` is not a supported hash algorithm. This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pre-hashed ML-DSA digital signature algorithm, defined by :cite-title:`FIPS204`, using hedging. ML-DSA requires an ML-DSA key, which determines the ML-DSA parameter set for the operation. @@ -1356,8 +1381,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_HASH_ML_DSA()` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_HASH_ML_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation. - * `PSA_ALG_HASH_ML_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_HASH_ML_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_HASH_ML_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: The signature produced by HashML-DSA is distinct from that produced by ML-DSA. @@ -1399,7 +1424,7 @@ Context values are arbitrary strings between zero and 255 bytes in length. Unspecified if ``hash_alg`` is not a supported hash algorithm. This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This algorithm can be used with the multi-part sign and verify operations. + This algorithm can be used with the multi-part sign and verify operations. It can also be used with the interruptible signature and verification operations. This is the pre-hashed ML-DSA digital signature algorithm, defined by :cite-title:`FIPS204`, without hedging. ML-DSA requires an ML-DSA key, which determines the ML-DSA parameter set for the operation. @@ -1420,8 +1445,8 @@ Context values are arbitrary strings between zero and 255 bytes in length. When `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` is used as a permitted algorithm in a key policy, this permits: - * `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation. - * `PSA_ALG_HASH_ML_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation. + * `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signing function or when setting up a multi-part sign operation or an interruptible signature operation. + * `PSA_ALG_HASH_ML_DSA()` or `PSA_ALG_DETERMINISTIC_HASH_ML_DSA()` as the algorithm in a call to any signature verification function or when setting up a multi-part verify operation or an interruptible verification operation. .. note:: The signature produced by HashML-DSA is distinct from that produced by ML-DSA. @@ -1714,6 +1739,8 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_sign_hash()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. + An implementation that supports interruptible signature operations can instead use `psa_sign_iop_t` when bounded execution time is required. This API has distinct availability and completion requirements, and is not a replacement for the multi-part signature API. + .. function:: psa_sign_message_with_context .. summary:: @@ -1790,6 +1817,8 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_sign_hash_with_context()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. + Interruptible signature operations use a zero-length context. To use a non-zero-length context, use this function instead. + .. function:: psa_verify_message .. summary:: @@ -1850,6 +1879,8 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_verify_hash()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. + An implementation that supports interruptible signature operations can instead use `psa_verify_iop_t` when bounded execution time is required. This API has distinct availability and completion requirements, and is not a replacement for the multi-part signature API. + .. function:: psa_verify_message_with_context .. summary:: @@ -1918,6 +1949,8 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_verify_hash_with_context()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. + Interruptible signature operations use a zero-length context. To use a non-zero-length context, use this function instead. + .. function:: psa_sign_hash .. summary:: @@ -2750,6 +2783,744 @@ Multi-part asymmetric signature operations In particular, calling `psa_verify_abort()` after the operation has been terminated by a call to `psa_verify_abort()` or `psa_verify_finish()` is safe and has no effect. +.. _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.6 + + 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.6 + +.. function:: psa_sign_iop_init + + .. summary:: + Return an initial value for an interruptible asymmetric signature operation object. + + .. versionadded:: 1.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + 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.6 + +.. function:: psa_verify_iop_init + + .. summary:: + Return an initial value for an interruptible asymmetric verification operation object. + + .. versionadded:: 1.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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.6 + + .. 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 -------------- @@ -2757,7 +3528,7 @@ Support macros :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a signature algorithm that can be used with message signature and verification functions, and with sign and verify multi-part operations. + Whether the specified algorithm is a signature algorithm that can be used with message signature and verification functions, multi-part signature operations, and interruptible signature operations. .. param:: alg An algorithm identifier: a value of type `psa_algorithm_t`. @@ -2771,7 +3542,7 @@ Support macros :definition: /* specification-defined value */ .. summary:: - Whether the specified algorithm is a signature algorithm that can be used with hash signature and verification functions. + Whether the specified algorithm is a signature algorithm that can be used with hash signature and verification functions, and with interruptible signature operations. .. param:: alg An algorithm identifier: a value of type `psa_algorithm_t`. diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index 050497ff..641f148f 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -12,21 +12,33 @@ Document change history This section provides the detailed changes made between published version of the document. -Changes in the draft GlobalPlatform publication revision -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +Changes between *1.5* and *1.6* +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Changes to the API +~~~~~~~~~~~~~~~~~~ + +* Added interruptible operations for asymmetric signatures, key generation, public-key export, and key agreement. See: + + - :secref:`interruptible-sign` and :secref:`interruptible-verify` + - :secref:`interruptible-generate-key` + - :secref:`interruptible-export-key` + - :secref:`interruptible-key-agreement` + + These APIs enable applications to bound the expensive computation performed in a single call. See :secref:`interruptible-operations`. Clarifications and fixes ~~~~~~~~~~~~~~~~~~~~~~~~ -* Corrected the WPA3-SAE operation example code: the send-confirm counter input step is `PSA_PAKE_STEP_CONFIRM_COUNT`, and the shared key is extracted from the ``wpa3_sae`` operation. -* Corrected the SPAKE2+ operation example code: the Prover input step for the Verifier confirmation value is `PSA_PAKE_STEP_CONFIRM`. +* Corrected the WPA3-SAE operation example code: the send-confirm counter input step is `PSA_PAKE_STEP_CONFIRM_COUNT`, and the shared key is extracted from the ``wpa3_sae`` operation. +* Corrected the SPAKE2+ operation example code: the Prover input step for the Verifier confirmation value is `PSA_PAKE_STEP_CONFIRM`. Other changes ~~~~~~~~~~~~~ -* Migrated the document to the 2026 PSA API template. +* Migrated the document to the 2026 PSA API template. - This changes the document front matter structure and publication styling, without changing the API. + This changes the document front matter structure and publication styling, without changing the API. Changes between *1.4.1* and *1.5* ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..49bd453646c2223f87f312e8d7eb6ba5058019a9 GIT binary patch literal 32413 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 NPJu>CE2%7n_FtLw>QVpz literal 0 HcmV?d00001 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_complex.pdf b/doc/crypto/figure/interruptible_operation_complex.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b2bd387ca619ed8adbc158be2858b2b5a85d3869 GIT binary patch literal 34722 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

jT`Z?HuwH{}J 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 P9tea+ODmxyiT1w$|A<9C literal 0 HcmV?d00001 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/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 17e7d5f2..0f351516 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -121,7 +121,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 a distinct set of functions that works with a stored operation state. It enables the application to limit the computation performed in a single function call for computationally expensive algorithms, for example digital signatures. See :secref:`interruptible-operations`. .. _single-part-functions: @@ -134,6 +134,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. Processing input data in fragments can meet this requirement for some algorithms. When an algorithm has computationally expensive steps that are independent of the input size, the use case requires an `interruptible operation `. .. _multi-part-operations: @@ -222,6 +223,169 @@ 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 split a computationally expensive operation into a sequence of function calls, each of which limits the computational progress made. They are useful when responsiveness is critical and the environment does not provide suitable multitasking. + +Processing a bounded amount of input in each call can meet this need for some operations. For example, a hash can be computed using a multi-part operation to break the computation into smaller blocks. However, a multi-part operation does not bound computation that is independent of the input size, such as asymmetric signature verification. + +.. note:: + + Interruptible operations and multi-part operations are distinct optional API capabilities. An implementation can provide one without the other. Applications that need only fragmented input should use the ordinary multi-part API when it is available. Applications that require bounded computation must use the interruptible API, and must handle :code:`PSA_OPERATION_INCOMPLETE`. + + Some interruptible operations accept input in a sequence of update calls. This does not make them substitutes for the ordinary multi-part operations: they have additional state, use an execution budget, and can report :code:`PSA_OPERATION_INCOMPLETE`. + +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 an 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-signature-operations: + +Interruptible signature operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The interruptible signature and verification APIs are separate from the ordinary multi-part signature APIs. They provide bounded computation for the setup and completion of an asymmetric signature operation. They also accept message input where the selected algorithm permits it, but this is incidental to their execution-budget purpose. + +These operations can have more than one step at which the application calls the same function repeatedly until it returns a status other than :code:`PSA_OPERATION_INCOMPLETE`. + +:numref:`fig-interruptible-signature` shows the state model used for interruptible asymmetric signature and verification operations. + +.. figure:: /figure/interruptible_operation_complex.* + :name: fig-interruptible-signature + + State model for an interruptible signature operation + +The sequence has the common interruptible-operation steps, with a setup phase that can itself require bounded computation and an optional input phase. + +1. **Allocate** + +#. **Initialize** + +#. **Begin setup:** Start a new interruptible signature or verification operation on an *inactive* object. + + 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 setup on an object in the *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. + +#. **Input:** Provide a pre-computed hash or message data to an object in the *input* state. + The signature APIs provide a hash-input function and an update function for message data. + + 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, the operation is left in the *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 apply to an interruptible signature operation. See :secref:`interruptible-operations`. + +Each type of interruptible signature operation can have multiple *setup*, *input*, and *completing* states. The operation documentation describes the setup, input, and completion functions and their ordering requirements. + +See :secref:`interruptible-sign` for the interruptible signature API. + Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ @@ -254,7 +418,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. @@ -277,11 +441,11 @@ This specification defines interfaces for the following types of asymmetric cryp * Key encapsulation. See :secref:`key-encapsulation`. * Password-authenticated key exchange (PAKE). See :secref:`pake`. -For asymmetric signature, the API provides *single-part* functions and *multi-part* operations. +For asymmetric signature, the API provides single-part functions, multi-part operations, and interruptible operations. For asymmetric encryption, the API provides single-part functions. -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, and an additional input method for a key derivation operation. For key encapsulation, the API provides single-part functions. From 7b889e80ac9352e74dcae5b57cc29a917972964e Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 18:44:06 +0100 Subject: [PATCH 02/22] Add missing 'time' to 'bounded execution' text Signed-off-by: Andrew Thoelke --- doc/crypto/api/keys/management.rst | 8 ++++---- doc/crypto/api/ops/key-agreement.rst | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 3780461c..b1b661e2 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -237,7 +237,7 @@ When creating a key, the attributes for the new key are specified in a `psa_key_ This function is equivalent to calling `psa_generate_key_custom()` with the production parameters `PSA_CUSTOM_KEY_PARAMETERS_INIT` and ``custom_data_length == 0`` (``custom_data`` is ignored). - If an application requires bounded execution when generating a key, the implementation might provide support for interruptible key generation. + If an application requires bounded execution time when generating a key, the implementation might provide support for interruptible key generation. See :secref:`interruptible-generate-key`. .. function:: psa_generate_key_custom @@ -726,7 +726,7 @@ 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. + If an application requires bounded execution time when exporting a public key, it can use an interruptible public-key export operation. See :secref:`interruptible-export-key`. @@ -846,7 +846,7 @@ 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. +For such keys, an interruptible key-generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution time requirements for use cases that require key generation. This operation uses the default production parameters of `psa_generate_key()`. It does not support the custom production parameters accepted by `psa_generate_key_custom()`. @@ -1105,7 +1105,7 @@ 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 can be used instead of calling `psa_export_public_key()`, in applications that have bounded execution time requirements for use cases that require public-key export. An interruptible public-key export operation is used as follows: diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 7cefeaaa..162758a5 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -17,7 +17,7 @@ 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. -If an application requires bounded execution during a key agreement, it can use an interruptible key-agreement operation. +If an application requires bounded execution time 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. @@ -231,7 +231,7 @@ 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. + If an application requires bounded execution time during key agreement, it can use an interruptible key-agreement operation. See :secref:`interruptible-key-agreement`. .. function:: psa_raw_key_agreement @@ -365,7 +365,7 @@ 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 can be used instead of calling `psa_key_agreement()`, in applications that have bounded execution time requirements for use cases involving key agreement. An interruptible key-agreement operation is used as follows: From 122457fe1dcc4fdaa54240ed137b8a9cc3d163f5 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 18:47:38 +0100 Subject: [PATCH 03/22] Clarified wording on IOP usage Signed-off-by: Andrew Thoelke --- doc/crypto/api/keys/management.rst | 4 ++-- doc/crypto/api/ops/key-agreement.rst | 2 +- doc/crypto/api/ops/signature.rst | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index b1b661e2..b5ab6a55 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -930,7 +930,7 @@ An interruptible key-generation operation is used as follows: .. 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. + After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. @@ -1183,7 +1183,7 @@ An interruptible public-key export operation is used as follows: .. 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. + After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 162758a5..c0f78e22 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -444,7 +444,7 @@ An interruptible key-agreement operation is used as follows: .. 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. + After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index b1869c49..9e6b7a41 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -2871,7 +2871,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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()`. + After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. @@ -3243,7 +3243,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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()`. + After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. From a7f1c8c0da4d81847b06e651796534065afa56b3 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:19:35 +0100 Subject: [PATCH 04/22] Add custom parameters for interruptible key generation Signed-off-by: Andrew Thoelke --- doc/crypto/api.db/psa/crypto.h | 4 +++ doc/crypto/api/keys/management.rst | 51 ++++++++++++++++++++++++++++-- 2 files changed, 52 insertions(+), 3 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index e48a7002..b9330587 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -669,6 +669,10 @@ psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, 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); +psa_status_t psa_generate_key_iop_custom(psa_generate_key_iop_t * operation, + const psa_custom_key_parameters_t * custom, + const uint8_t * custom_data, + size_t custom_data_length); 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, diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index b5ab6a55..2f8ef61c 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -848,7 +848,8 @@ 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 time requirements for use cases that require key generation. -This operation uses the default production parameters of `psa_generate_key()`. It does not support the custom production parameters accepted by `psa_generate_key_custom()`. +By default, this operation uses the default production parameters of `psa_generate_key()`. +To use custom production parameters, call `psa_generate_key_iop_custom()` after `psa_generate_key_iop_setup()` and before `psa_generate_key_iop_complete()`. .. note:: An implementation of the |API| does not need to provide incremental generation for all key types supported by the implementation. @@ -859,6 +860,7 @@ 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. +#. Optionally, call `psa_generate_key_iop_custom()` to specify custom production parameters. #. 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()`. @@ -972,7 +974,7 @@ An interruptible key-generation operation is used as follows: .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The interruptible operation must now be completed by calling `psa_generate_key_iop_complete()`. + The interruptible operation can be configured with custom production parameters by calling `psa_generate_key_iop_custom()`, or 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 @@ -1017,7 +1019,7 @@ An interruptible key-generation operation is used as follows: 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`. + The operation can be configured with custom production parameters by calling `psa_generate_key_iop_custom()`, or 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()`. @@ -1025,6 +1027,49 @@ An interruptible key-generation operation is used as follows: If `psa_generate_key_iop_setup()` returns an error, the operation object is unchanged. +.. function:: psa_generate_key_iop_custom + + .. summary:: + Set custom production parameters for an interruptible key-generation operation. + + .. versionadded:: 1.6 + + .. param:: psa_generate_key_iop_t * operation + The interruptible key-generation operation to configure. + The operation must be active, and `psa_generate_key_iop_complete()` must not have been called. + .. param:: const psa_custom_key_parameters_t * custom + Customized production parameters for the key generation. + .. param:: const uint8_t * custom_data + A buffer containing additional variable-sized production parameters. + .. param:: size_t custom_data_length + Length of ``custom_data`` in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The production parameters are not supported by the implementation. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The production parameters are invalid. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active, and `psa_generate_key_iop_complete()` must not have been called. + * 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 + + This function sets custom production parameters for a key-generation operation. + The application must call `psa_generate_key_iop_setup()` before calling this function. + It may call this function at most once for an operation. + + If this function is not called, the operation uses the default production parameters `PSA_CUSTOM_KEY_PARAMETERS_INIT` with ``custom_data_length == 0``. + + 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 this function returns an error status, the operation enters an error state and must be aborted by calling `psa_generate_key_iop_abort()`. + .. function:: psa_generate_key_iop_complete .. summary:: From c4c5fa8ee1002a5fdae06456cf63a04c92eb1970 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:22:48 +0100 Subject: [PATCH 05/22] Add support for contexts in interruptible signature and verify operations Signed-off-by: Andrew Thoelke --- doc/crypto/api.db/psa/crypto.h | 6 ++ doc/crypto/api/ops/signature.rst | 124 +++++++++++++++++++++++++++---- 2 files changed, 117 insertions(+), 13 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index b9330587..6bf742d3 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -906,6 +906,9 @@ 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_set_context(psa_sign_iop_t * operation, + const uint8_t * context, + size_t context_length); psa_status_t psa_sign_iop_setup(psa_sign_iop_t * operation, psa_key_id_t key, psa_algorithm_t alg); @@ -968,6 +971,9 @@ 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_set_context(psa_verify_iop_t * operation, + const uint8_t * context, + size_t context_length); psa_status_t psa_verify_iop_setup(psa_verify_iop_t * operation, psa_key_id_t key, psa_algorithm_t alg, diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 9e6b7a41..6507a454 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -135,7 +135,7 @@ Some applications need to bound the expensive computation performed in an indivi * Version 1.6 of the |API| introduces optional interruptible signature and verification operations, `psa_sign_iop_t` and `psa_verify_iop_t`. They limit the computation performed in a call and can return :code:`PSA_OPERATION_INCOMPLETE` when further calls are required. - These operations are distinct from the multi-part operations and are intended for applications that require bounded execution time. The interruptible operations use a zero-length context when the algorithm has a context parameter. + These operations are distinct from the multi-part operations and are intended for applications that require bounded execution time. If the algorithm has a context parameter, an interruptible operation uses a zero-length context unless the application calls `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. See :secref:`interruptible-sign` and :secref:`interruptible-verify`. @@ -588,7 +588,7 @@ They are used with the Edwards25519 and Edwards448 elliptic curve keys, see `PSA Both PureEdDSA and HashEdDSA can be used with contexts, which enables domain-separation when signatures are made of different message structures with the same key. For EdDSA, the context is an arbitrary byte string between zero and 255 bytes in length. -Interruptible signature operations use a zero-length context. +Interruptible signature operations use a zero-length context unless the application sets a context with `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. The development of EdDSA resulted in a total of five distinct algorithms: @@ -682,7 +682,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: * Edwards448: the Ed448 algorithm is computed, with a zero-length 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`. - To use a non-zero-length context, use the message-signature functions that accept a context parameter, :code:`psa_sign_message_with_context()` and :code:`psa_verify_message_with_context()` + To use a non-zero-length context, use the message-signature functions that accept a context parameter, :code:`psa_sign_message_with_context()` and :code:`psa_verify_message_with_context()`, or call `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()` when using an interruptible signature operation. The `psa_sign_message()` and `psa_verify_message()` functions use a zero-length context when computing or verifying signatures. .. note:: @@ -726,7 +726,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: When used to sign or verify a hash, the ``hash`` parameter is the SHA-512 message digest. The signature functions without a context parameter use a zero-length context when computing or verifying signatures. - To use a non-zero-length context, use the signature functions that accept a context parameter, such as :code:`psa_sign_hash_with_context()` or :code:`psa_verify_message_with_context()` + To use a non-zero-length context, use the signature functions that accept a context parameter, such as :code:`psa_sign_hash_with_context()` or :code:`psa_verify_message_with_context()`, or call `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()` when using an interruptible signature operation. The context parameter can be between zero and 255 bytes in length. .. subsection:: Usage @@ -776,7 +776,7 @@ The development of EdDSA resulted in a total of five distinct algorithms: When used to sign or verify a hash, the ``hash`` parameter is the truncated SHAKE256 message digest. The signature functions without a context parameter use a zero-length context when computing or verifying signatures. - To use a non-zero-length context, use the signature functions that accept a context parameter, for example, `psa_sign_hash_with_context()` or `psa_verify_message_with_context()` + To use a non-zero-length context, use the signature functions that accept a context parameter, for example, `psa_sign_hash_with_context()` or `psa_verify_message_with_context()`, or call `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()` when using an interruptible signature operation. The context parameter can be between zero and 255 bytes in length. .. subsection:: Usage @@ -915,8 +915,8 @@ All SLH-DSA algorithms can be used with contexts, which enables domain-separatio Context values are arbitrary strings between zero and 255 bytes in length. * The signature functions without a context parameter provide a zero-length context when computing or verifying SLH-DSA signatures. -* To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`. -* Interruptible signature operations use a zero-length context. +* To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`, or call `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()` when using an interruptible signature operation. +* Interruptible signature operations use a zero-length context unless the application sets a context with `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. .. macro:: PSA_ALG_SLH_DSA :definition: ((psa_algorithm_t) 0x06004000) @@ -1267,8 +1267,8 @@ All ML-DSA algorithms can be used with contexts, which enables domain-separation Context values are arbitrary strings between zero and 255 bytes in length. * The signature functions without a context parameter provide a zero-length context when computing or verifying ML-DSA signatures. -* To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`. -* Interruptible signature operations use a zero-length context. +* To provide a context, use the ``psa_xxxx_with_context()`` signature functions with a context parameter, such as :code:`psa_sign_message_with_context()`, or call `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()` when using an interruptible signature operation. +* Interruptible signature operations use a zero-length context unless the application sets a context with `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. .. macro:: PSA_ALG_ML_DSA :definition: ((psa_algorithm_t) 0x06004400) @@ -1817,7 +1817,7 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_sign_hash_with_context()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. - Interruptible signature operations use a zero-length context. To use a non-zero-length context, use this function instead. + Interruptible signature operations use a zero-length context unless the application calls `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. .. function:: psa_verify_message @@ -1949,7 +1949,7 @@ Single-part asymmetric signature functions * For a hash-and-sign signature algorithm, use a `psa_hash_operation_t` multi-part hash operation and then pass the resulting hash to `psa_verify_hash_with_context()`. :code:`PSA_ALG_GET_HASH(alg)` can be used to determine the hash algorithm to use. - Interruptible signature operations use a zero-length context. To use a non-zero-length context, use this function instead. + Interruptible signature operations use a zero-length context unless the application calls `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. .. function:: psa_sign_hash @@ -2796,6 +2796,7 @@ An interruptible asymmetric signature operation is used as follows: #. 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`. +#. Optionally, call `psa_sign_iop_set_context()` to provide a context. #. Either: 1. Call `psa_sign_iop_hash()` with a pre-computed hash of the message to sign; or @@ -2966,12 +2967,60 @@ An interruptible asymmetric signature operation is used as follows: .. 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()`. + When this function returns successfully, the operation is ready for context or data input using a call to `psa_sign_iop_set_context()`, `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_set_context + + .. summary:: + Provide a context for an interruptible signature operation. + + .. versionadded:: 1.6 + + .. param:: psa_sign_iop_t * operation + The interruptible signature operation to configure. + The operation setup must be complete, with no hash, message, or completion input. + .. param:: const uint8_t * context + Buffer containing the context value. + .. param:: size_t context_length + Size of the ``context`` 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: setup must be complete, and no call to `psa_sign_iop_set_context()`, `psa_sign_iop_hash()`, `psa_sign_iop_update()`, or `psa_sign_iop_complete()` has been made. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``context_length`` is not valid for the algorithm and key type. + * ``context`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The context value is not supported by this implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + + This function sets the context value in an interruptible signature operation. + The application must complete setup by calling `psa_sign_iop_setup_complete()` before calling this function. + + For a signature algorithm that has a context parameter: + + * If this function is not called, the operation uses the algorithm with a zero-length or empty context. + * To set a non-zero-length context, call this function after `psa_sign_iop_setup_complete()` has returned success and before calling any input or completion function on the operation. + + If a context parameter is not supported by the algorithm, this function call can be omitted, or can be called with a zero-length context. + + The macro `PSA_ALG_SIGN_SUPPORTS_CONTEXT()` can be used to determine if a signature algorithm supports non-zero-length context values. + + 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_hash .. summary:: @@ -3168,6 +3217,7 @@ An interruptible asymmetric verification operation is used as follows: #. 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`. +#. Optionally, call `psa_verify_iop_set_context()` to provide a context. #. Either: 1. Call `psa_verify_iop_hash()` with a pre-computed hash of the message to verify; or @@ -3345,12 +3395,60 @@ An interruptible asymmetric verification operation is used as follows: .. 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()`. + When this function returns successfully, the operation is ready for context or data input using a call to `psa_verify_iop_set_context()`, `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_set_context + + .. summary:: + Provide a context for an interruptible verification operation. + + .. versionadded:: 1.6 + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to configure. + The operation setup must be complete, with no hash, message, or completion input. + .. param:: const uint8_t * context + Buffer containing the context value. + .. param:: size_t context_length + Size of the ``context`` 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: setup must be complete, and no call to `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, `psa_verify_iop_update()`, or `psa_verify_iop_complete()` has been made. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``context_length`` is not valid for the algorithm and key type. + * ``context`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The context value is not supported by this implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + + This function sets the context value in an interruptible verification operation. + The application must complete setup by calling `psa_verify_iop_setup_complete()` before calling this function. + + For a signature algorithm that has a context parameter: + + * If this function is not called, the operation uses the algorithm with a zero-length or empty context. + * To set a non-zero-length context, call this function after `psa_verify_iop_setup_complete()` has returned success and before calling any input or completion function on the operation. + + If a context parameter is not supported by the algorithm, this function call can be omitted, or can be called with a zero-length context. + + The macro `PSA_ALG_SIGN_SUPPORTS_CONTEXT()` can be used to determine if a signature algorithm supports non-zero-length context values. + + 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_hash .. summary:: From b8552b23aa1c22cc250db491bb9ebe6395f003e0 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:32:54 +0100 Subject: [PATCH 06/22] Clarify interruptible setup failure state --- doc/crypto/api/keys/management.rst | 10 ++++++---- doc/crypto/api/ops/key-agreement.rst | 5 +++-- doc/crypto/api/ops/signature.rst | 10 ++++++---- doc/crypto/overview/functionality.rst | 2 +- 4 files changed, 16 insertions(+), 11 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 2f8ef61c..c323e958 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -933,7 +933,8 @@ An interruptible key-generation operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. + The value is reset to zero by a successful call to either `psa_generate_key_iop_setup()` or `psa_generate_key_iop_abort()`. + A failed call to `psa_generate_key_iop_setup()` can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -1025,7 +1026,7 @@ An interruptible key-generation operation is used as follows: * 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. + If `psa_generate_key_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_generate_key_iop_custom @@ -1229,7 +1230,8 @@ An interruptible public-key export operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. + The value is reset to zero by a successful call to either `psa_export_public_key_iop_setup()` or `psa_export_public_key_iop_abort()`. + A failed call to `psa_export_public_key_iop_setup()` can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -1286,7 +1288,7 @@ An interruptible public-key export operation is used as follows: * 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. + If `psa_export_public_key_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_export_public_key_iop_complete diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index c0f78e22..992a1a3f 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -445,7 +445,8 @@ An interruptible key-agreement operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on 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()`. + The value is reset to zero by a successful call to either `psa_key_agreement_iop_setup()` or `psa_key_agreement_iop_abort()`. + A failed call to `psa_key_agreement_iop_setup()` can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -563,7 +564,7 @@ An interruptible key-agreement operation is used as follows: * 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. + If `psa_key_agreement_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_key_agreement_iop_complete diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 6507a454..6d65abc5 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -2872,7 +2872,8 @@ An interruptible asymmetric signature operation is used as follows: .. 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* spent on the entire operation. The value is reset to zero by a call to either `psa_sign_iop_setup()` or `psa_sign_iop_abort()`. + After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. The value is reset to zero by a successful call to either `psa_sign_iop_setup()` or `psa_sign_iop_abort()`. + A failed call to `psa_sign_iop_setup()` can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -2933,7 +2934,7 @@ An interruptible asymmetric signature operation is used as follows: * 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. + If `psa_sign_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_sign_iop_setup_complete @@ -3293,7 +3294,8 @@ An interruptible asymmetric verification operation is used as follows: .. 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* spent on the entire operation. The value is reset to zero by a call to either `psa_verify_iop_setup()` or `psa_verify_iop_abort()`. + After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. The value is reset to zero by a successful call to either `psa_verify_iop_setup()` or `psa_verify_iop_abort()`. + A failed call to `psa_verify_iop_setup()` can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -3360,7 +3362,7 @@ An interruptible asymmetric verification operation is used as follows: * 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. + If `psa_verify_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_verify_iop_setup_complete diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 0f351516..80d371f0 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -253,7 +253,7 @@ There are three components in an interruptible operation: 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. + Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is reset when the operation is aborted, or when an operation object is successfully set up for a new operation. A failed setup can also reset the value. 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`. From 0f8d3dcb32f21227cc2bbedbbb84b6811fb5b847 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:33:19 +0100 Subject: [PATCH 07/22] Clarify interruptible signature input states --- doc/crypto/api/ops/signature.rst | 28 ++++++++++++---------------- 1 file changed, 12 insertions(+), 16 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 6d65abc5..d7e1380b 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -3030,7 +3030,7 @@ An interruptible asymmetric signature operation is used as follows: .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input. + The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input, and completion must not have started. .. param:: const uint8_t * hash The input to sign. This is usually the hash of a message. @@ -3045,7 +3045,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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 operation state is not valid: the operation must be set up, with no data input, and completion must not have started. * 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. @@ -3125,10 +3125,6 @@ An interruptible asymmetric signature operation is used as follows: 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:: @@ -3137,7 +3133,7 @@ An interruptible asymmetric signature operation is used as follows: .. versionadded:: 1.6 .. 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. + The interruptible asymmetric signature operation to use. The operation must be active. It 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 @@ -3157,7 +3153,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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 operation state is not valid: the operation must be active and have hash or message data input, or a previous call to `psa_sign_iop_complete()` must have 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. @@ -3173,6 +3169,8 @@ An interruptible asymmetric signature operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + To sign the zero-length message, call `psa_sign_iop_update()` once with a zero-length message fragment before calling this function. + 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()`. @@ -3459,7 +3457,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must have been set up, with no data input. + The interruptible verification operation to use. The operation must have been set up, with no data input, and completion must not have started. .. param:: const uint8_t * hash The input whose signature is to be verified. This is usually the hash of a message. @@ -3474,7 +3472,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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 operation state is not valid: the operation must be set up, with no data input, and completion must not have started. * 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. @@ -3553,10 +3551,6 @@ An interruptible asymmetric verification operation is used as follows: 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:: @@ -3565,7 +3559,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. 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. + The interruptible verification operation to use. The operation must be active. It must have hash or message data input, or be in the process of finishing. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -3576,7 +3570,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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 operation state is not valid: the operation must be active and have hash or message data input, or a previous call to `psa_verify_iop_complete()` must have 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. @@ -3590,6 +3584,8 @@ An interruptible asymmetric verification operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + To verify the signature of the zero-length message, call `psa_verify_iop_update()` once with a zero-length message fragment before calling this function. + 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()`. From d32a8bc971cbb54919d500c8ee39c9eec39affc4 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:33:31 +0100 Subject: [PATCH 08/22] Clarify interruptible completion summaries --- doc/crypto/api/keys/management.rst | 4 ++-- doc/crypto/api/ops/key-agreement.rst | 2 +- doc/crypto/api/ops/signature.rst | 4 ++-- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index c323e958..54c01051 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -1074,7 +1074,7 @@ An interruptible key-generation operation is used as follows: .. function:: psa_generate_key_iop_complete .. summary:: - Attempt to finish the interruptible generation of a key. + Progress an interruptible operation to generate a key. .. versionadded:: 1.6 @@ -1293,7 +1293,7 @@ An interruptible public-key export operation is used as follows: .. function:: psa_export_public_key_iop_complete .. summary:: - Attempt to finish the interruptible export of a public key. + Progress an interruptible operation to export a public key. .. versionadded:: 1.6 diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 992a1a3f..f4f037f4 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -569,7 +569,7 @@ An interruptible key-agreement operation is used as follows: .. function:: psa_key_agreement_iop_complete .. summary:: - Attempt to finish a key agreement and return the shared secret. + Progress an interruptible key-agreement operation and return the shared secret. .. versionadded:: 1.6 diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index d7e1380b..7b9bd699 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -3128,7 +3128,7 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_complete .. summary:: - Attempt to finish the interruptible calculation of an asymmetric signature. + Progress an interruptible asymmetric signature calculation. .. versionadded:: 1.6 @@ -3554,7 +3554,7 @@ An interruptible asymmetric verification operation is used as follows: .. function:: psa_verify_iop_complete .. summary:: - Attempt to finish the interruptible verification of an asymmetric signature. + Progress an interruptible asymmetric signature verification. .. versionadded:: 1.6 From 6b8483c6435f88883ec667e84994674658b5633e Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:33:39 +0100 Subject: [PATCH 09/22] Use key agreement in interruptible operations example --- doc/crypto/overview/functionality.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 80d371f0..efddaf2e 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -232,7 +232,7 @@ Interruptible operations Interruptible operations split a computationally expensive operation into a sequence of function calls, each of which limits the computational progress made. They are useful when responsiveness is critical and the environment does not provide suitable multitasking. -Processing a bounded amount of input in each call can meet this need for some operations. For example, a hash can be computed using a multi-part operation to break the computation into smaller blocks. However, a multi-part operation does not bound computation that is independent of the input size, such as asymmetric signature verification. +Processing a bounded amount of input in each call can meet this need for some operations. For example, a hash can be computed using a multi-part operation to break the computation into smaller blocks. However, a multi-part operation does not bound computation that is independent of the input size, such as calculating a shared secret in key agreement. .. note:: From 08e87e81f727faa77b8dddb87d717565c66e61d7 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 19:57:44 +0100 Subject: [PATCH 10/22] Allow empty messages in interruptible signatures --- doc/crypto/api/ops/signature.rst | 30 ++++++++++++++++++++---------- 1 file changed, 20 insertions(+), 10 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 7b9bd699..bddad564 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -2800,7 +2800,7 @@ An interruptible asymmetric signature operation is used as follows: #. 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. + 2. Call `psa_sign_iop_update()` zero, one or more times, passing a fragment of the message each time. The signature that is calculated is that 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()`. @@ -3133,7 +3133,7 @@ An interruptible asymmetric signature operation is used as follows: .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to use. The operation must be active. It must have hash or message data input, or be in the process of finishing. + The interruptible asymmetric signature operation to use. The operation must be active, and setup must be complete. .. param:: uint8_t * signature Buffer where the signature is to be written. .. param:: size_t signature_size @@ -3150,14 +3150,20 @@ An interruptible asymmetric signature operation is used as follows: 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_NOT_PERMITTED + If no data has been input to the operation, the key does not have the `PSA_KEY_USAGE_SIGN_MESSAGE` flag. .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must be active and have hash or message data input, or a previous call to `psa_sign_iop_complete()` must have returned :code:`PSA_OPERATION_INCOMPLETE`. + * The operation state is not valid: the operation must be active, and setup must be complete. * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + If no data has been input to the operation, the algorithm does not allow signing of a message. .. 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_NOT_SUPPORTED + If no data has been input to the operation, the implementation does not support signing of a message. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED @@ -3169,9 +3175,8 @@ An interruptible asymmetric signature operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - To sign the zero-length message, call `psa_sign_iop_update()` once with a zero-length message fragment before calling this function. - When this function returns successfully, the signature is returned in ``signature``, and the operation becomes inactive. + If neither a pre-computed hash nor a message fragment has been input, the signature is of the zero-length message. 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()`. @@ -3220,7 +3225,7 @@ An interruptible asymmetric verification operation is used as follows: #. 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. + 2. Call `psa_verify_iop_update()` zero, 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()`. @@ -3559,7 +3564,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must be active. It must have hash or message data input, or be in the process of finishing. + The interruptible verification operation to use. The operation must be active, and setup must be complete. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -3567,11 +3572,17 @@ An interruptible asymmetric verification operation is used as follows: 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_NOT_PERMITTED + If no data has been input to the operation, the key does not have the `PSA_KEY_USAGE_VERIFY_MESSAGE` flag. .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must be active and have hash or message data input, or a previous call to `psa_verify_iop_complete()` must have returned :code:`PSA_OPERATION_INCOMPLETE`. + * The operation state is not valid: the operation must be active, and setup must be complete. * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + If no data has been input to the operation, the algorithm does not allow verification of a message. + .. retval:: PSA_ERROR_NOT_SUPPORTED + If no data has been input to the operation, the implementation does not support verification of a message. .. 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 @@ -3584,9 +3595,8 @@ An interruptible asymmetric verification operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - To verify the signature of the zero-length message, call `psa_verify_iop_update()` once with a zero-length message fragment before calling this function. - When this function returns successfully, the operation becomes inactive. + If neither a pre-computed hash nor a message fragment has been input, the signature is verified against the zero-length message. 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()`. From 86157414ea371da8a93ec67465c60633767ade91 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 20:13:55 +0100 Subject: [PATCH 11/22] Clarify interruptible operation completion flows --- doc/crypto/api/keys/management.rst | 4 ++-- doc/crypto/api/ops/key-agreement.rst | 2 +- doc/crypto/api/ops/signature.rst | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 54c01051..d176b258 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -861,7 +861,7 @@ An interruptible key-generation operation is used as follows: #. 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. #. Optionally, call `psa_generate_key_iop_custom()` to specify custom production parameters. -#. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function returns a status code other than :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 @@ -1158,7 +1158,7 @@ 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`. +#. Call `psa_export_public_key_iop_complete()` to finish exporting the key data, until this function returns a status code other than :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 diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index f4f037f4..7717f0c6 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -372,7 +372,7 @@ 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`. +#. Call `psa_key_agreement_iop_complete()` to finish the key agreement and output the shared secret, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_key_agreement_iop_abort()`. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index bddad564..641a910e 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -2795,13 +2795,13 @@ 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`. +#. Call `psa_sign_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_sign_iop_set_context()` to provide a context. #. Either: 1. Call `psa_sign_iop_hash()` with a pre-computed hash of the message to sign; or 2. Call `psa_sign_iop_update()` zero, one or more times, passing a fragment of the message each time. The signature that is calculated is that 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`. +#. Call `psa_sign_iop_complete()` to finish calculating the signature value, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_sign_iop_abort()`. @@ -3220,13 +3220,13 @@ 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`. +#. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_verify_iop_set_context()` to provide a context. #. Either: 1. Call `psa_verify_iop_hash()` with a pre-computed hash of the message to verify; or 2. Call `psa_verify_iop_update()` zero, 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`. +#. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. From 577d6e43a1aac675c52350100ab2f2c10bfe6c68 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 20:23:39 +0100 Subject: [PATCH 12/22] Specify single-fragment interruptible signature errors --- doc/crypto/api/ops/signature.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 641a910e..9b08da9d 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -3103,6 +3103,7 @@ An interruptible asymmetric signature operation is used as follows: The following conditions can result in this error: * The algorithm does not allow signing of a message. + * The selected algorithm or implementation permits only one message fragment, and `psa_sign_iop_update()` has already been called. * 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: @@ -3535,6 +3536,7 @@ An interruptible asymmetric verification operation is used as follows: The following conditions can result in this error: * The algorithm does not allow verification of a message. + * The selected algorithm or implementation permits only one message fragment, and `psa_verify_iop_update()` has already been called. * 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: From a8ddd07c39908527e89e4edb1eb72df685f34f2d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 4 Aug 2026 20:25:26 +0100 Subject: [PATCH 13/22] Clarify incomplete key-agreement output --- doc/crypto/api/ops/key-agreement.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 7717f0c6..c056c697 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -578,7 +578,7 @@ An interruptible key-agreement operation is used as follows: 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. + `PSA_KEY_ID_NULL` if the function returns :code:`PSA_OPERATION_INCOMPLETE` or an error status. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -613,7 +613,7 @@ An interruptible key-agreement operation is used as follows: .. 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 :code:`PSA_OPERATION_INCOMPLETE`, ``*key`` is `PSA_KEY_ID_NULL`, 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()`. From 70ae29823e69d8bd524a5b203f176317bf528e05 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 6 Aug 2026 17:20:07 +0100 Subject: [PATCH 14/22] Clarify persistent key collision timing --- doc/crypto/api/keys/management.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index d176b258..8429e828 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -1012,6 +1012,9 @@ An interruptible key-generation operation is used as follows: This function sets up the random generation of a new key. The location, policy, type, and size of the key are taken from ``attributes``. + If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_generate_key_iop_complete()` returns this error. + Applications must be prepared for either function to report this error. + Implementations must reject an attempt to generate a key of size ``0``. The following type-specific considerations apply: @@ -1111,6 +1114,9 @@ An interruptible key-generation operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. + Applications must be prepared for either function to report this error. + 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()`. From a26ae1e797a2163d9006a8325a42f3253df15d1a Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 6 Aug 2026 17:29:35 +0100 Subject: [PATCH 15/22] Clarify key-agreement collision timing --- doc/crypto/api/ops/key-agreement.rst | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index c056c697..63e31543 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -556,6 +556,9 @@ An interruptible key-agreement operation is used as follows: 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. + If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_key_agreement_iop_complete()` returns this error. + Applications must be prepared for either function to report this error. + 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. @@ -606,6 +609,9 @@ An interruptible key-agreement operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. + Applications must be prepared for either function to report this error. + 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()`. From d5548bde45ccfac971b10cade9e05a99e6f56dd2 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 6 Aug 2026 17:55:52 +0100 Subject: [PATCH 16/22] Clarify single-fragment signature update errors --- doc/crypto/api/ops/signature.rst | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 9b08da9d..b3142a38 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -3103,12 +3103,13 @@ An interruptible asymmetric signature operation is used as follows: The following conditions can result in this error: * The algorithm does not allow signing of a message. - * The selected algorithm or implementation permits only one message fragment, and `psa_sign_iop_update()` has already been called. + * The selected algorithm permits only one message fragment, and `psa_sign_iop_update()` has already been called. * 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 selected algorithm or implementation permits only one message fragment, and `psa_sign_iop_update()` has already been called. * The total input for the operation is too large for the implementation. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE @@ -3536,12 +3537,13 @@ An interruptible asymmetric verification operation is used as follows: The following conditions can result in this error: * The algorithm does not allow verification of a message. - * The selected algorithm or implementation permits only one message fragment, and `psa_verify_iop_update()` has already been called. + * The selected algorithm permits only one message fragment, and `psa_verify_iop_update()` has already been called. * 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 implementation does not support verification of a message. + * The selected algorithm or implementation permits only one message fragment, and `psa_verify_iop_update()` has already been called. * The total input for the operation is too large for the implementation. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE From b98ea11bf9494aac91e78294516b6c0bb4f52b7a Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 6 Aug 2026 18:14:48 +0100 Subject: [PATCH 17/22] Add deferred-signature interruptible verification --- doc/crypto/api.db/psa/crypto.h | 8 ++ doc/crypto/api/ops/signature.rst | 150 ++++++++++++++++++++++++++++--- 2 files changed, 147 insertions(+), 11 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 6bf742d3..a003c49d 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -224,6 +224,8 @@ typedef struct psa_custom_key_parameters_t { #define PSA_ALG_SHA_512_224 ((psa_algorithm_t)0x0200000c) #define PSA_ALG_SHA_512_256 ((psa_algorithm_t)0x0200000d) #define PSA_ALG_SIGN_SUPPORTS_CONTEXT(alg) /* implementation-defined value */ +#define PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE(alg) \ + /* implementation-defined value */ #define PSA_ALG_SLH_DSA ((psa_algorithm_t) 0x06004000) #define PSA_ALG_SM3 ((psa_algorithm_t)0x02000014) #define PSA_ALG_SP800_108_COUNTER_CMAC ((psa_algorithm_t)0x08000800) @@ -974,12 +976,18 @@ psa_verify_iop_t psa_verify_iop_init(void); psa_status_t psa_verify_iop_set_context(psa_verify_iop_t * operation, const uint8_t * context, size_t context_length); +psa_status_t psa_verify_iop_set_signature(psa_verify_iop_t * operation, + const uint8_t * signature, + size_t signature_length); 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_setup_deferred_signature(psa_verify_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); psa_status_t psa_verify_iop_update(psa_verify_iop_t * operation, const uint8_t * input, size_t input_length); diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index b3142a38..c9590946 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -137,6 +137,8 @@ Some applications need to bound the expensive computation performed in an indivi These operations are distinct from the multi-part operations and are intended for applications that require bounded execution time. If the algorithm has a context parameter, an interruptible operation uses a zero-length context unless the application calls `psa_sign_iop_set_context()` or `psa_verify_iop_set_context()`. + Interruptible verification normally receives the signature when the operation is set up. A streaming protocol that provides the signature after the message data can use the deferred-signature verification flow, if the selected algorithm supports it. + See :secref:`interruptible-sign` and :secref:`interruptible-verify`. .. _rsa-sign-algorithms: @@ -3231,6 +3233,16 @@ An interruptible asymmetric verification operation is used as follows: #. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. +To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead: + +1. Call `psa_verify_iop_setup_deferred_signature()` to specify the algorithm and key. +#. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. +#. Optionally, call `psa_verify_iop_set_context()` to provide a context. +#. Call `psa_verify_iop_update()` zero, one or more times, passing a fragment of the message each time. +#. Call `psa_verify_iop_set_signature()` to provide the signature, after all message fragments have been passed to the operation. +#. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function returns a status code other than :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 @@ -3299,8 +3311,8 @@ An interruptible asymmetric verification operation is used as follows: .. 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* spent on the entire operation. The value is reset to zero by a successful call to either `psa_verify_iop_setup()` or `psa_verify_iop_abort()`. - A failed call to `psa_verify_iop_setup()` can also reset the value to zero. + After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. The value is reset to zero by a successful call to `psa_verify_iop_setup()`, `psa_verify_iop_setup_deferred_signature()`, or `psa_verify_iop_abort()`. + A failed call to either setup function can also reset the value to zero. This function can be used to tune the value passed to `psa_iop_set_max_ops()`. @@ -3361,6 +3373,8 @@ An interruptible asymmetric verification operation is used as follows: .. 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`. + Use this function when the signature is available before the message or hash input. It supports all signature algorithms that are available through the interruptible verification operation. + To verify a message whose signature is available only after the message data, use `psa_verify_iop_setup_deferred_signature()` instead. 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: @@ -3369,6 +3383,64 @@ An interruptible asymmetric verification operation is used as follows: If `psa_verify_iop_setup()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. +.. function:: psa_verify_iop_setup_deferred_signature + + .. summary:: + Begin the setup of an interruptible verification operation with a deferred signature. + + .. versionadded:: 1.6 + + .. 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 permit the usage `PSA_KEY_USAGE_VERIFY_MESSAGE`. + .. param:: psa_algorithm_t alg + An asymmetric message signature algorithm that supports deferred signatures: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE(alg)` is true. + + .. 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 key does not have the `PSA_KEY_USAGE_VERIFY_MESSAGE` flag, or it does not permit the requested algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported, is not an asymmetric message signature algorithm, or does not support deferred signatures. + * ``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 message signature algorithm, or does not support deferred signatures. + * ``key`` is not an asymmetric key pair, or asymmetric public key, 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 + + This function sets up verification of an asymmetric signature of a message when the signature is received after the message data. + It does not accept verification of a pre-computed hash. + The application must provide the signature by calling `psa_verify_iop_set_signature()` after all message input, and before calling `psa_verify_iop_complete()`. + + `PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE()` can be used to determine whether a signature algorithm supports this flow. + An implementation can still return :code:`PSA_ERROR_NOT_SUPPORTED` if it does not support deferred-signature verification for the algorithm. + + After a successful call to `psa_verify_iop_setup_deferred_signature()`, 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 this function returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. + .. function:: psa_verify_iop_setup_complete .. summary:: @@ -3403,6 +3475,7 @@ An interruptible asymmetric verification operation is used as follows: 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 context or data input using a call to `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, or `psa_verify_iop_update()`. + An operation set up with `psa_verify_iop_setup_deferred_signature()` can only accept message input using `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()`. @@ -3417,7 +3490,7 @@ An interruptible asymmetric verification operation is used as follows: .. param:: psa_verify_iop_t * operation The interruptible verification operation to configure. - The operation setup must be complete, with no hash, message, or completion input. + The operation setup must be complete, with no hash, message, signature, or completion input. .. param:: const uint8_t * context Buffer containing the context value. .. param:: size_t context_length @@ -3429,7 +3502,7 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: setup must be complete, and no call to `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, `psa_verify_iop_update()`, or `psa_verify_iop_complete()` has been made. + * The operation state is not valid: setup must be complete, and no call to `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, `psa_verify_iop_update()`, `psa_verify_iop_set_signature()`, or `psa_verify_iop_complete()` has been made. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_ARGUMENT The following conditions can result in this error: @@ -3464,7 +3537,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must have been set up, with no data input, and completion must not have started. + The interruptible verification operation to use. The operation must have been set up with `psa_verify_iop_setup()`, with no data input, and completion must not have started. .. param:: const uint8_t * hash The input whose signature is to be verified. This is usually the hash of a message. @@ -3479,7 +3552,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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, and completion must not have started. + * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup()`, with no data input, and completion must not have started. * 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. @@ -3517,7 +3590,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must have been set up, with no hash value input. + The interruptible verification operation to use. The operation must have been set up, with no hash value or signature input, and completion must not have started. .. param:: const uint8_t * input Buffer containing the message fragment to add to the verification. .. param:: size_t input_length @@ -3529,7 +3602,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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 operation state is not valid: the operation must be set up, with no pre-computed hash value or signature input, and completion must not have started. * 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. @@ -3560,6 +3633,42 @@ An interruptible asymmetric verification operation is used as follows: 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_set_signature + + .. summary:: + Provide the signature for a deferred-signature interruptible verification operation. + + .. versionadded:: 1.6 + + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to use. It must have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and completion must not have started. + .. 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 is 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 have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and no call to `psa_verify_iop_set_signature()` or `psa_verify_iop_complete()` may have been made. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + ``signature`` is not a valid signature for the algorithm and key. + .. 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 + + The application must call this function after all calls to `psa_verify_iop_update()`, and before the first call to `psa_verify_iop_complete()`. + This function provides the signature exactly once. The implementation must consume the signature before this function returns, and must not require the application to provide the signature again to complete 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()`. + .. function:: psa_verify_iop_complete .. summary:: @@ -3568,7 +3677,7 @@ An interruptible asymmetric verification operation is used as follows: .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must be active, and setup must be complete. + The interruptible verification operation to use. The operation must be active, and setup must be complete. If the operation was set up with `psa_verify_iop_setup_deferred_signature()`, the signature must have been provided with `psa_verify_iop_set_signature()`. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -3581,7 +3690,7 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must be active, and setup must be complete. + * The operation state is not valid: the operation must be active, setup must be complete, and a deferred-signature operation must have a signature input. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_ARGUMENT If no data has been input to the operation, the algorithm does not allow verification of a message. @@ -3625,7 +3734,7 @@ An interruptible asymmetric verification operation is used as follows: .. 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. + 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()` or `psa_verify_iop_setup_deferred_signature()`. This function can be called at any time after the operation object has been initialized as described in `psa_verify_iop_t`. @@ -3706,6 +3815,25 @@ Support macros This macro identifies signature algorithms that have a context parameter, and can be used with the appropriate functions that support non-zero-length contexts. +.. macro:: PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE + :definition: /* implementation-defined value */ + + .. summary:: + Whether the specified signature algorithm supports verification with a deferred signature. + + .. versionadded:: 1.6 + + .. param:: alg + A signature algorithm identifier: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN_MESSAGE(alg)` is true. + + .. return:: + ``1`` if ``alg`` is a signature algorithm that can verify a message when the signature is provided after the message input. + ``0`` if ``alg`` is a signature algorithm that requires the signature before message input. + This macro can return either ``0`` or ``1`` if ``alg`` is not a supported signature algorithm identifier. + + This macro identifies algorithms that can be used with the deferred-signature interruptible verification flow, beginning with `psa_verify_iop_setup_deferred_signature()`. + It indicates algorithm compatibility only. An implementation can still return :code:`PSA_ERROR_NOT_SUPPORTED` if it does not support the deferred-signature flow for the algorithm. + .. macro:: PSA_ALG_ANY_HASH :definition: ((psa_algorithm_t)0x020000ff) From beee5ddbe83b45e8455de6456abf4739bbe89c73 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 7 Aug 2026 11:03:26 +0100 Subject: [PATCH 18/22] Clarify deferred verification flow setup --- doc/crypto/api/ops/signature.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index c9590946..654c068c 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -3233,9 +3233,9 @@ An interruptible asymmetric verification operation is used as follows: #. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. -To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead: +To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead. The first two steps are the same as above: -1. Call `psa_verify_iop_setup_deferred_signature()` to specify the algorithm and key. +3. Call `psa_verify_iop_setup_deferred_signature()` to specify the algorithm and key. #. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_verify_iop_set_context()` to provide a context. #. Call `psa_verify_iop_update()` zero, one or more times, passing a fragment of the message each time. From f4b3178bd725faabec49bcf57107a4e1da09db48 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 7 Aug 2026 13:37:28 +0100 Subject: [PATCH 19/22] Editorial polishing Signed-off-by: Andrew Thoelke --- doc/crypto/api/ops/signature.rst | 118 ++++++++++++++------------ doc/crypto/overview/functionality.rst | 8 +- 2 files changed, 67 insertions(+), 59 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 654c068c..8e4e519f 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -642,9 +642,9 @@ The development of EdDSA resulted in a total of five distinct algorithms: 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()`. + When using an interruptible 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. + However, it is possible to fragment the message data when verifying a signature using an interruptible 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. @@ -2790,11 +2790,11 @@ Multi-part asymmetric signature operations 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. +The interruptible 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: +An interruptible 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. +1. Allocate an interruptible 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 returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. @@ -2810,11 +2810,11 @@ An interruptible asymmetric signature operation is used as follows: .. typedef:: /* implementation-defined type */ psa_sign_iop_t .. summary:: - The type of the state data structure for an interruptible asymmetric signature operation. + The type of the state data structure for an interruptible signature operation. .. versionadded:: 1.6 - Before calling any function on an interruptible asymmetric signature operation object, the application must initialize it by any of the following means: + Before calling any function on an interruptible signature operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -2848,14 +2848,14 @@ An interruptible asymmetric signature operation is used as follows: :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_iop_t`. + This macro evaluates to an initializer for an interruptible signature operation object of type `psa_sign_iop_t`. .. versionadded:: 1.6 .. function:: psa_sign_iop_init .. summary:: - Return an initial value for an interruptible asymmetric signature operation object. + Return an initial value for an interruptible signature operation object. .. versionadded:: 1.6 @@ -2864,12 +2864,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_get_num_ops .. summary:: - Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. + Get the number of *ops* that an interruptible signature operation has taken so far. .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to inspect. + The interruptible signature operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. @@ -2884,12 +2884,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_setup .. summary:: - Begin the setup of an interruptible asymmetric signature operation. + Begin the setup of an interruptible signature operation. .. versionadded:: 1.6 .. 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. + The interruptible 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 @@ -2929,7 +2929,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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`. + 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 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: @@ -2941,12 +2941,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_setup_complete .. summary:: - Finish setting up an interruptible asymmetric signature operation. + Finish setting up an interruptible signature operation. .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to use. The operation must be in the process of being set up. + The interruptible signature operation to use. The operation must be in the process of being set up. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -2971,7 +2971,9 @@ An interruptible asymmetric signature operation is used as follows: 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 context or data input using a call to `psa_sign_iop_set_context()`, `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()`. @@ -2985,7 +2987,7 @@ An interruptible asymmetric signature operation is used as follows: .. param:: psa_sign_iop_t * operation The interruptible signature operation to configure. - The operation setup must be complete, with no hash, message, or completion input. + The operation setup must be complete, with no context, hash, or message input, and completion must not have started. .. param:: const uint8_t * context Buffer containing the context value. .. param:: size_t context_length @@ -3027,12 +3029,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_hash .. summary:: - Input a pre-computed hash to an interruptible asymmetric signature operation. + Input a pre-computed hash to an interruptible signature operation. .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input, and completion must not have started. + The interruptible signature operation to use. The operation must have been set up, with no data input, and completion must not have started. .. param:: const uint8_t * hash The input to sign. This is usually the hash of a message. @@ -3047,7 +3049,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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, and completion must not have started. + * The operation state is not valid: setup must be complete, and no call to `psa_sign_iop_hash()`, `psa_sign_iop_update()`, or `psa_sign_iop_complete()` has been made. * 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. @@ -3080,12 +3082,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_update .. summary:: - Add a message fragment to an interruptible asymmetric signature operation. + Add a message fragment to an interruptible signature operation. .. versionadded:: 1.6 .. 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. + The interruptible signature operation to use. The operation must have been set up, with no pre-computed hash value input, and completion must not have started. .. param:: const uint8_t * input Buffer containing the message fragment to add to the signature calculation. .. param:: size_t input_length @@ -3097,7 +3099,7 @@ An interruptible asymmetric signature operation is used as follows: .. 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 operation state is not valid: setup must be complete, and no call to `psa_sign_iop_hash()` or `psa_sign_iop_complete()` has been made. * 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. @@ -3132,12 +3134,12 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_complete .. summary:: - Progress an interruptible asymmetric signature calculation. + Progress an interruptible signature calculation. .. versionadded:: 1.6 .. param:: psa_sign_iop_t * operation - The interruptible asymmetric signature operation to use. The operation must be active, and setup must be complete. + The interruptible signature operation to use. The operation must be active, and setup must be complete. .. param:: uint8_t * signature Buffer where the signature is to be written. .. param:: size_t signature_size @@ -3181,7 +3183,9 @@ An interruptible asymmetric signature operation is used as follows: When this function returns successfully, the signature is returned in ``signature``, and the operation becomes inactive. If neither a pre-computed hash nor a message fragment has been input, the signature is of the zero-length message. + 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()`. @@ -3189,7 +3193,7 @@ An interruptible asymmetric signature operation is used as follows: .. function:: psa_sign_iop_abort .. summary:: - Abort an interruptible asymmetric signature operation. + Abort an interruptible signature operation. .. versionadded:: 1.6 @@ -3217,11 +3221,11 @@ An interruptible asymmetric signature operation is used as follows: 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. +The interruptible 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: +An interruptible 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. +1. Allocate an interruptible 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 returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. @@ -3233,7 +3237,7 @@ An interruptible asymmetric verification operation is used as follows: #. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. -To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead. The first two steps are the same as above: +To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead. The first two steps are the same as above, and then: 3. Call `psa_verify_iop_setup_deferred_signature()` to specify the algorithm and key. #. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. @@ -3247,11 +3251,11 @@ To verify a message received from a streaming protocol that provides the signatu .. typedef:: /* implementation-defined type */ psa_verify_iop_t .. summary:: - The type of the state data structure for an interruptible asymmetric verification operation. + The type of the state data structure for an interruptible verification operation. .. versionadded:: 1.6 - Before calling any function on an interruptible asymmetric verification operation object, the application must initialize it by any of the following means: + Before calling any function on an interruptible verification operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -3285,14 +3289,14 @@ To verify a message received from a streaming protocol that provides the signatu :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_iop_t`. + This macro evaluates to an initializer for an interruptible verification operation object of type `psa_verify_iop_t`. .. versionadded:: 1.6 .. function:: psa_verify_iop_init .. summary:: - Return an initial value for an interruptible asymmetric verification operation object. + Return an initial value for an interruptible verification operation object. .. versionadded:: 1.6 @@ -3301,12 +3305,12 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_get_num_ops .. summary:: - Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. + Get the number of *ops* that an interruptible verification operation has taken so far. .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible asymmetric verification operation to inspect. + The interruptible verification operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. @@ -3321,7 +3325,7 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_setup .. summary:: - Begin the setup of an interruptible asymmetric verification operation. + Begin the setup of an interruptible verification operation. .. versionadded:: 1.6 @@ -3372,7 +3376,8 @@ To verify a message received from a streaming protocol that provides the signatu .. 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`. + 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 signature operation, see :secref:`interruptible-sign`. + Use this function when the signature is available before the message or hash input. It supports all signature algorithms that are available through the interruptible verification operation. To verify a message whose signature is available only after the message data, use `psa_verify_iop_setup_deferred_signature()` instead. @@ -3395,7 +3400,7 @@ To verify a message received from a streaming protocol that provides the signatu .. 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 permit the usage `PSA_KEY_USAGE_VERIFY_MESSAGE`. .. param:: psa_algorithm_t alg - An asymmetric message signature algorithm that supports deferred signatures: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE(alg)` is true. + An asymmetric message signature algorithm that supports deferred signatures: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN_MESSAGE(alg)` and :code:`PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE(alg)` are true. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -3432,7 +3437,6 @@ To verify a message received from a streaming protocol that provides the signatu The application must provide the signature by calling `psa_verify_iop_set_signature()` after all message input, and before calling `psa_verify_iop_complete()`. `PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE()` can be used to determine whether a signature algorithm supports this flow. - An implementation can still return :code:`PSA_ERROR_NOT_SUPPORTED` if it does not support deferred-signature verification for the algorithm. After a successful call to `psa_verify_iop_setup_deferred_signature()`, 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: @@ -3444,7 +3448,7 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_setup_complete .. summary:: - Finish setting up an interruptible asymmetric verification operation. + Finish setting up an interruptible verification operation. .. versionadded:: 1.6 @@ -3474,9 +3478,10 @@ To verify a message received from a streaming protocol that provides the signatu .. 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 context or data input using a call to `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, or `psa_verify_iop_update()`. - An operation set up with `psa_verify_iop_setup_deferred_signature()` can only accept message input using `psa_verify_iop_update()`. + When this function returns successfully, the operation is ready for the next function in the selected verification flow. Depending on the setup function and the choice of hash or message input, the next call can be `psa_verify_iop_set_context()`, `psa_verify_iop_hash()`, `psa_verify_iop_update()`, `psa_verify_iop_set_signature()`, or `psa_verify_iop_complete()`. See the operation flows at the start of :secref:`interruptible-verify` for the permitted call sequences. + 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()`. @@ -3490,7 +3495,7 @@ To verify a message received from a streaming protocol that provides the signatu .. param:: psa_verify_iop_t * operation The interruptible verification operation to configure. - The operation setup must be complete, with no hash, message, signature, or completion input. + The operation setup must be complete, with no context, hash, message, or deferred signature input, and completion must not have started. .. param:: const uint8_t * context Buffer containing the context value. .. param:: size_t context_length @@ -3532,7 +3537,7 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_hash .. summary:: - Input a pre-computed hash to an interruptible asymmetric verification operation. + Input a pre-computed hash to an interruptible verification operation. .. versionadded:: 1.6 @@ -3552,7 +3557,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup()`, with no data input, and completion must not have started. + * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup()`, and no call to `psa_verify_iop_hash()`, `psa_verify_iop_update()`, or `psa_verify_iop_complete()` has been made. * 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. @@ -3571,7 +3576,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID - The application must complete the setup of the operation before calling this function. + The application must complete the setup of the operation before calling this function. A pre-computed hash can only be used with an interruptible verification operation that provides the signature as part of the set up, using `psa_verify_iop_setup()`. 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))`. @@ -3585,12 +3590,12 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_update .. summary:: - Add a message fragment to an interruptible asymmetric verification operation. + Add a message fragment to an interruptible verification operation. .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must have been set up, with no hash value or signature input, and completion must not have started. + The interruptible verification operation to use. The operation must have been set up, with no pre-computed hash value or signature input, and completion must not have started. .. param:: const uint8_t * input Buffer containing the message fragment to add to the verification. .. param:: size_t input_length @@ -3602,7 +3607,7 @@ To verify a message received from a streaming protocol that provides the signatu .. 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 or signature input, and completion must not have started. + * The operation state is not valid: setup must be complete, and no call to `psa_verify_iop_hash()`, `psa_verify_iop_set_signature()`, or `psa_verify_iop_complete()` has been made. * 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. @@ -3654,7 +3659,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and no call to `psa_verify_iop_set_signature()` or `psa_verify_iop_complete()` may have been made. + * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and no call to `psa_verify_iop_set_signature()` or `psa_verify_iop_complete()` has been made. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_ARGUMENT ``signature`` is not a valid signature for the algorithm and key. @@ -3664,15 +3669,16 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED + The application must complete the setup of the operation before calling this function. This function is for use with the deferred-signature flow, when the operation is set up by calling `psa_verify_iop_setup_deferred_signature()`. + The application must call this function after all calls to `psa_verify_iop_update()`, and before the first call to `psa_verify_iop_complete()`. - This function provides the signature exactly once. The implementation must consume the signature before this function returns, and must not require the application to provide the signature again to complete 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()`. .. function:: psa_verify_iop_complete .. summary:: - Progress an interruptible asymmetric signature verification. + Progress an interruptible signature verification. .. versionadded:: 1.6 @@ -3710,7 +3716,9 @@ To verify a message received from a streaming protocol that provides the signatu When this function returns successfully, the operation becomes inactive. If neither a pre-computed hash nor a message fragment has been input, the signature is verified against the zero-length message. + 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()`. @@ -3718,7 +3726,7 @@ To verify a message received from a streaming protocol that provides the signatu .. function:: psa_verify_iop_abort .. summary:: - Abort an interruptible asymmetric verification operation. + Abort an interruptible verification operation. .. versionadded:: 1.6 @@ -3824,7 +3832,7 @@ Support macros .. versionadded:: 1.6 .. param:: alg - A signature algorithm identifier: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN_MESSAGE(alg)` is true. + A signature algorithm identifier: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN(alg)` is true. .. return:: ``1`` if ``alg`` is a signature algorithm that can verify a message when the signature is provided after the message input. diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index efddaf2e..6bfbc06b 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -335,14 +335,14 @@ The interruptible signature and verification APIs are separate from the ordinary These operations can have more than one step at which the application calls the same function repeatedly until it returns a status other than :code:`PSA_OPERATION_INCOMPLETE`. -:numref:`fig-interruptible-signature` shows the state model used for interruptible asymmetric signature and verification operations. +:numref:`fig-interruptible-signature` shows the state model used for interruptible signature and verification operations. .. figure:: /figure/interruptible_operation_complex.* :name: fig-interruptible-signature State model for an interruptible signature operation -The sequence has the common interruptible-operation steps, with a setup phase that can itself require bounded computation and an optional input phase. +The sequence has the common interruptible-operation steps, with a setup phase that can itself require bounded computation and an optional input phase. The following flow adds detail for an interruptible signature operation. Steps without accompanying text are the same as in the preceding interruptible-operation flow: 1. **Allocate** @@ -363,8 +363,8 @@ The sequence has the common interruptible-operation steps, with a setup phase th An application needs to repeat this step until the setup completes with success or an error status. -#. **Input:** Provide a pre-computed hash or message data to an object in the *input* state. - The signature APIs provide a hash-input function and an update function for message data. +#. **Input:** Provide data to an object in the *input* state. + The interruptible signature and verification APIs provide functions to set the context, input a pre-computed message hash or a message fragment, and provide a deferred signature. See the individual interruptible operations for the specific ordering requirements on input data. On success, the operation object remains in *input* state. On failure, the operation object will enter an *error* state. From db5926ebf809b6b504a44ea8dc8bdbf0c1e8e1d4 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 7 Aug 2026 18:26:18 +0100 Subject: [PATCH 20/22] Rename interruptible operation starts --- doc/crypto/api.db/psa/crypto.h | 28 +++++----- doc/crypto/api/keys/management.rst | 48 ++++++++--------- doc/crypto/api/keys/policy.rst | 11 ++-- doc/crypto/api/ops/key-agreement.rst | 26 ++++----- doc/crypto/api/ops/signature.rst | 50 +++++++++--------- doc/crypto/figure/interruptible_operation.pdf | Bin 32413 -> 25877 bytes .../figure/interruptible_operation.puml | 6 +-- doc/crypto/figure/interruptible_operation.svg | 10 +--- .../interruptible_operation_complex.pdf | Bin 34722 -> 28096 bytes .../interruptible_operation_complex.puml | 6 +-- .../interruptible_operation_complex.svg | 20 +------ doc/crypto/overview/functionality.rst | 14 ++--- 12 files changed, 97 insertions(+), 122 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index a003c49d..7faca35c 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -659,7 +659,7 @@ psa_status_t psa_export_public_key_iop_complete(psa_export_public_key_iop_t * op 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_status_t psa_export_public_key_iop_start(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); @@ -677,7 +677,7 @@ psa_status_t psa_generate_key_iop_custom(psa_generate_key_iop_t * operation, size_t custom_data_length); 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, +psa_status_t psa_generate_key_iop_start(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); @@ -740,7 +740,7 @@ 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_status_t psa_key_agreement_iop_start(psa_key_agreement_iop_t * operation, psa_key_id_t private_key, const uint8_t * peer_key, size_t peer_key_length, @@ -911,10 +911,10 @@ psa_sign_iop_t psa_sign_iop_init(void); psa_status_t psa_sign_iop_set_context(psa_sign_iop_t * operation, const uint8_t * context, size_t context_length); -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_setup_start(psa_sign_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); psa_status_t psa_sign_iop_update(psa_sign_iop_t * operation, const uint8_t * input, size_t input_length); @@ -979,15 +979,15 @@ psa_status_t psa_verify_iop_set_context(psa_verify_iop_t * operation, psa_status_t psa_verify_iop_set_signature(psa_verify_iop_t * operation, const uint8_t * signature, size_t signature_length); -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_setup_deferred_signature(psa_verify_iop_t * operation, - psa_key_id_t key, - psa_algorithm_t alg); +psa_status_t psa_verify_iop_setup_deferred_signature_start(psa_verify_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); +psa_status_t psa_verify_iop_setup_start(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_update(psa_verify_iop_t * operation, const uint8_t * input, size_t input_length); diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 8429e828..0e2b2be7 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -849,7 +849,7 @@ 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 time requirements for use cases that require key generation. By default, this operation uses the default production parameters of `psa_generate_key()`. -To use custom production parameters, call `psa_generate_key_iop_custom()` after `psa_generate_key_iop_setup()` and before `psa_generate_key_iop_complete()`. +To use custom production parameters, call `psa_generate_key_iop_custom()` after `psa_generate_key_iop_start()` and before `psa_generate_key_iop_complete()`. .. note:: An implementation of the |API| does not need to provide incremental generation for all key types supported by the implementation. @@ -859,7 +859,7 @@ 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_start()` to specify the key attributes. #. Optionally, call `psa_generate_key_iop_custom()` to specify custom production parameters. #. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_generate_key_iop_abort()`. @@ -933,14 +933,14 @@ An interruptible key-generation operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. - The value is reset to zero by a successful call to either `psa_generate_key_iop_setup()` or `psa_generate_key_iop_abort()`. - A failed call to `psa_generate_key_iop_setup()` can also reset the value to zero. + The value is reset to zero by a successful call to either `psa_generate_key_iop_start()` or `psa_generate_key_iop_abort()`. + A failed call to `psa_generate_key_iop_start()` can also reset the value to zero. 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 +.. function:: psa_generate_key_iop_start .. summary:: Start an interruptible operation to generate a key or key pair. @@ -948,7 +948,7 @@ An interruptible key-generation operation is used as follows: .. versionadded:: 1.6 .. param:: psa_generate_key_iop_t * operation - The interruptible key-generation operation to set up. + The interruptible key-generation operation to start. 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. @@ -1009,10 +1009,10 @@ An interruptible key-generation operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID - This function sets up the random generation of a new key. + This function starts the random generation of a new key. The location, policy, type, and size of the key are taken from ``attributes``. - If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_generate_key_iop_complete()` returns this error. + If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_start()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_generate_key_iop_complete()` returns this error. Applications must be prepared for either function to report this error. Implementations must reject an attempt to generate a key of size ``0``. @@ -1022,14 +1022,14 @@ An interruptible key-generation operation is used as follows: * 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. + After a successful call to `psa_generate_key_iop_start()`, the operation is active. The operation can be configured with custom production parameters by calling `psa_generate_key_iop_custom()`, or 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 remains inactive, but its number of *ops* can be reset to zero. + If `psa_generate_key_iop_start()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_generate_key_iop_custom @@ -1065,7 +1065,7 @@ An interruptible key-generation operation is used as follows: .. retval:: PSA_ERROR_CORRUPTION_DETECTED This function sets custom production parameters for a key-generation operation. - The application must call `psa_generate_key_iop_setup()` before calling this function. + The application must call `psa_generate_key_iop_start()` before calling this function. It may call this function at most once for an operation. If this function is not called, the operation uses the default production parameters `PSA_CUSTOM_KEY_PARAMETERS_INIT` with ``custom_data_length == 0``. @@ -1114,7 +1114,7 @@ An interruptible key-generation operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. + If a persistent key identifier already exists, then it is unspecified whether `psa_generate_key_iop_start()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. Applications must be prepared for either function to report this error. When this function returns successfully, the new key is returned in ``key``, and the operation becomes inactive. @@ -1143,11 +1143,11 @@ An interruptible key-generation operation is used as follows: 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. + Once aborted, the operation object can be reused for another operation by calling `psa_generate_key_iop_start()` 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. + 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 started. .. _interruptible-export-key: @@ -1163,7 +1163,7 @@ 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_start()` to specify the key to export. #. Call `psa_export_public_key_iop_complete()` to finish exporting the key data, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_export_public_key_iop_abort()`. @@ -1236,14 +1236,14 @@ An interruptible public-key export operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. - The value is reset to zero by a successful call to either `psa_export_public_key_iop_setup()` or `psa_export_public_key_iop_abort()`. - A failed call to `psa_export_public_key_iop_setup()` can also reset the value to zero. + The value is reset to zero by a successful call to either `psa_export_public_key_iop_start()` or `psa_export_public_key_iop_abort()`. + A failed call to `psa_export_public_key_iop_start()` can also reset the value to zero. 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 +.. function:: psa_export_public_key_iop_start .. summary:: Start an interruptible operation to export a public key or the public part of a key pair in binary format. @@ -1251,7 +1251,7 @@ An interruptible public-key export operation is used as follows: .. versionadded:: 1.6 .. param:: psa_export_public_key_iop_t * operation - The interruptible public-key export operation to set up. + The interruptible public-key export operation to start. 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. @@ -1281,12 +1281,12 @@ An interruptible public-key export operation is used as follows: .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY - This function sets up the export of a public key in binary format. + This function starts 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. + After a successful call to `psa_export_public_key_iop_start()`, 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: @@ -1294,7 +1294,7 @@ An interruptible public-key export operation is used as follows: * 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 remains inactive, but its number of *ops* can be reset to zero. + If `psa_export_public_key_iop_start()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_export_public_key_iop_complete @@ -1375,8 +1375,8 @@ An interruptible public-key export operation is used as follows: 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. + Once aborted, the operation object can be reused for another operation by calling `psa_export_public_key_iop_start()` 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. + 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 started. diff --git a/doc/crypto/api/keys/policy.rst b/doc/crypto/api/keys/policy.rst index fc1e1741..afae5a5b 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -210,7 +210,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_sign_message()` * `psa_sign_message_with_context()` * `psa_sign_setup()` - * `psa_sign_iop_setup()`, when signing a message. + * `psa_sign_iop_setup_start()`, when signing a message. For a key pair, this concerns the private key. @@ -227,7 +227,8 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_verify_message()` * `psa_verify_message_with_context()` * `psa_verify_setup()` - * `psa_verify_iop_setup()`, when verifying the signature of a message. + * `psa_verify_iop_setup_start()`, when verifying the signature of a message. + * `psa_verify_iop_setup_deferred_signature_start()` For a key pair, this concerns the public key. @@ -241,7 +242,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_sign_hash()` * `psa_sign_hash_with_context()` - * `psa_sign_iop_setup()` when signing a pre-computed hash. + * `psa_sign_iop_setup_start()` 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. @@ -257,7 +258,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_verify_hash()` * `psa_verify_hash_with_context()` - * `psa_verify_iop_setup()`, when verifying the signature of a pre-computed hash. + * `psa_verify_iop_setup_start()`, 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. @@ -274,7 +275,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag must be present on keys used with the following APIs: * `psa_key_agreement()` - * `psa_key_agreement_iop_setup()` + * `psa_key_agreement_iop_start()` * `psa_key_derivation_key_agreement()` * `psa_raw_key_agreement()` diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 63e31543..9c4c9760 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -371,7 +371,7 @@ 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_start()` 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 returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. If an error occurs at any stage, or to terminate the operation early, call `psa_key_agreement_iop_abort()`. @@ -445,14 +445,14 @@ An interruptible key-agreement operation is used as follows: Number of *ops* that the operation has taken so far. After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. - The value is reset to zero by a successful call to either `psa_key_agreement_iop_setup()` or `psa_key_agreement_iop_abort()`. - A failed call to `psa_key_agreement_iop_setup()` can also reset the value to zero. + The value is reset to zero by a successful call to either `psa_key_agreement_iop_start()` or `psa_key_agreement_iop_abort()`. + A failed call to `psa_key_agreement_iop_start()` can also reset the value to zero. 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 +.. function:: psa_key_agreement_iop_start .. summary:: Start an interruptible operation to perform a key agreement. @@ -460,7 +460,7 @@ An interruptible key-agreement operation is used as follows: .. versionadded:: 1.6 .. param:: psa_key_agreement_iop_t * operation - The interruptible key-agreement operation to set up. + The interruptible key-agreement operation to start. 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. @@ -551,15 +551,15 @@ An interruptible key-agreement operation is used as follows: .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE - This function sets up an interruptible operation to perform a key-agreement. + This function starts 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. - If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_key_agreement_iop_complete()` returns this error. + If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_start()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether `psa_key_agreement_iop_complete()` returns this error. Applications must be prepared for either function to report this error. - After a successful call to `psa_key_agreement_iop_setup()`, the operation is active. + After a successful call to `psa_key_agreement_iop_start()`, 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: @@ -567,7 +567,7 @@ An interruptible key-agreement operation is used as follows: * 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 remains inactive, but its number of *ops* can be reset to zero. + If `psa_key_agreement_iop_start()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_key_agreement_iop_complete @@ -609,11 +609,11 @@ An interruptible key-agreement operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_setup()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. + If a persistent key identifier already exists, then it is unspecified whether `psa_key_agreement_iop_start()` returns :code:`PSA_ERROR_ALREADY_EXISTS`, or whether this function returns this error. Applications must be prepared for either function to report this error. 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. + The attributes of the new key are specified in the call to `psa_key_agreement_iop_start()` used to start this operation. This key can be input to a key derivation operation using `psa_key_derivation_input_key()`. .. warning:: @@ -643,11 +643,11 @@ An interruptible key-agreement operation is used as follows: .. 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. + 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_start()` 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. + 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 started. Support macros -------------- diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 8e4e519f..b0a24fb2 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -2796,7 +2796,7 @@ An interruptible signature operation is used as follows: 1. Allocate an interruptible 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_start()` to specify the algorithm and key. #. Call `psa_sign_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_sign_iop_set_context()` to provide a context. #. Either: @@ -2874,14 +2874,14 @@ An interruptible signature operation is used as follows: .. 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* spent on the entire operation. The value is reset to zero by a successful call to either `psa_sign_iop_setup()` or `psa_sign_iop_abort()`. - A failed call to `psa_sign_iop_setup()` can also reset the value to zero. + After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. The value is reset to zero by a successful call to either `psa_sign_iop_setup_start()` or `psa_sign_iop_abort()`. + A failed call to `psa_sign_iop_setup_start()` can also reset the value to zero. 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 +.. function:: psa_sign_iop_setup_start .. summary:: Begin the setup of an interruptible signature operation. @@ -2931,12 +2931,12 @@ An interruptible signature operation is used as follows: 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 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: + After a successful call to `psa_sign_iop_setup_start()`, 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 remains inactive, but its number of *ops* can be reset to zero. + If `psa_sign_iop_setup_start()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. .. function:: psa_sign_iop_setup_complete @@ -3209,7 +3209,7 @@ An interruptible signature operation is used as follows: .. 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. + 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_start()` again. This function can be called at any time after the operation object has been initialized as described in `psa_sign_iop_t`. @@ -3227,7 +3227,7 @@ An interruptible verification operation is used as follows: 1. Allocate an interruptible 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_start()` to specify the algorithm, key, and the signature to verify. #. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_verify_iop_set_context()` to provide a context. #. Either: @@ -3239,7 +3239,7 @@ An interruptible verification operation is used as follows: To verify a message received from a streaming protocol that provides the signature after the message data, use the deferred-signature flow instead. The first two steps are the same as above, and then: -3. Call `psa_verify_iop_setup_deferred_signature()` to specify the algorithm and key. +3. Call `psa_verify_iop_setup_deferred_signature_start()` to specify the algorithm and key. #. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function returns a status code other than :code:`PSA_OPERATION_INCOMPLETE`. #. Optionally, call `psa_verify_iop_set_context()` to provide a context. #. Call `psa_verify_iop_update()` zero, one or more times, passing a fragment of the message each time. @@ -3315,14 +3315,14 @@ To verify a message received from a streaming protocol that provides the signatu .. 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* spent on the entire operation. The value is reset to zero by a successful call to `psa_verify_iop_setup()`, `psa_verify_iop_setup_deferred_signature()`, or `psa_verify_iop_abort()`. + After the interruptible operation has completed, the returned value is the number of *ops* spent on the entire operation. The value is reset to zero by a successful call to `psa_verify_iop_setup_start()`, `psa_verify_iop_setup_deferred_signature_start()`, or `psa_verify_iop_abort()`. A failed call to either setup function can also reset the value to zero. 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 +.. function:: psa_verify_iop_setup_start .. summary:: Begin the setup of an interruptible verification operation. @@ -3379,16 +3379,16 @@ To verify a message received from a streaming protocol that provides the signatu 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 signature operation, see :secref:`interruptible-sign`. Use this function when the signature is available before the message or hash input. It supports all signature algorithms that are available through the interruptible verification operation. - To verify a message whose signature is available only after the message data, use `psa_verify_iop_setup_deferred_signature()` instead. + To verify a message whose signature is available only after the message data, use `psa_verify_iop_setup_deferred_signature_start()` instead. - 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: + After a successful call to `psa_verify_iop_setup_start()`, 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 remains inactive, but its number of *ops* can be reset to zero. + If `psa_verify_iop_setup_start()` returns an error, the operation object remains inactive, but its number of *ops* can be reset to zero. -.. function:: psa_verify_iop_setup_deferred_signature +.. function:: psa_verify_iop_setup_deferred_signature_start .. summary:: Begin the setup of an interruptible verification operation with a deferred signature. @@ -3438,7 +3438,7 @@ To verify a message received from a streaming protocol that provides the signatu `PSA_ALG_SIGN_SUPPORTS_DEFERRED_SIGNATURE()` can be used to determine whether a signature algorithm supports this flow. - After a successful call to `psa_verify_iop_setup_deferred_signature()`, 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: + After a successful call to `psa_verify_iop_setup_deferred_signature_start()`, 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()`. @@ -3542,7 +3542,7 @@ To verify a message received from a streaming protocol that provides the signatu .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must have been set up with `psa_verify_iop_setup()`, with no data input, and completion must not have started. + The interruptible verification operation to use. The operation must have been set up with `psa_verify_iop_setup_start()`, with no data input, and completion must not have started. .. param:: const uint8_t * hash The input whose signature is to be verified. This is usually the hash of a message. @@ -3557,7 +3557,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup()`, and no call to `psa_verify_iop_hash()`, `psa_verify_iop_update()`, or `psa_verify_iop_complete()` has been made. + * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup_start()`, and no call to `psa_verify_iop_hash()`, `psa_verify_iop_update()`, or `psa_verify_iop_complete()` has been made. * 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. @@ -3576,7 +3576,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID - The application must complete the setup of the operation before calling this function. A pre-computed hash can only be used with an interruptible verification operation that provides the signature as part of the set up, using `psa_verify_iop_setup()`. + The application must complete the setup of the operation before calling this function. A pre-computed hash can only be used with an interruptible verification operation that provides the signature as part of the set up, using `psa_verify_iop_setup_start()`. 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))`. @@ -3646,7 +3646,7 @@ To verify a message received from a streaming protocol that provides the signatu .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. It must have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and completion must not have started. + The interruptible verification operation to use. It must have been set up with `psa_verify_iop_setup_deferred_signature_start()`, setup must be complete, and completion must not have started. .. param:: const uint8_t * signature Buffer containing the signature to verify. .. param:: size_t signature_length @@ -3659,7 +3659,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup_deferred_signature()`, setup must be complete, and no call to `psa_verify_iop_set_signature()` or `psa_verify_iop_complete()` has been made. + * The operation state is not valid: the operation must have been set up with `psa_verify_iop_setup_deferred_signature_start()`, setup must be complete, and no call to `psa_verify_iop_set_signature()` or `psa_verify_iop_complete()` has been made. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_ARGUMENT ``signature`` is not a valid signature for the algorithm and key. @@ -3669,7 +3669,7 @@ To verify a message received from a streaming protocol that provides the signatu .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED - The application must complete the setup of the operation before calling this function. This function is for use with the deferred-signature flow, when the operation is set up by calling `psa_verify_iop_setup_deferred_signature()`. + The application must complete the setup of the operation before calling this function. This function is for use with the deferred-signature flow, when the operation is set up by calling `psa_verify_iop_setup_deferred_signature_start()`. The application must call this function after all calls to `psa_verify_iop_update()`, and before the first call to `psa_verify_iop_complete()`. @@ -3683,7 +3683,7 @@ To verify a message received from a streaming protocol that provides the signatu .. versionadded:: 1.6 .. param:: psa_verify_iop_t * operation - The interruptible verification operation to use. The operation must be active, and setup must be complete. If the operation was set up with `psa_verify_iop_setup_deferred_signature()`, the signature must have been provided with `psa_verify_iop_set_signature()`. + The interruptible verification operation to use. The operation must be active, and setup must be complete. If the operation was set up with `psa_verify_iop_setup_deferred_signature_start()`, the signature must have been provided with `psa_verify_iop_set_signature()`. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -3742,7 +3742,7 @@ To verify a message received from a streaming protocol that provides the signatu .. 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()` or `psa_verify_iop_setup_deferred_signature()`. + 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_start()` or `psa_verify_iop_setup_deferred_signature_start()`. This function can be called at any time after the operation object has been initialized as described in `psa_verify_iop_t`. @@ -3839,7 +3839,7 @@ Support macros ``0`` if ``alg`` is a signature algorithm that requires the signature before message input. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported signature algorithm identifier. - This macro identifies algorithms that can be used with the deferred-signature interruptible verification flow, beginning with `psa_verify_iop_setup_deferred_signature()`. + This macro identifies algorithms that can be used with the deferred-signature interruptible verification flow, beginning with `psa_verify_iop_setup_deferred_signature_start()`. It indicates algorithm compatibility only. An implementation can still return :code:`PSA_ERROR_NOT_SUPPORTED` if it does not support the deferred-signature flow for the algorithm. .. macro:: PSA_ALG_ANY_HASH diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf index 49bd453646c2223f87f312e8d7eb6ba5058019a9..5f6cd4ed4f4df53441d0717141afba7f37bc33d8 100644 GIT binary patch literal 25877 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 literal 32413 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 NPJu>CE2%7n_FtLw>QVpz diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation.puml index 2a49b993..df7508c2 100644 --- a/doc/crypto/figure/interruptible_operation.puml +++ b/doc/crypto/figure/interruptible_operation.puml @@ -2,7 +2,7 @@ ' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license @startuml -!include atg-spec.pumh +!include psa-spec.pumh skinparam LegendFontSize 12 @@ -21,11 +21,11 @@ note as N1 Operation object starts as uninitialised memory end note -inactive --> active: **Setup** +inactive --> active: **Start** active --> active: **Complete**\n//incomplete// active --> inactive: **Complete**\n//success// error -[#darkred,dashed]r-> inactive: **Abort** -inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +inactive -[#darkred,dashed]-> inactive: **Start**\n//fails// active -[#darkred,dashed]-> error: **Complete**\n//fails// active -[#blue,dotted]l-> inactive: **Abort** diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg index 1efea1a0..2273ea55 100644 --- a/doc/crypto/figure/interruptible_operation.svg +++ b/doc/crypto/figure/interruptible_operation.svg @@ -1,9 +1 @@ -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 +inactiveactiveerrorOperation object starts asuninitialised memoryInitializeStartCompletesuccessAbortCompleteincompleteAbortStartfailsCompletefails———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.pdf b/doc/crypto/figure/interruptible_operation_complex.pdf index b2bd387ca619ed8adbc158be2858b2b5a85d3869..3a51237bc421a614b1b90bee4a1bda5a98ba18b9 100644 GIT binary patch literal 28096 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

jT`Z?HuwH{}J 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 P9tea+ODmxyiT1w$|A<9C diff --git a/doc/crypto/figure/interruptible_operation_complex.puml b/doc/crypto/figure/interruptible_operation_complex.puml index 00a2c339..e4ffa518 100644 --- a/doc/crypto/figure/interruptible_operation_complex.puml +++ b/doc/crypto/figure/interruptible_operation_complex.puml @@ -2,7 +2,7 @@ ' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license @startuml -!include atg-spec.pumh +!include psa-spec.pumh skinparam LegendFontSize 12 @@ -23,7 +23,7 @@ note as N1 Operation object starts as uninitialised memory end note -inactive --> setup: **Setup**\n//success// +inactive --> setup: **Setup-start**\n//success// setup --> setup: **Setup-complete**\n//incomplete// setup --> input: **Setup-complete**\n//success// input --> input: **Update**\n//success// @@ -32,7 +32,7 @@ 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// +inactive -[#darkred,dashed]-> inactive: **Setup-start**\n//fails// setup -[#darkred,dashed]-> error: **Setup-complete**\n//fails// input -[#darkred,dashed]-> error: **Update**\n//fails// input -[#darkred,dashed]-> error: **Complete**\n//fails// diff --git a/doc/crypto/figure/interruptible_operation_complex.svg b/doc/crypto/figure/interruptible_operation_complex.svg index 83d741c6..38674281 100644 --- a/doc/crypto/figure/interruptible_operation_complex.svg +++ b/doc/crypto/figure/interruptible_operation_complex.svg @@ -1,19 +1 @@ -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 +inactivesetupinputcompletingerrorOperation object starts asuninitialised memoryInitializeSetup-startsuccessAbortSetup-completeincompleteSetup-completesuccessUpdatesuccessCompleteincompleteCompletesuccessAbortCompleteincompleteCompletesuccessAbortAbortSetup-startfailsSetup-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/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 6bfbc06b..abeba5c0 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -253,7 +253,7 @@ There are three components in an interruptible operation: 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 reset when the operation is aborted, or when an operation object is successfully set up for a new operation. A failed setup can also reset the value. This permits the final value to be queried after an operation has finished successfully. + Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is reset when the operation is aborted, or when an operation object is successfully started for a new operation. A failed start can also reset the value. 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`. @@ -281,12 +281,12 @@ The typical sequence of actions with an interruptible operation is as follows: 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. +#. **Start:** Start a new interruptible operation on an *inactive* operation object. + Each interruptible operation object will define one or more functions that 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 success, a start 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. @@ -306,7 +306,7 @@ The typical sequence of actions with an interruptible operation is as follows: ``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. +Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the start functions again. If an interruptible operation object is not initialized before use, the behavior is undefined. @@ -322,7 +322,7 @@ However, this does not permit the following behaviors: * 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. +Documentation for the specific operation describes the start and completion functions, and any requirements about their usage and ordering. See :secref:`interruptible-generate-key` for an example of an interruptible operation. @@ -348,7 +348,7 @@ The sequence has the common interruptible-operation steps, with a setup phase th #. **Initialize** -#. **Begin setup:** Start a new interruptible signature or verification operation on an *inactive* object. +#. **Start setup:** Start the setup phase of a new interruptible signature or verification operation on an *inactive* object. The accumulated *ops* value for the operation is reset to zero. From b37b5d553b931dced56d082b03c5ec24e73668a9 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Aug 2026 16:37:10 +0100 Subject: [PATCH 21/22] Clarify interruptible key-generation sequencing --- doc/crypto/api/keys/management.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 0e2b2be7..9c5913b2 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -1023,7 +1023,8 @@ An interruptible key-generation operation is used as follows: 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_start()`, the operation is active. - The operation can be configured with custom production parameters by calling `psa_generate_key_iop_custom()`, or completed by calling `psa_generate_key_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Before calling `psa_generate_key_iop_complete()`, the application can configure the operation with custom production parameters by calling `psa_generate_key_iop_custom()`. + The application must then call `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()`. @@ -1040,7 +1041,7 @@ An interruptible key-generation operation is used as follows: .. param:: psa_generate_key_iop_t * operation The interruptible key-generation operation to configure. - The operation must be active, and `psa_generate_key_iop_complete()` must not have been called. + The operation must be active, and neither `psa_generate_key_iop_custom()` nor `psa_generate_key_iop_complete()` must have been called. .. param:: const psa_custom_key_parameters_t * custom Customized production parameters for the key generation. .. param:: const uint8_t * custom_data @@ -1058,7 +1059,7 @@ An interruptible key-generation operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: it must be active, and `psa_generate_key_iop_complete()` must not have been called. + * The operation state is not valid: it must be active, and neither `psa_generate_key_iop_custom()` nor `psa_generate_key_iop_complete()` must have been called. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE @@ -1072,6 +1073,8 @@ An interruptible key-generation operation is used as follows: 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. + After a successful call to this function, the application must call `psa_generate_key_iop_complete()` repeatedly, 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_generate_key_iop_abort()`. .. function:: psa_generate_key_iop_complete From c92935914b80a568f7b37936bd26ebff71069212 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Aug 2026 17:54:49 +0100 Subject: [PATCH 22/22] Clarify interruptible operation limits --- doc/crypto/overview/functionality.rst | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index abeba5c0..6b840755 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -245,15 +245,25 @@ 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: +Interruptible operations have three principal components: * 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. +* The concept of a unit of work --- called *ops* --- that can be carried out by an interruptible operation function. - 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. +*ops* limits and accounting +^^^^^^^^^^^^^^^^^^^^^^^^^^^ - Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is reset when the operation is aborted, or when an operation object is successfully started for a new operation. A failed start can also reset the value. This permits the final value to be queried after an operation has finished successfully. +The *maximum ops* value limits the *ops* performed by each call to an interruptible function. An application sets this value by calling `psa_iop_set_max_ops()`, and can query the current value with `psa_iop_get_max_ops()`. If a function exhausts the maximum *ops* before its calculation is complete, it returns :code:`PSA_OPERATION_INCOMPLETE`. + +After a successful call to `psa_crypto_init()`, the *maximum ops* value defaults to `PSA_IOP_MAX_OPS_UNLIMITED`. With this default, interruptible functions complete their calculation before returning. An application can set a lower value to bound the computation performed in one function call. + +The computation and execution time represented by an *op* have no fixed meaning across implementations or functions. They can depend on the hardware, algorithm, key type, and current stage of the operation. Applications can use the cumulative *ops* count to tune the maximum value for a particular implementation. + +The cumulative *ops* count for an interruptible operation can be retrieved by calling the operation's associated ``psa_xxx_iop_get_num_ops()`` function. This count can be queried while the operation is active, or after a successful completion to obtain the count for the entire operation. It is reset when the operation is aborted or when an operation object is successfully started for a new operation. A failed start can also reset the count. + +Interruptible operation flow +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Interruptible operations follow a common pattern of use, which is shown in :numref:`fig-interruptible`.