From 10cfb62ac14913be07d7f8ede2e7c328f1ae73fc Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 18 Sep 2023 13:01:10 +0100 Subject: [PATCH 01/12] Remove hard line-wraps --- doc/crypto/overview/functionality.rst | 191 +++++++------------------- 1 file changed, 49 insertions(+), 142 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 7be012c7..edd2d600 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -6,36 +6,25 @@ Functionality overview ====================== -This section provides a high-level overview of the functionality provided by the -interface defined in this specification. Refer to the -API definition for a detailed description, which begins with :secref:`library-management`. +This section provides a high-level overview of the functionality provided by the interface defined in this specification. Refer to the API definition for a detailed description, which begins with :secref:`library-management`. -:secref:`future` describes features that might be included in future versions of this -specification. +:secref:`future` describes features that might be included in future versions of this specification. -Due to the modularity of the interface, almost every part of the library is -optional. The only mandatory function is `psa_crypto_init()`. +Due to the modularity of the interface, almost every part of the library is optional. The only mandatory function is `psa_crypto_init()`. Library management ------------------ -Applications must call `psa_crypto_init()` to initialize the library before -using any other function. +Applications must call `psa_crypto_init()` to initialize the library before using any other function. .. _key-overview: Key management -------------- -Applications always access keys indirectly via an identifier, and can perform -operations using a key without accessing the key material. This allows keys to -be *non-extractable*, where an application can use a key but is not permitted to -obtain the key material. Non-extractable keys are bound to the device, can be -rate-limited and can have their usage restricted by policies. +Applications always access keys indirectly via an identifier, and can perform operations using a key without accessing the key material. This allows keys to be *non-extractable*, where an application can use a key but is not permitted to obtain the key material. Non-extractable keys are bound to the device, can be rate-limited and can have their usage restricted by policies. -Each key has a set of attributes that describe the key and the policy for using -the key. A `psa_key_attributes_t` object contains all of the attributes, which -is used when creating a key and when querying key attributes. +Each key has a set of attributes that describe the key and the policy for using the key. A `psa_key_attributes_t` object contains all of the attributes, which is used when creating a key and when querying key attributes. The key attributes include: @@ -55,15 +44,9 @@ Keys are created using one of the *key creation functions*: These output the key identifier, that is used to access the key in all other parts of the API. -All of the key attributes are set when the key is created and cannot be changed -without destroying the key first. If the original key permits copying, then the -application can specify a different lifetime or restricted policy for the -copy of the key. +All of the key attributes are set when the key is created and cannot be changed without destroying the key first. If the original key permits copying, then the application can specify a different lifetime or restricted policy for the copy of the key. -A call to `psa_destroy_key()` destroys the key material, and will cause any active -operations that are using the key to fail. Therefore an application must not -destroy a key while an operation using that key is in progress, unless the -application is prepared to handle a failure of the operation. +A call to `psa_destroy_key()` destroys the key material, and will cause any active operations that are using the key to fail. Therefore an application must not destroy a key while an operation using that key is in progress, unless the application is prepared to handle a failure of the operation. .. _key-types-intro: @@ -85,9 +68,7 @@ Key identifiers are integral values that act as permanent names for persistent k Key identifiers are output from a successful call to one of the key creation functions. -Valid key identifiers must have distinct values within the same application. If -the implementation provides :term:`caller isolation`, then key -identifiers are local to each application. +Valid key identifiers must have distinct values within the same application. If the implementation provides :term:`caller isolation`, then key identifiers are local to each application. See :secref:`key-identifiers`. @@ -118,22 +99,13 @@ Recommendations of minimum standards for key management Most implementations provide the following functions: -* `psa_import_key()`. The exceptions are implementations that only give access - to a key or keys that are provisioned by proprietary means, and do not allow - the main application to use its own cryptographic material. +* `psa_import_key()`. The exceptions are implementations that only give access to a key or keys that are provisioned by proprietary means, and do not allow the main application to use its own cryptographic material. -* `psa_get_key_attributes()` and the ``psa_get_key_xxx()`` accessor functions. - They are easy to implement, and it is difficult to write applications and to - diagnose issues without being able to check the metadata. +* `psa_get_key_attributes()` and the ``psa_get_key_xxx()`` accessor functions. They are easy to implement, and it is difficult to write applications and to diagnose issues without being able to check the metadata. -* `psa_export_public_key()`. This function is usually provided if the - implementation supports any asymmetric algorithm, since public-key - cryptography often requires the delivery of a public key that is associated - with a protected private key. +* `psa_export_public_key()`. This function is usually provided if the implementation supports any asymmetric algorithm, since public-key cryptography often requires the delivery of a public key that is associated with a protected private key. -* `psa_export_key()`. However, highly constrained implementations that are - designed to work only with short-term keys, or only with long-term - non-extractable keys, do not need to provide this function. +* `psa_export_key()`. However, highly constrained implementations that are designed to work only with short-term keys, or only with long-term non-extractable keys, do not need to provide this function. Cryptographic operations ------------------------ @@ -146,24 +118,16 @@ For each type of cryptographic operation, the API can include: Single-part Functions ~~~~~~~~~~~~~~~~~~~~~ -Single-part functions are APIs that implement the cryptographic operation in a -single function call. This is the easiest API to use when all of the inputs and -outputs fit into the application memory. +Single-part functions are APIs that implement the cryptographic operation in a single function call. This is the easiest API to use when all of the inputs and outputs fit into the application memory. -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 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 `. .. _multi-part-operations: Multi-part operations ~~~~~~~~~~~~~~~~~~~~~ -Multi-part operations are APIs which split a single cryptographic operation into -a sequence of separate steps. This enables fine control over the configuration -of the cryptographic operation, and allows the message data to be processed in -fragments instead of all at once. For example, the following situations require -the use of a multi-part operation: +Multi-part operations are APIs which split a single cryptographic operation into a sequence of separate steps. This enables fine control over the configuration of the cryptographic operation, and allows the message data to be processed in fragments instead of all at once. For example, the following situations require the use of a multi-part operation: * Processing messages that cannot be assembled in memory. * Using a deterministic IV for unauthenticated encryption. @@ -171,8 +135,7 @@ the use of a multi-part operation: * Separating the AEAD authentication tag from the cipher text. * Password-authenticated key exchange (PAKE) is a multi-step process. -Each multi-part operation defines a specific object type to maintain the state -of the operation. These types are implementation-defined. +Each multi-part operation defines a specific object type to maintain the state of the operation. These types are implementation-defined. All multi-part operations follow the same pattern of use, which is shown in :numref:`fig-multi-part`. @@ -183,59 +146,36 @@ All multi-part operations follow the same pattern of use, which is shown in :num The typical sequence of actions with a multi-part 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. +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 operation object by one of the - following methods: +#. **Initialize:** Initialize or assign the 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_INIT`` macro as the type is implementation-defined. - - Set it to all-bits zero. This is automatic if the object was - allocated with ``calloc()``. + - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_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_INIT``. - - Assign the result of calling the associated function - ``psa_xxx_init()``. + - Assign the result of calling the associated function ``psa_xxx_init()``. The resulting object is now *inactive*. - It is an error to initialize an operation object that is in *active* or - *error* states. This can leak memory or other resources. + It is an error to initialize an operation object that is in *active* or *error* states. This can leak memory or other resources. -#. **Setup:** Start a new multi-part operation on an *inactive* operation - object. Each operation object will define one or more setup functions to - start a specific operation. +#. **Setup:** Start a new multi-part operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. - On success, a setup function will put an operation object into an *active* - state. On failure, the operation object will remain *inactive*. + On success, a setup function will put an operation object into an *active* state. On failure, the operation object will remain *inactive*. -#. **Update:** Update an *active* operation object. Each operation object - defines one or more update functions, which are used to provide additional - parameters, supply data for processing or generate outputs. +#. **Update:** Update an *active* operation object. Each operation object defines one or more update functions, which are used to provide additional parameters, supply data for processing or generate outputs. - On success, the operation object remains *active*. On failure, the - operation object will enter an *error* state. + On success, the operation object remains *active*. On failure, the operation object will enter an *error* state. -#. **Finish:** To end the operation, call the applicable finishing function. - This will take any final inputs, produce any final outputs, and then - release any resources associated with the operation. +#. **Finish:** To end the operation, call the applicable finishing function. This will take any final inputs, produce any final outputs, and then release any resources associated with the operation. - On success, the operation object returns to the *inactive* state. On - failure, the operation object will enter an *error* state. + On success, the operation object returns to the *inactive* state. On failure, the operation object will enter an *error* state. -#. **Abort:** An operation can be aborted at any stage during its use by - calling the associated ``psa_xxx_abort()`` function. This will release any - resources associated with the operation and return the operation object to - the *inactive* state. +#. **Abort:** An operation can be aborted at any stage during its use by calling the associated ``psa_xxx_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. - 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_abort()`` function to release the - operation resources and return the object to the *inactive* state. + 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_abort()`` function to release the operation resources and return the object to the *inactive* state. - ``psa_xxx_abort()`` can be called on an *inactive* operation, and this - has no effect. + ``psa_xxx_abort()`` can be called on an *inactive* operation, and this has no effect. .. rationale:: @@ -256,32 +196,18 @@ The typical sequence of actions with a multi-part operation is as follows: Once an operation object is returned to the *inactive* state, it can be reused by calling one of the applicable setup functions again. -If a multi-part operation object is not initialized before use, the behavior is -undefined. +If a multi-part operation object is not initialized before use, the behavior is undefined. -If a multi-part operation function determines that the operation object is not in -any valid state, it can return :code:`PSA_ERROR_CORRUPTION_DETECTED`. +If a multi-part operation function determines that the operation object is not in any valid state, it can return :code:`PSA_ERROR_CORRUPTION_DETECTED`. -If a multi-part 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. +If a multi-part 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 a multi-part 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: +It is safe to move a multi-part 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. This is - not safe. See also :secref:`concurrency`. -* Working with both the original and the copied operation objects. This - requires cloning the operation, which is only available for hash operations - using `psa_hash_clone()`. +* Moving the object while a function is being called on the object. This is not safe. See also :secref:`concurrency`. +* Working with both the original and the copied operation objects. This requires cloning the operation, which is only available for hash operations using `psa_hash_clone()`. -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. +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. Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ @@ -302,31 +228,22 @@ Key derivation only provides multi-part operation, to support the flexibility re Example of the symmetric cryptography API ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -Here is an example of a use case where a master key is used to generate both a -message encryption key and an IV for the encryption, and the derived key and IV -are then used to encrypt a message. +Here is an example of a use case where a master key is used to generate both a message encryption key and an IV for the encryption, and the derived key and IV are then used to encrypt a message. 1. Derive the message encryption material from the master key. - a. Initialize a `psa_key_derivation_operation_t` object to zero or to - `PSA_KEY_DERIVATION_OPERATION_INIT`. + a. Initialize a `psa_key_derivation_operation_t` object to zero or to `PSA_KEY_DERIVATION_OPERATION_INIT`. #. Call `psa_key_derivation_setup()` with `PSA_ALG_HKDF` as the algorithm. - #. Call `psa_key_derivation_input_key()` with the step - `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. - #. Call `psa_key_derivation_input_bytes()` with the step - `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely - identifies the message. - #. Populate a `psa_key_attributes_t` object with the derived message - encryption key’s attributes. + #. Call `psa_key_derivation_input_key()` with the step `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. + #. Call `psa_key_derivation_input_bytes()` with the step `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely identifies the message. + #. 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. - a. Initialize a `psa_cipher_operation_t` object to zero or to - `PSA_CIPHER_OPERATION_INIT`. + a. Initialize a `psa_cipher_operation_t` object to zero or to `PSA_CIPHER_OPERATION_INIT`. #. Call `psa_cipher_encrypt_setup()` with the derived message encryption key. #. Call `psa_cipher_set_iv()` using the derived IV retrieved above. #. Call `psa_cipher_update()` one or more times to encrypt the message. @@ -349,16 +266,6 @@ For asymmetric encryption and signature, the API provides *single-part* function Randomness and key generation ----------------------------- -We strongly recommended that implementations include a random generator, -consisting of a cryptographically secure pseudo-random generator (CSPRNG), which -is adequately seeded with a cryptographic-quality hardware entropy source, -commonly referred to as a true random number generator (TRNG). Constrained -implementations can omit the random generation functionality if they do not -implement any algorithm that requires randomness internally, and they do not -provide a key generation functionality. For example, a special-purpose component -for signature verification can omit this. - -It is recommended that applications use `psa_generate_key()`, -`psa_cipher_generate_iv()` or `psa_aead_generate_nonce()` to generate -suitably-formatted random data, as applicable. In addition, the API includes a -function `psa_generate_random()` to generate and extract arbitrary random data. +We strongly recommended that implementations include a random generator, consisting of a cryptographically secure pseudo-random generator (CSPRNG), which is adequately seeded with a cryptographic-quality hardware entropy source, commonly referred to as a true random number generator (TRNG). Constrained implementations can omit the random generation functionality if they do not implement any algorithm that requires randomness internally, and they do not provide a key generation functionality. For example, a special-purpose component for signature verification can omit this. + +It is recommended that applications use `psa_generate_key()`, `psa_cipher_generate_iv()` or `psa_aead_generate_nonce()` to generate suitably-formatted random data, as applicable. In addition, the API includes a function `psa_generate_random()` to generate and extract arbitrary random data. From ac5fa8b8e26076f6179a7ef157fbd1351e024856 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 25 Sep 2023 17:28:56 +0100 Subject: [PATCH 02/12] Add an overview of interruptible operation functionality --- doc/crypto/figure/interruptible_operation.pdf | Bin 0 -> 37853 bytes .../interruptible_operation.pdf.license | 2 + .../figure/interruptible_operation.puml | 44 ++++++++ doc/crypto/figure/interruptible_operation.svg | 19 ++++ .../interruptible_operation.svg.license | 2 + doc/crypto/overview/functionality.rst | 95 +++++++++++++++++- 6 files changed, 161 insertions(+), 1 deletion(-) create mode 100644 doc/crypto/figure/interruptible_operation.pdf create mode 100644 doc/crypto/figure/interruptible_operation.pdf.license 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.svg.license diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..81472000390439f607d973bdad5919eeb9a4b5c6 GIT binary patch literal 37853 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®>tfIj8wm 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 literal 0 HcmV?d00001 diff --git a/doc/crypto/figure/interruptible_operation.pdf.license b/doc/crypto/figure/interruptible_operation.pdf.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation.puml new file mode 100644 index 00000000..a50a2f4d --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.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 starting as "//starting//" +state active as "//active//" +state finishing as "//finishing//" +state error as "//error//" ##darkred + +[*] --> inactive: **Initialize** +note as N1 + Operation object starts as + uninitialised memory +end note +inactive --> starting: **Setup** +starting --> starting: **Setup-complete**\n//incomplete// +starting --> active: **Setup-complete** +active --> active: **Update** +active --> finishing: **Finish**\n//incomplete// +active --> inactive: **Finish** +finishing --> finishing: **Finish**\n//incomplete// +finishing --> inactive: **Finish** +error -[#darkred,dashed]-> inactive: **Abort** +inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +starting -[#darkred,dashed]-> error: **Setup-complete**\n//fails// +active -[#darkred,dashed]-> error: **Update**\n//fails// +active -[#darkred,dashed]-> error: **Finish**\n//fails// +finishing -[#darkred,dashed]-> error: **Finish**\n//fails// +starting -[#blue,dotted]-> inactive: **Abort** +active -[#blue,dotted]-> inactive: **Abort** +finishing -[#blue,dotted]-> 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..575421bc --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg @@ -0,0 +1,19 @@ +inactivestartingactivefinishingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateFinishincompleteFinishAbortFinishincompleteFinishAbortAbortSetupfailsSetup-completefailsUpdatefailsFinishfailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/interruptible_operation.svg.license b/doc/crypto/figure/interruptible_operation.svg.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index edd2d600..b50f1573 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -261,7 +261,100 @@ This specification defines interfaces for the following types of asymmetric cryp * Two-way key agreement (also known as key establishment). See :secref:`key-agreement`. * Password-authenticated key exchange (PAKE). See :secref:`pake`. -For asymmetric encryption and signature, 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 PAKE, the API provides a multi-part operation. +For asymmetric encryption, the API provides *single-part* functions. + +For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). + +For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. + +For PAKE, the API provides a *multi-part* operation. + +.. _interruptible-operations: + +Interruptible operations +^^^^^^^^^^^^^^^^^^^^^^^^ + +Interruptible operations are APIs which split a single computationally-expensive operation into a sequence of separate function calls, each of which has a bounded execution time. Interruptible operations are useful in application contexts where responsiveness is critical, and techniques such a multi-threading are not available. This is achieved by limiting the amount of computational progress that is made for each function call. + +For some use cases, this can be achieved by controlling the amount of data processed by the function. For example, a hash can be computed using a multi-part operation to break up the computation into smaller blocks. For other use cases, the execution time is a consequence of the computational complexity of the algorithm, and a multi-part operation does not enable a caller to bound the runtime of each function. For example, verifying an asymmetric signature. + +.. note:: + + Unlike multi-part operations, an interruptible operation does not provide additional control over the inputs to an algorithm. If an application does not need to interrupt the operation, it is recommended that the appropriate single-part function is used. + +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 *max ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *max ops* value can also be queried. + + 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 object is set up for a new operation, which permits the value to be queried after an operation has finished. + +All interruptible operations follow the same pattern of use, which is shown in :numref:`fig-interruptible`. + +.. figure:: /figure/interruptible_operation.* + :name: fig-interruptible + + General state model for an interruptible operation + +The typical sequence of actions with a interruptible operation is as follows: + +1. **Allocate:** Allocate memory for an operation object of the appropriate type. The application can use any allocation strategy: stack, heap, static, etc. + +#. **Initialize:** Initialize or assign the 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_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_INIT``. + - Assign the result of calling the associated function ``psa_xxx_init()``. + + The resulting object is now *inactive*. + It is an error to initialize an operation object that is in *active* or *error* states. This can leak memory or other resources. + +#. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. + + On success, an operation object enters a *starting* state. On failure, the operation object will remain *inactive*. + +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. + + If the setup computation is interrupted, a the operation remains in *starting* state. If setup completes successfully, the operation enters an *active* state. On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the setup completes with success or an error status. + +#. **Update:** Update an *active* interruptible operation object. The update function can provide additional parameters, supply data for processing or generate outputs. + + On success, the operation object remains *active*. On failure, the operation object will enter an *error* state. + +#. **Finish:** To end an interruptible operation, call the applicable finishing function. This will take any final inputs, produce any final outputs, and then release any resources associated with the operation. + + If the finishing computation is interrupted, a the operation is left in *finishing* state. If finishing 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 finishing function completes with success or an error status. + +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. + + 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_abort()`` function to release the operation resources and return the object to the *inactive* state. + + ``psa_xxx_abort()`` can be called on an *inactive* 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 applicable 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 *starting*, *active*, and *finishing* states. Documentation for the specific operation describes the setup, update and finishing functions, and any requirements about their usage and ordering. + +See :secref:`interruptible_example` for an example of using an interruptible operation. Randomness and key generation ----------------------------- From 22329b899f7334210f0566094adfc3c345de80dc Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 25 Sep 2023 17:29:17 +0100 Subject: [PATCH 03/12] Add PSA_OPERATION_INCOMPLETE --- doc/crypto/api/library/status.rst | 50 ++++++++++++++++--------------- 1 file changed, 26 insertions(+), 24 deletions(-) diff --git a/doc/crypto/api/library/status.rst b/doc/crypto/api/library/status.rst index 51517183..669d8ef3 100644 --- a/doc/crypto/api/library/status.rst +++ b/doc/crypto/api/library/status.rst @@ -12,29 +12,31 @@ The following elements are defined in :file:`psa/error.h` from :cite-title:`PSA- .. code-block:: xref - typedef int32_t psa_status_t; - - #define PSA_SUCCESS ((psa_status_t)0) - - #define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132) - #define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133) - #define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134) - #define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135) - #define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136) - #define PSA_ERROR_BAD_STATE ((psa_status_t)-137) - #define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138) - #define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139) - #define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140) - #define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141) - #define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142) - #define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143) - #define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145) - #define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146) - #define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)-147) - #define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149) - #define PSA_ERROR_CORRUPTION_DETECTED ((psa_status_t)-151) - #define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152) - #define PSA_ERROR_DATA_INVALID ((psa_status_t)-153) + typedef int32_t psa_status_t; + + #define PSA_SUCCESS ((psa_status_t)0) + + #define PSA_ERROR_GENERIC_ERROR ((psa_status_t)-132) + #define PSA_ERROR_NOT_PERMITTED ((psa_status_t)-133) + #define PSA_ERROR_NOT_SUPPORTED ((psa_status_t)-134) + #define PSA_ERROR_INVALID_ARGUMENT ((psa_status_t)-135) + #define PSA_ERROR_INVALID_HANDLE ((psa_status_t)-136) + #define PSA_ERROR_BAD_STATE ((psa_status_t)-137) + #define PSA_ERROR_BUFFER_TOO_SMALL ((psa_status_t)-138) + #define PSA_ERROR_ALREADY_EXISTS ((psa_status_t)-139) + #define PSA_ERROR_DOES_NOT_EXIST ((psa_status_t)-140) + #define PSA_ERROR_INSUFFICIENT_MEMORY ((psa_status_t)-141) + #define PSA_ERROR_INSUFFICIENT_STORAGE ((psa_status_t)-142) + #define PSA_ERROR_INSUFFICIENT_DATA ((psa_status_t)-143) + #define PSA_ERROR_COMMUNICATION_FAILURE ((psa_status_t)-145) + #define PSA_ERROR_STORAGE_FAILURE ((psa_status_t)-146) + #define PSA_ERROR_HARDWARE_FAILURE ((psa_status_t)-147) + #define PSA_ERROR_INVALID_SIGNATURE ((psa_status_t)-149) + #define PSA_ERROR_CORRUPTION_DETECTED ((psa_status_t)-151) + #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. @@ -79,7 +81,7 @@ Some of the common status codes have a more precise meaning when returned by a f .. _specific-errors: Error codes specific to the |API| -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ .. header:: psa/crypto :seq: 2 From e35976ccf05ec2e921fe73e4826b503ff6f5e565 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 12 Oct 2023 17:18:13 +0100 Subject: [PATCH 04/12] crypto: Add interruptible asymmetric signature API Provide interruptible operations for message and hash signature calculation and verification. Reworked information in this section of the API relating to the behavior of different types of signature algorithm, consolidating this to the section introduction. --- doc/crypto/api.db/psa/crypto.h | 42 + doc/crypto/api/library/library.rst | 52 ++ doc/crypto/api/ops/signature.rst | 860 ++++++++++++++++-- doc/crypto/appendix/history.rst | 1 + doc/crypto/figure/interruptible_operation.pdf | Bin 37853 -> 34550 bytes .../figure/interruptible_operation.puml | 36 +- doc/crypto/figure/interruptible_operation.svg | 38 +- doc/crypto/overview/functionality.rst | 24 +- 8 files changed, 942 insertions(+), 111 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 3aa3bc8a..3d089866 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -24,6 +24,8 @@ typedef uint32_t psa_pake_primitive_t; typedef uint8_t psa_pake_primitive_type_t; typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; +typedef /* implementation-defined type */ psa_sign_interruptible_operation_t; +typedef /* implementation-defined type */ psa_verify_interruptible_operation_t; #define PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(ciphertext_length) \ /* implementation-defined value */ #define PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg, ciphertext_length) \ @@ -237,6 +239,7 @@ typedef uint8_t psa_pake_step_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_INTERRUPTIBLE_MAX_OPS_UNLIMITED UINT32_MAX #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 */ @@ -367,10 +370,14 @@ typedef uint8_t psa_pake_step_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_INTERRUPTIBLE_OPERATION_INIT \ + /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ #define PSA_TLS12_ECJPAKE_TO_PMS_OUTPUT_SIZE 32 #define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE /* implementation-defined value */ +#define PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT \ + /* implementation-defined value */ psa_status_t psa_aead_abort(psa_aead_operation_t * operation); psa_status_t psa_aead_decrypt(psa_key_id_t key, psa_algorithm_t alg, @@ -553,6 +560,8 @@ 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_interruptible_get_max_ops(void); +void psa_interruptible_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, @@ -691,6 +700,23 @@ psa_status_t psa_sign_hash(psa_key_id_t key, uint8_t * signature, size_t signature_size, size_t * signature_length); +psa_status_t psa_sign_interruptible_abort(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_complete(psa_sign_interruptible_operation_t * operation, + uint8_t * signature, + size_t signature_size, + size_t * signature_length); +uint32_t psa_sign_interruptible_get_num_ops(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_hash(psa_sign_interruptible_operation_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_sign_interruptible_operation_t psa_sign_interruptible_operation_init(void); +psa_status_t psa_sign_interruptible_setup(psa_sign_interruptible_operation_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); +psa_status_t psa_sign_interruptible_setup_complete(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_update(psa_sign_interruptible_operation_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, @@ -704,6 +730,22 @@ psa_status_t psa_verify_hash(psa_key_id_t key, size_t hash_length, const uint8_t * signature, size_t signature_length); +psa_status_t psa_verify_interruptible_abort(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_complete(psa_verify_interruptible_operation_t * operation); +uint32_t psa_verify_interruptible_get_num_ops(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_hash(psa_verify_interruptible_operation_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_verify_interruptible_operation_t psa_verify_interruptible_operation_init(void); +psa_status_t psa_verify_interruptible_setup(psa_verify_interruptible_operation_t * operation, + psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t * signature, + size_t signature_length); +psa_status_t psa_verify_interruptible_setup_complete(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_update(psa_verify_interruptible_operation_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/library/library.rst b/doc/crypto/api/library/library.rst index 2d734f67..b021b392 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -73,3 +73,55 @@ 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 +----------------------------- + +.. todo:: Provide simple intro to these support functions + +See :secref:`interruptible-operations` + +.. function:: psa_interruptible_set_max_ops + + .. summary:: + Set the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. param:: uint32_t max_ops + The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. + + .. return:: void + + Interruptible functions use this value to limit the computation that is done in any single call to the function. If this limit is reached, the function will return :code:`PSA_OPERATION_INCOMPLETE`, and the caller must repeat the function call until a different status code is returned, or abort the operation. + + After initialization of the implementation, the maximum *ops* defaults to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_interruptible_set_max_ops()` to set a different limit. + + .. note:: + + The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_interruptible_get_num_ops()` and `psa_verify_interruptible_get_num_ops()` are provided to help with tuning this value. + + .. admonition:: Implementation note + + The interpretation of this maximum number is obviously also implementation defined. On a hard real-time system, this can indicate a hard deadline, which is good, as a real-time system needs a guarantee of not spending more than X time, however care must be taken to avoid the situation whereby calls just return, not being able to do any actual work within the allotted time. On a non-real-time system, the implementation can be more relaxed, but again whether this number should be interpreted as as hard or soft limit or even whether a less than or equals as regards to ops executed in a single call is implementation defined. + + .. warning:: + With implementations that interpret this number as a hard limit, setting this number too small can result in an infinite loop, whereby each call results in immediate return with no computation done. + +.. function:: psa_interruptible_get_max_ops + + .. summary:: + Get the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. return:: uint32_t + Maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + This returns the value last set in a call to `psa_interruptible_set_max_ops()`. + +.. macro:: PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED + :definition: UINT32_MAX + + .. summary:: + + Maximum value for use with `psa_interruptible_set_max_ops()`. + + Using this value in a call to `psa_interruptible_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index d802a9ea..6574af35 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -9,13 +9,64 @@ Asymmetric signature ==================== -There are two pairs of single-part functions for asymmetric signature: +An asymmetric signature algorithm provides two functions: -* The signature and verification functions `psa_sign_message()` and `psa_verify_message()` take a message as one of their inputs and perform a hash-and-sign algorithm. +* **Sign**: Calculate a message signature using a private, or secret, key. +* **Verify**: Check that a signature matches a message using a public key. -* The functions `psa_sign_hash()` and `psa_verify_hash()` take a message hash as one of their inputs. This is useful for signing pre-computed hashes, or for implementing hash-and-sign using a :ref:`multi-part hash operation ` before signing the resulting hash. To determine which hash algorithm to use, call the macro `PSA_ALG_GET_HASH()` on the corresponding signature algorithm. +Successful verification indicates that the message signature was calculated using the private key that is associated with the public key. - Some hash-and-sign algorithms add padding to the message hash before completing the signing operation. The format of the padding that is used depends on the algorithm used to construct the signature, see the description of the specific algorithm for details. +In the |API|, an asymmetric-sign function requires an asymmetric key-pair; and an asymmetric-verify function requires an asymmetric public-key or key-pair. + +There are three categories of asymmetric signature algorithm in the |API|: + +* Hash-and-sign algorithms, that have two distinct phases: + + - Calculate a hash of the message + - Calculate a signature over the hash + + For these algorithms, the asymmetric signature API allows applications to either calculate the full message signature, or calculate the signature of a pre-computed hash. For example, this enables the application to use a multi-part hash operation to calculate the hash of a large message, prior to calculating or verifying a signature on the calculated hash. + + The following algorithms are in this category: + + | `PSA_ALG_RSA_PKCS1V15_SIGN` + | `PSA_ALG_RSA_PSS` + | `PSA_ALG_RSA_PSS_ANY_SALT` + | `PSA_ALG_ECDSA` + | `PSA_ALG_DETERMINISTIC_ECDSA` + | `PSA_ALG_ED25519PH` + | `PSA_ALG_ED448PH` + +* Message signature algorithms that do not separate the message processing from the signature calculations. This approach can provide better security against certain types of attack. + + For these algorithms, it is not possible to inject a pre-computed hash into the middle of the algorithm. An application can choose to calculate a message hash, and sign that instead of the message --- but this is not functionally equivalent to signing the message, and eliminates the security benefits of signing the message directly. + + Some of these algorithms still permit the signature of a large message to be calculated, or verified, by providing the message data in fragments. This is possible when the algorithm only processes the message data once. See the individual algorithm descriptions for details. + + The following algorithms are in this category: + + | `PSA_ALG_PURE_EDDSA` + +* Specialized signature algorithms, that use part of a standard signature algorithm within a specific protocol. It is recommended that these algorithms are only used for that purpose, with inputs as specified by the higher-level protocol. See the individual algorithm descriptions for details on their usage. + + The following algorithms are in this category: + + | `PSA_ALG_RSA_PKCS1V15_SIGN_RAW` + | `PSA_ALG_ECDSA_ANY` + +The |API| provides several functions for calculating and verifying signatures: + +* The single-part signature and verification functions, `psa_sign_message()` and `psa_verify_message()`, take a message as one of their inputs, and perform the sign or verify algorithm. + + These functions can be used on any hash-and-sign, or message signature, algorithms. See also `PSA_ALG_IS_SIGN_MESSAGE()`. + +* The single-part functions, `psa_sign_hash()` and `psa_verify_hash()`, typically take a message hash as one of their inputs, and perform the sign or verify algorithm. + + These functions can be used on any hash-and-sign signature algorithm. It is recommended that the input to these functions is a hash, computed using the corresponding hash algorithm. To determine which hash algorithm to use, the macro `PSA_ALG_GET_HASH()` can be called on the signature algorithm identifier. + + These functions can also be used on the specialized signature algorithms, with a hash or encoded-hash as input. See also `PSA_ALG_IS_SIGN_HASH()`. + +* The pair of `interruptible operations `, `psa_sign_interruptible_operation_t` and `psa_verify_interruptible_operation_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible_sign` and :secref:`interruptible_verify` for details on how to use these operations. .. _sign-algorithms: @@ -36,7 +87,7 @@ Asymmetric signature algorithms Unspecified if ``hash_alg`` is not a supported hash algorithm. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -53,7 +104,7 @@ Asymmetric signature algorithms .. summary:: The raw RSA PKCS#1 v1.5 signature algorithm, without hashing. - This algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -87,7 +138,7 @@ Asymmetric signature algorithms Unspecified if ``hash_alg`` is not a supported hash algorithm. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -124,7 +175,7 @@ Asymmetric signature algorithms Unspecified if ``hash_alg`` is not a supported hash algorithm. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -160,7 +211,7 @@ Asymmetric signature algorithms Unspecified if ``hash_alg`` is not a supported hash algorithm. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. 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. @@ -194,13 +245,15 @@ Asymmetric signature algorithms .. summary:: The randomized ECDSA signature scheme, without hashing. - This algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This algorithm is randomized: each invocation returns a different, equally valid signature. This is the same signature scheme as `PSA_ALG_ECDSA()`, but without specifying a hash algorithm, and skipping the message hashing operation. - This algorithm is only recommended to sign or verify a sequence of bytes that are an already-calculated hash. Note that the input is padded with zeros on the left or truncated on the right as required to fit the curve size. + .. warning:: + + This algorithm is only recommended to sign or verify a sequence of bytes that are a pre-computed hash. Note that the input is padded with zeros on the left or truncated on the right as required to fit the curve size. This algorithm cannot be used with the wildcard key policy :code:`PSA_ALG_ECDSA(PSA_ALG_ANY_HASH)`. It is only permitted when `PSA_ALG_ECDSA_ANY` is the key's permitted-algorithm policy. @@ -230,7 +283,7 @@ Asymmetric signature algorithms Unspecified if ``hash_alg`` is not a supported hash algorithm. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. 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. @@ -260,9 +313,9 @@ Asymmetric signature algorithms :definition: ((psa_algorithm_t) 0x06000800) .. summary:: - Edwards-curve digital signature algorithm without prehashing (PureEdDSA), using standard parameters. + Edwards-curve digital signature algorithm without pre-hashing (PureEdDSA), using standard parameters. - This algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions. + This message signature algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions, or with the interruptible asymmetric signature and verification operations. This is the PureEdDSA digital signature algorithm defined by :RFC-title:`8032`, using standard parameters. @@ -273,13 +326,18 @@ Asymmetric signature algorithms * Edwards448: the Ed448 algorithm is computed with an empty string as the context. The output signature is a 114-byte string: the concatenation of :math:`R` and :math:`S` as defined by :RFC:`8032#5.2.6`. .. note:: - Contexts are not supported in the current version of this specification because there is no suitable signature interface that can take the context as a parameter. A future version of this specification may add suitable functions and extend this algorithm to support contexts. + 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_interruptible_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 with `psa_sign_hash()` and `psa_verify_hash()`. + 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. + .. note:: + Contexts are not supported in the current version of this specification because there is no suitable signature interface that can take the context as a parameter. A future version of this specification may add suitable functions and extend this algorithm to support contexts. + .. subsection:: Compatible key types | :code:`PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY_TWISTED_EDWARDS)` @@ -289,11 +347,11 @@ Asymmetric signature algorithms :definition: ((psa_algorithm_t) 0x0600090B) .. summary:: - Edwards-curve digital signature algorithm with prehashing (HashEdDSA), using the Edwards25519 curve. + Edwards-curve digital signature algorithm with pre-hashing (HashEdDSA), using the Edwards25519 curve. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This computes the Ed25519ph algorithm as specified in :RFC-title:`8032#5.1`, and requires an Edwards25519 curve key. An empty string is used as the context. The prehash function is SHA-512. + This calculates the Ed25519ph algorithm as specified in :RFC-title:`8032#5.1`, and requires an Edwards25519 curve key. An empty string is used as the context. The pre-hash function is SHA-512. When used with `psa_sign_hash()` or `psa_verify_hash()`, the provided ``hash`` parameter is the SHA-512 message digest. @@ -320,11 +378,11 @@ Asymmetric signature algorithms :definition: ((psa_algorithm_t) 0x06000915) .. summary:: - Edwards-curve digital signature algorithm with prehashing (HashEdDSA), using the Edwards448 curve. + Edwards-curve digital signature algorithm with pre-hashing (HashEdDSA), using the Edwards448 curve. - This algorithm can be used with both the message and hash signature functions. + This hash-and-sign signature algorithm can be used with both the message and hash signature functions. - This computes the Ed448ph algorithm as specified in :RFC-title:`8032#5.2`, and requires an Edwards448 curve key. An empty string is used as the context. The prehash function is the first 64 bytes of the output from SHAKE256. + This calculates the Ed448ph algorithm as specified in :RFC-title:`8032#5.2`, and requires an Edwards448 curve key. An empty string is used as the context. The pre-hash function is the first 64 bytes of the output from SHAKE256. When used with `psa_sign_hash()` or `psa_verify_hash()`, the provided ``hash`` parameter is the truncated SHAKE256 message digest. @@ -348,8 +406,8 @@ Asymmetric signature algorithms When used with `psa_sign_hash()` or `psa_verify_hash()`, the ``hash`` parameter to the call should be used as :math:`\text{PH}(M)` in the algorithms defined in :RFC:`8032#5.2`. -Asymmetric signature functions ------------------------------- +Single-part asymmetric signature functions +------------------------------------------ .. function:: psa_sign_message @@ -390,13 +448,13 @@ Asymmetric signature functions .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: - * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``alg`` is not supported, or is not an asymmetric signature algorithm that permits signing a message. * ``key`` is not supported for use with ``alg``. * ``input_length`` is too large for the implementation. .. retval:: PSA_ERROR_INVALID_ARGUMENT The following conditions can result in this error: - * ``alg`` is not an asymmetric signature algorithm. + * ``alg`` is not an asymmetric signature algorithm that permits signing a message. * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. * ``input_length`` is too large for the algorithm and key type. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY @@ -443,13 +501,13 @@ Asymmetric signature functions .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: - * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``alg`` is not supported, or is not an asymmetric signature algorithm that permits verifying a message. * ``key`` is not supported for use with ``alg``. * ``input_length`` is too large for the implementation. .. retval:: PSA_ERROR_INVALID_ARGUMENT The following conditions can result in this error: - * ``alg`` is not an asymmetric signature algorithm. + * ``alg`` is not an asymmetric signature algorithm that permits verifying a message. * ``key`` is not a public key or an asymmetric key pair, that is compatible with ``alg``. * ``input_length`` is too large for the algorithm and key type. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY @@ -467,7 +525,7 @@ Asymmetric signature functions .. function:: psa_sign_hash .. summary:: - Sign an already-calculated hash with a private key. + Sign a pre-computed hash with a private key. .. 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 permit the usage `PSA_KEY_USAGE_SIGN_HASH`. @@ -505,12 +563,12 @@ Asymmetric signature functions .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: - * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``alg`` is not supported, or is not an asymmetric signature algorithm that permits signing a pre-computed hash. * ``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. + * ``alg`` is not an asymmetric signature algorithm that permits signing a pre-computed hash. * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. * ``hash_length`` is not valid for the algorithm and key type. * ``hash`` is not a valid input value for the algorithm and key type. @@ -524,20 +582,9 @@ Asymmetric signature functions .. retval:: PSA_ERROR_BAD_STATE The library requires initializing by a call to `psa_crypto_init()`. - With most signature algorithms that follow the hash-and-sign paradigm, the ``hash`` input to this function is the hash of the message to sign. The algorithm used to compute this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output, and the following condition is true: - - .. code-block:: xref - - hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg)) + 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))`. - The current version of this specification defines the following signature algorithms with this property: `PSA_ALG_RSA_PKCS1V15_SIGN`, `PSA_ALG_RSA_PSS`, `PSA_ALG_ECDSA`, `PSA_ALG_DETERMINISTIC_ECDSA`, `PSA_ALG_ED25519PH`, and `PSA_ALG_ED448PH`. - - Some hash-and-sign mechanisms apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. The current version of this specification defines one such signature algorithm: `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. - - .. note:: - To perform a hash-and-sign signature algorithm, the hash must be calculated before passing it to this function. This can be done by calling `psa_hash_compute()` or with a multi-part hash operation. The correct hash algorithm to use can be determined using `PSA_ALG_GET_HASH()`. - - Alternatively, to hash and sign a message in a single call, use `psa_sign_message()`. + 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`. .. function:: psa_verify_hash @@ -572,38 +619,730 @@ Asymmetric signature functions .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: + * ``alg`` is not supported, or is not an asymmetric signature algorithm that permits verifying a pre-computed hash. + * ``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 that permits verifying a pre-computed hash. + * ``key`` is not a public key or an asymmetric key pair, that is compatible with ``alg``. + * ``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_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_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + 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`. + + +.. _interruptible_sign: + +Interruptible asymmetric signature operations +--------------------------------------------- + +.. todo:: + + Decide how to calculate the signature of the zero-length message using the interruptible API. Either: + + * Implicitly, if neither `psa_sign_interruptible_hash()`, nor `psa_sign_interruptible_update()`, is called; OR + * Require that `psa_sign_interruptible_update()` is called with a zero-length input. + + In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_interruptible_complete()`. + + :issue:`Current preference for the latter` + +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_interruptible_operation_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_interruptible_operation_t`, for example, `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`. +#. Call `psa_sign_interruptible_setup()` to specify the algorithm and key. +#. Call `psa_sign_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_sign_interruptible_hash()` with a pre-computed hash of the message to sign; or + 2. Call `psa_sign_interruptible_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_interruptible_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_interruptible_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_sign_interruptible_operation_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric signature operation. + + 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_interruptible_operation_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_interruptible_operation_t operation; + + * Initialize the object to the initializer `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`, for example: + + .. code-block:: xref + + psa_sign_interruptible_operation_t operation = PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT; + + * Assign the result of the function `psa_sign_interruptible_operation_init()` to the object, for example: + + .. code-block:: xref + + psa_sign_interruptible_operation_t operation; + operation = psa_sign_interruptible_operation_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_INTERRUPTIBLE_OPERATION_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_interruptible_operation_t`. + +.. function:: psa_sign_interruptible_operation_init + + .. summary:: + Return an initial value for an interruptible asymmetric signature operation object. + + .. return:: psa_sign_interruptible_operation_t + +.. function:: psa_sign_interruptible_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. + + .. param:: psa_sign_interruptible_operation_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_interruptible_setup()` or `psa_sign_interruptible_abort()`. + + This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_sign_interruptible_setup + + .. summary:: + Begin the setup of an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_interruptible_operation_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_interruptible_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 a public key or an asymmetric key pair, that is compatible with ``alg``. + * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. + .. 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_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_interruptible_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_interruptible_complete()`. + * A call to `psa_sign_interruptible_abort()`. + + If `psa_sign_interruptible_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_sign_interruptible_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_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_interruptible_hash()` or `psa_sign_interruptible_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_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_sign_interruptible_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_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 + :issue:`I don't think is necessary in this phase of the operation?` + + 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_interruptible_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_interruptible_abort()`. + +.. function:: psa_sign_interruptible_update + + .. summary:: + Add a message fragment to an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_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 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 + :issue:`I don't think is necessary in this phase of the operation?` + + 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_interruptible_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_interruptible_update()`. + + After input of the message, the signature operation can be completed by calling `psa_sign_interruptible_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_interruptible_abort()`. + +.. function:: psa_sign_interruptible_complete + + .. summary:: + Attempt to finish the interruptible calculation of an asymmetric signature. + + .. param:: psa_sign_interruptible_operation_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_interruptible_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_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_sign_interruptible_abort + + .. summary:: + Abort an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_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()`. - With most signature algorithms that follow the hash-and-sign paradigm, the ``hash`` input to this function is the hash of the message to verify. The algorithm used to compute this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output, and the following condition is true: + 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_interruptible_setup()` again. - .. code-block:: xref + This function can be called at any time after the operation object has been initialized as described in `psa_sign_interruptible_operation_t`. + + In particular, it is valid to call `psa_sign_interruptible_abort()` twice, or to call `psa_sign_interruptible_abort()` on an operation that has not been set up. + + +.. _interruptible_verify: + +Interruptible asymmetric verification operations +------------------------------------------------ + +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_interruptible_operation_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_interruptible_operation_t`, for example, `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`. +#. Call `psa_verify_interruptible_setup()` to specify the algorithm, key, and the signature to verify. +#. Call `psa_verify_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_verify_interruptible_hash()` with a pre-computed hash of the message to verify; or + 2. Call `psa_verify_interruptible_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_interruptible_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_interruptible_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_verify_interruptible_operation_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric verification operation. + + 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_interruptible_operation_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_interruptible_operation_t operation; + + * Initialize the object to the initializer `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`, for example: + + .. code-block:: xref + + psa_verify_interruptible_operation_t operation = PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT; + + * Assign the result of the function `psa_verify_interruptible_operation_init()` to the object, for example: + + .. code-block:: xref + + psa_verify_interruptible_operation_t operation; + operation = psa_verify_interruptible_operation_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_INTERRUPTIBLE_OPERATION_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_interruptible_operation_t`. + +.. function:: psa_verify_interruptible_operation_init + + .. summary:: + Return an initial value for an interruptible asymmetric verification operation object. + + .. return:: psa_verify_interruptible_operation_t + +.. function:: psa_verify_interruptible_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. + + .. param:: psa_verify_interruptible_operation_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_interruptible_setup()` or `psa_verify_interruptible_abort()`. + + This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_verify_interruptible_setup + + .. summary:: + Begin the setup of an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_interruptible_operation_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_interruptible_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. + + .. todo:: Decision required on handling invalid signatures. + + If the signature is clearly invalid (for the algorithm/key combination), an implementation could: + + 1. report ``PSA_ERROR_INVALID_ARGUMENT`` now, + 2. ``PSA_ERROR_INVALID_SIGNATURE`` now, or + 3. defer ``PSA_ERROR_INVALID_SIGNATURE`` until the operation is completed. + + Although (3) might appear to reduce information leakage (early return), it will still short cut all the calculations (as would happen in the single-part function). + + My preference is permitting an implementation to do (1)-or-(3), but we could instead permit (2)-or-(3) + + .. 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_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_interruptible_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_interruptible_complete()`. + * A call to `psa_verify_interruptible_abort()`. + + If `psa_verify_interruptible_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_verify_interruptible_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to use. The operation must be in the process of being set up. - hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg)) + .. 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_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_interruptible_hash()` or `psa_verify_interruptible_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_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_verify_interruptible_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_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`. - The current version of this specification defines the following signature algorithms with this property: `PSA_ALG_RSA_PKCS1V15_SIGN`, `PSA_ALG_RSA_PSS`, `PSA_ALG_ECDSA`, `PSA_ALG_DETERMINISTIC_ECDSA`, `PSA_ALG_ED25519PH`, and `PSA_ALG_ED448PH`. + After input of the hash, the verification operation can be completed by calling `psa_verify_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - Some hash-and-sign mechanisms apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. The current version of this specification defines one such signature algorithm: `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + + +.. function:: psa_verify_interruptible_update + + .. summary:: + Add a message fragment to an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_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 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_interruptible_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_interruptible_update()`. + + After input of the message, the verification operation can be completed by calling `psa_verify_interruptible_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_interruptible_abort()`. + +.. function:: psa_verify_interruptible_complete + + .. summary:: + Attempt to finish the interruptible verification of an asymmetric signature. + + .. param:: psa_verify_interruptible_operation_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_interruptible_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:: - To perform a hash-and-sign verification algorithm, the hash must be calculated before passing it to this function. This can be done by calling `psa_hash_compute()` or with a multi-part hash operation. The correct hash algorithm to use can be determined using `PSA_ALG_GET_HASH()`. + 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_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_verify_interruptible_abort + + .. summary:: + Abort an interruptible asymmetric verification operation. - Alternatively, to hash and verify a message in a single call, use `psa_verify_message()`. + .. param:: psa_verify_interruptible_operation_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_interruptible_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_verify_interruptible_operation_t`. + + In particular, it is valid to call `psa_verify_interruptible_abort()` twice, or to call `psa_verify_interruptible_abort()` on an operation that has not been set up. Support macros -------------- @@ -618,7 +1357,9 @@ Support macros An algorithm identifier: a value of type `psa_algorithm_t`. .. return:: - ``1`` if ``alg`` is a signature algorithm that can be used to sign a message. ``0`` if ``alg`` is a signature algorithm that can only be used to sign an already-calculated hash. ``0`` if ``alg`` is not a signature algorithm. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. + ``1`` if ``alg`` is a signature algorithm that can be used to sign a message. ``0`` if ``alg`` is a signature algorithm that can only be used to sign a pre-computed hash. ``0`` if ``alg`` is not a signature algorithm. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. + + This macro evaluates to ``1`` for hash-and-sign and message-signature algorithms. .. macro:: PSA_ALG_IS_SIGN_HASH :definition: /* specification-defined value */ @@ -632,8 +1373,7 @@ Support macros .. return:: ``1`` if ``alg`` is a signature algorithm that can be used to sign a hash. ``0`` if ``alg`` is a signature algorithm that can only be used to sign a message. ``0`` if ``alg`` is not a signature algorithm. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. - - This includes all algorithms such that `PSA_ALG_IS_HASH_AND_SIGN()` is true, as well as signature algorithms for which the input to `psa_sign_hash()` or `psa_verify_hash()` is not directly a hash, such as `PSA_ALG_IS_RSA_PKCS1V15_SIGN`. + This macro evaluates to ``1`` for hash-and-sign and specialized signature algorithms. .. macro:: PSA_ALG_IS_RSA_PKCS1V15_SIGN :definition: /* specification-defined value */ @@ -773,22 +1513,18 @@ Support macros .. return:: ``1`` if ``alg`` is a hash-and-sign algorithm that signs exactly the hash value, ``0`` otherwise. This macro can return either ``0`` or ``1`` if ``alg`` is not a supported algorithm identifier. - A wildcard signature algorithm policy, using `PSA_ALG_ANY_HASH`, returns the same value as the signature algorithm parameterised with a valid hash algorithm. + A wildcard signature algorithm policy, using `PSA_ALG_ANY_HASH`, returns the same value as the signature algorithm parameterized with a valid hash algorithm. This macro identifies algorithms that can be used with `psa_sign_hash()` that use the exact message hash value as an input the signature operation. For example, if :code:`PSA_ALG_IS_HASH_AND_SIGN(alg)` is true, the following call sequence is equivalent to :code:`psa_sign_message(key, alg, msg, msg_len, ...)`: .. code-block:: xref - psa_hash_operation_t op = {0}; uint8_t hash[PSA_HASH_MAX_SIZE]; size_t hash_len; - psa_hash_setup(&op, PSA_ALG_GET_HASH(alg)); - psa_hash_update(&op, msg, msg_len); - psa_hash_finish(&op, hash, sizeof(hash), &hash_len); + psa_hash_compute(PSA_ALG_GET_HASH(alg), msg, msg_len, + hash, sizeof(hash), &hash_len); psa_sign_hash(key, alg, hash, hash_len, ...); - This excludes hash-and-sign algorithms that require a encoded or modified hash for the signature step in the algorithm, such as `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. For such algorithms, `PSA_ALG_IS_SIGN_HASH()` is true but `PSA_ALG_IS_HASH_AND_SIGN()` is false. - .. macro:: PSA_ALG_ANY_HASH :definition: ((psa_algorithm_t)0x020000ff) diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index 58167286..6e7d1fc3 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -20,6 +20,7 @@ Changes to the API ~~~~~~~~~~~~~~~~~~ * Added `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` to evaluate the export buffer size for any asymmetric key pair or public key. +* Added interruptible operations for asymmetric sign and verify. See :secref:`sign` and :secref:`interruptible-operations`. Other changes ~~~~~~~~~~~~~ diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf index 81472000390439f607d973bdad5919eeb9a4b5c6..c5c9b415051e10e2d3a258069072815c8ca084d6 100644 GIT binary patch delta 16027 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 VFBfwQlvjIPKon|faYYG~{|ClRHJ|_h delta 18774 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 QW`D#Hni3tIl4//error//" ##darkred [*] --> inactive: **Initialize** @@ -23,22 +23,22 @@ note as N1 Operation object starts as uninitialised memory end note -inactive --> starting: **Setup** -starting --> starting: **Setup-complete**\n//incomplete// -starting --> active: **Setup-complete** -active --> active: **Update** -active --> finishing: **Finish**\n//incomplete// -active --> inactive: **Finish** -finishing --> finishing: **Finish**\n//incomplete// -finishing --> inactive: **Finish** +inactive --> setup: **Setup** +setup --> setup: **Setup-complete**\n//incomplete// +setup --> input: **Setup-complete** +input --> input: **Update** +input --> completing: **Complete**\n//incomplete// +input --> inactive: **Complete** +completing --> completing: **Complete**\n//incomplete// +completing --> inactive: **Complete** error -[#darkred,dashed]-> inactive: **Abort** inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// -starting -[#darkred,dashed]-> error: **Setup-complete**\n//fails// -active -[#darkred,dashed]-> error: **Update**\n//fails// -active -[#darkred,dashed]-> error: **Finish**\n//fails// -finishing -[#darkred,dashed]-> error: **Finish**\n//fails// -starting -[#blue,dotted]-> inactive: **Abort** -active -[#blue,dotted]-> inactive: **Abort** -finishing -[#blue,dotted]-> inactive: **Abort** +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]-> inactive: **Abort** +input -[#blue,dotted]-> inactive: **Abort** +completing -[#blue,dotted]-> inactive: **Abort** @enduml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg index 575421bc..ce2ed7c2 100644 --- a/doc/crypto/figure/interruptible_operation.svg +++ b/doc/crypto/figure/interruptible_operation.svg @@ -1,19 +1,19 @@ -inactivestartingactivefinishingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateFinishincompleteFinishAbortFinishincompleteFinishAbortAbortSetupfailsSetup-completefailsUpdatefailsFinishfailsFinishfails———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 memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateCompleteincompleteCompleteAbortCompleteincompleteCompleteAbortAbortSetupfailsSetup-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 b50f1573..c7e33c0e 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -288,7 +288,7 @@ There are three components in an interruptible operation: * 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 *max ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *max ops* value can also be queried. + 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 object is set up for a new operation, which permits the value to be queried after an operation has finished. @@ -319,25 +319,25 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. - If the setup computation is interrupted, a the operation remains in *starting* state. If setup completes successfully, the operation enters an *active* state. On failure, the operation object will enter an *error* state. + If the setup computation is interrupted, a the operation remains in *setup* state. If setup completes successfully, the operation enters an *input* state. On failure, the operation object will enter an *error* state. An application needs to repeat this step until the setup completes with success or an error status. -#. **Update:** Update an *active* interruptible operation object. The update function can provide additional parameters, supply data for processing or generate outputs. +#. **Update:** Update an interruptible operation object in *input* state. The update function can provide additional parameters, supply data for processing or generate outputs. - On success, the operation object remains *active*. On failure, the operation object will enter an *error* state. + On success, the operation object remains in *input* state. On failure, the operation object will enter an *error* state. -#. **Finish:** To end an interruptible operation, call the applicable finishing function. This will take any final inputs, produce any final outputs, and then release any resources associated with the operation. +#. **Complete:** To end an interruptible operation, call the applicable completion function. This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. - If the finishing computation is interrupted, a the operation is left in *finishing* state. If finishing completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. + If the finishing computation is interrupted, a the operation is left in *completing* state. If the operation completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. - An application needs to repeat this step until the finishing function completes with success or an error status. + An application needs to repeat this step until the completion function completes with success or an error status. -#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_interruptible_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. - 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_abort()`` function to release the operation resources and return the object to the *inactive* state. + Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_interruptible_abort()`` function to release the operation resources and return the object to the *inactive* state. - ``psa_xxx_abort()`` can be called on an *inactive* operation, and this has no effect. + ``psa_xxx_interruptible_abort()`` can be called on an *inactive* 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 applicable setup functions again. @@ -352,9 +352,9 @@ It is safe to move an interruptible operation object to a different memory locat * 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 *starting*, *active*, and *finishing* states. Documentation for the specific operation describes the setup, update and finishing functions, and any requirements about their usage and ordering. +Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. -See :secref:`interruptible_example` for an example of using an interruptible operation. +See :secref:`interruptible_sign` for an example of using an interruptible operation. Randomness and key generation ----------------------------- From ec1e4cd76d8d224af8f32cc2bc89cdffbb0029d7 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 9 Nov 2023 09:47:54 +0000 Subject: [PATCH 05/12] Xref interruptible operations from key usage flags --- doc/crypto/api/keys/policy.rst | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/doc/crypto/api/keys/policy.rst b/doc/crypto/api/keys/policy.rst index fdbbe250..91f90a8a 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -181,6 +181,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_compute()` * `psa_mac_sign_setup()` * `psa_sign_message()` + * `psa_sign_interruptible_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -195,6 +196,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_verify()` * `psa_mac_verify_setup()` * `psa_verify_message()` + * `psa_verify_interruptible_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -204,7 +206,10 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. .. summary:: Permission to sign a message hash with the key. - This flag is required to use the key to sign a message hash in an asymmetric signature operation. The flag must be present on keys used when calling `psa_sign_hash()`. + This flag is required to use the key to sign a pre-computed message hash in an asymmetric signature operation. The flag must be present on keys used with the following APIs: + + * `psa_sign_hash()` + * `psa_sign_interruptible_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. @@ -216,7 +221,10 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. .. summary:: Permission to verify a message hash with the key. - This flag is required to use the key to verify a message hash in an asymmetric signature verification operation. The flag must be present on keys used when calling `psa_verify_hash()`. + This flag is required to use the key to verify a pre-computed message hash in an asymmetric signature verification operation. The flag must be present on keys used with the following APIs: + + * `psa_verify_hash()` + * `psa_verify_interruptible_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. From 644356999d8678fae8d857757ed6ac5ff492d136 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 17:13:22 +0100 Subject: [PATCH 06/12] Rework the addition of Interruptible operations to the functionality chapter --- doc/crypto/overview/functionality.rst | 136 ++++++++++++++------------ 1 file changed, 73 insertions(+), 63 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index c7e33c0e..58b83b43 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -112,15 +112,24 @@ Cryptographic operations For each type of cryptographic operation, the API can include: -* One or more *single-part* functions, which perform a whole operation in a single function call. For example, compute, verify, encrypt or decrypt. -* A *multi-part operation* --- which is a series of functions that work with a stored operation state. +* One or more *single-part* functions, which perform a whole operation in a single function call. For example, compute, verify, encrypt or decrypt. See :secref:`single-part-functions`. + +* A *multi-part operation* --- which 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`. + +* An *interruptible operation* --- which is also a set of functions that work with a stored operation state. However, these operations are for computationally expensive algorithms (for example, digital signatures), and enable the application to limit the computation performed in a single function call. See :secref:`interruptible-operations`. + +.. _single-part-functions: Single-part Functions ~~~~~~~~~~~~~~~~~~~~~ Single-part functions are APIs that implement the cryptographic operation in a single function call. This is the easiest API to use when all of the inputs and outputs fit into the application memory. -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 `. +Single-part functions do not meet the needs of all use cases: + +* Some use cases involve messages that are too large to be assembled in memory, or require non-default configuration of the algorithm. These use cases require the use of a `multi-part operation `. + +* Some use cases require that the time spent in a single function call is bounded. For some algorithms, this result can be achieved using a multi-part operation. For algorithms that involve computationally expensive steps, the use case requires the use of an `interruptible operation `. .. _multi-part-operations: @@ -209,66 +218,6 @@ 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. -Symmetric cryptography -~~~~~~~~~~~~~~~~~~~~~~ - -This specification defines interfaces for the following types of symmetric -cryptographic operation: - -* Message digests, commonly known as hash functions. See :secref:`hashes`. -* Message authentication codes (MAC). See :secref:`macs`. -* Symmetric ciphers. See :secref:`ciphers`. -* Authenticated encryption with associated data (AEAD). See :secref:`aead`. -* Key derivation. See :secref:`kdf`. - -Key derivation only provides multi-part operation, to support the flexibility required by these type of algorithms. - -.. _symmetric-crypto-example: - -Example of the symmetric cryptography API -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Here is an example of a use case where a master key is used to generate both a message encryption key and an IV for the encryption, and the derived key and IV are then used to encrypt a message. - -1. Derive the message encryption material from the master key. - - a. Initialize a `psa_key_derivation_operation_t` object to zero or to `PSA_KEY_DERIVATION_OPERATION_INIT`. - #. Call `psa_key_derivation_setup()` with `PSA_ALG_HKDF` as the algorithm. - #. Call `psa_key_derivation_input_key()` with the step `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. - #. Call `psa_key_derivation_input_bytes()` with the step `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely identifies the message. - #. 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. - -#. Encrypt the message with the derived material. - - a. Initialize a `psa_cipher_operation_t` object to zero or to `PSA_CIPHER_OPERATION_INIT`. - #. Call `psa_cipher_encrypt_setup()` with the derived message encryption key. - #. Call `psa_cipher_set_iv()` using the derived IV retrieved above. - #. Call `psa_cipher_update()` one or more times to encrypt the message. - #. Call `psa_cipher_finish()` at the end of the message. - -#. Call `psa_destroy_key()` to clear the generated key. - -Asymmetric cryptography -~~~~~~~~~~~~~~~~~~~~~~~ - -This specification defines interfaces for the following types of asymmetric cryptographic operation: - -* Asymmetric encryption (also known as public key encryption). See :secref:`pke`. -* Asymmetric signature. See :secref:`sign`. -* Two-way key agreement (also known as key establishment). See :secref:`key-agreement`. -* Password-authenticated key exchange (PAKE). See :secref:`pake`. - -For asymmetric encryption, the API provides *single-part* functions. - -For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). - -For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. - -For PAKE, the API provides a *multi-part* operation. - .. _interruptible-operations: Interruptible operations @@ -356,6 +305,67 @@ Each type of interruptible operation can have multiple *setup*, *input*, and *co See :secref:`interruptible_sign` for an example of using an interruptible operation. +Symmetric cryptography +~~~~~~~~~~~~~~~~~~~~~~ + +This specification defines interfaces for the following types of symmetric +cryptographic operation: + +* Message digests, commonly known as hash functions. See :secref:`hashes`. +* Message authentication codes (MAC). See :secref:`macs`. +* Symmetric ciphers. See :secref:`ciphers`. +* Authenticated encryption with associated data (AEAD). See :secref:`aead`. +* Key derivation. See :secref:`kdf`. + +Key derivation only provides multi-part operation, to support the flexibility required by these type of algorithms. + +.. _symmetric-crypto-example: + +Example of the symmetric cryptography API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here is an example of a use case where a master key is used to generate both a message encryption key and an IV for the encryption, and the derived key and IV are then used to encrypt a message. + +1. Derive the message encryption material from the master key. + + a. Initialize a `psa_key_derivation_operation_t` object to zero or to `PSA_KEY_DERIVATION_OPERATION_INIT`. + #. Call `psa_key_derivation_setup()` with `PSA_ALG_HKDF` as the algorithm. + #. Call `psa_key_derivation_input_key()` with the step `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. + #. Call `psa_key_derivation_input_bytes()` with the step `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely identifies the message. + #. 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. + +#. Encrypt the message with the derived material. + + a. Initialize a `psa_cipher_operation_t` object to zero or to `PSA_CIPHER_OPERATION_INIT`. + #. Call `psa_cipher_encrypt_setup()` with the derived message encryption key. + #. Call `psa_cipher_set_iv()` using the derived IV retrieved above. + #. Call `psa_cipher_update()` one or more times to encrypt the message. + #. Call `psa_cipher_finish()` at the end of the message. + +#. Call `psa_destroy_key()` to clear the generated key. + +Asymmetric cryptography +~~~~~~~~~~~~~~~~~~~~~~~ + +This specification defines interfaces for the following types of asymmetric cryptographic operation: + +* Asymmetric encryption (also known as public key encryption). See :secref:`pke`. +* Asymmetric signature. See :secref:`sign`. +* Two-way key agreement (also known as key establishment). See :secref:`key-agreement`. +* Password-authenticated key exchange (PAKE). See :secref:`pake`. + +For asymmetric encryption, the API provides *single-part* functions. + +For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). + +For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. + +For PAKE, the API provides a *multi-part* operation. + + Randomness and key generation ----------------------------- From 49bf8244d5b04865e4b1242d82d0ab57da8ce46e Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 22:41:14 +0100 Subject: [PATCH 07/12] fixup: Incorrect section level --- 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 58b83b43..4606f566 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -221,7 +221,7 @@ Each type of multi-part operation can have multiple *active* states. Documentati .. _interruptible-operations: Interruptible operations -^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~ Interruptible operations are APIs which split a single computationally-expensive operation into a sequence of separate function calls, each of which has a bounded execution time. Interruptible operations are useful in application contexts where responsiveness is critical, and techniques such a multi-threading are not available. This is achieved by limiting the amount of computational progress that is made for each function call. From 6af56d43e2fd17db3eb85d27c21462f72f228926 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 22:51:18 +0100 Subject: [PATCH 08/12] fixup: Fix residual uses of 'starting' state --- doc/crypto/overview/functionality.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 4606f566..fcc78ed2 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -264,9 +264,9 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. - On success, an operation object enters a *starting* state. On failure, the operation object will remain *inactive*. + On success, an operation object enters a *setup* state. On failure, the operation object will remain *inactive*. -#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is in *setup* state. If the setup computation is interrupted, a 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. From a6aea819b1bcaa3ec8c652d4afef74aea1ba307c Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 14 May 2024 14:00:11 +0100 Subject: [PATCH 09/12] Resolve some issues and todos * Align permitted errors with the single-part functions * Introduce the max_ops APIs --- doc/crypto/api/library/library.rst | 4 ++-- doc/crypto/api/ops/signature.rst | 19 ++++--------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index b021b392..00f548a9 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -78,9 +78,9 @@ Library initialization Interruptible operation limit ----------------------------- -.. todo:: Provide simple intro to these support functions +Using an interruptible operation, an application can perform an expensive cryptographic computation while limiting the execution time of each function call. The execution limit is controlled via the *maximum ops* value. -See :secref:`interruptible-operations` +See :secref:`interruptible-operations`. .. function:: psa_interruptible_set_max_ops diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 6574af35..8a7a89f9 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -865,7 +865,6 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - :issue:`I don't think is necessary in this phase of the operation?` The application must complete the setup of the operation before calling this function. @@ -916,7 +915,6 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - :issue:`I don't think is necessary in this phase of the operation?` The application must complete the setup of the operation before calling this function. @@ -1122,19 +1120,8 @@ An interruptible asymmetric verification operation is used as follows: * ``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. - - .. todo:: Decision required on handling invalid signatures. - - If the signature is clearly invalid (for the algorithm/key combination), an implementation could: - - 1. report ``PSA_ERROR_INVALID_ARGUMENT`` now, - 2. ``PSA_ERROR_INVALID_SIGNATURE`` now, or - 3. defer ``PSA_ERROR_INVALID_SIGNATURE`` until the operation is completed. - - Although (3) might appear to reduce information leakage (early return), it will still short cut all the calculations (as would happen in the single-part function). - - My preference is permitting an implementation to do (1)-or-(3), but we could instead permit (2)-or-(3) - + .. 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 @@ -1170,6 +1157,8 @@ An interruptible asymmetric verification operation is used as follows: * 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 From 8f65bbac72f6ef117b17e9b621bfbc54d613b853 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Jun 2024 14:03:40 +0100 Subject: [PATCH 10/12] Updated naming pattern for interruptible APIs: * Operation objects are now psa_xxx_iop_t, initialization PSA_XXX_IOP_INIT etc. * Operation and support functions use 'iop' as an infix instead of 'interruptible' --- doc/crypto/api.db/psa/crypto.h | 82 +++++---- doc/crypto/api/keys/policy.rst | 8 +- doc/crypto/api/library/library.rst | 18 +- doc/crypto/api/ops/signature.rst | 242 +++++++++++++------------- doc/crypto/overview/functionality.rst | 20 +-- 5 files changed, 184 insertions(+), 186 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 3d089866..446dfc1b 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -24,8 +24,8 @@ typedef uint32_t psa_pake_primitive_t; typedef uint8_t psa_pake_primitive_type_t; typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; -typedef /* implementation-defined type */ psa_sign_interruptible_operation_t; -typedef /* implementation-defined type */ psa_verify_interruptible_operation_t; +typedef /* implementation-defined type */ psa_sign_iop_t; +typedef /* implementation-defined type */ psa_verify_iop_t; #define PSA_AEAD_DECRYPT_OUTPUT_MAX_SIZE(ciphertext_length) \ /* implementation-defined value */ #define PSA_AEAD_DECRYPT_OUTPUT_SIZE(key_type, alg, ciphertext_length) \ @@ -239,7 +239,7 @@ typedef /* implementation-defined type */ psa_verify_interruptible_operation_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_INTERRUPTIBLE_MAX_OPS_UNLIMITED UINT32_MAX +#define PSA_IOP_MAX_OPS_UNLIMITED UINT32_MAX #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 */ @@ -370,14 +370,12 @@ typedef /* implementation-defined type */ psa_verify_interruptible_operation_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_INTERRUPTIBLE_OPERATION_INIT \ - /* implementation-defined value */ +#define PSA_SIGN_IOP_INIT /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ #define PSA_TLS12_ECJPAKE_TO_PMS_OUTPUT_SIZE 32 #define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE /* implementation-defined value */ -#define PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT \ - /* implementation-defined value */ +#define PSA_VERIFY_IOP_INIT /* implementation-defined value */ psa_status_t psa_aead_abort(psa_aead_operation_t * operation); psa_status_t psa_aead_decrypt(psa_key_id_t key, psa_algorithm_t alg, @@ -560,8 +558,8 @@ 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_interruptible_get_max_ops(void); -void psa_interruptible_set_max_ops(uint32_t max_ops); +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, @@ -700,23 +698,23 @@ psa_status_t psa_sign_hash(psa_key_id_t key, uint8_t * signature, size_t signature_size, size_t * signature_length); -psa_status_t psa_sign_interruptible_abort(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_complete(psa_sign_interruptible_operation_t * operation, - uint8_t * signature, - size_t signature_size, - size_t * signature_length); -uint32_t psa_sign_interruptible_get_num_ops(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_hash(psa_sign_interruptible_operation_t * operation, - const uint8_t * hash, - size_t hash_length); -psa_sign_interruptible_operation_t psa_sign_interruptible_operation_init(void); -psa_status_t psa_sign_interruptible_setup(psa_sign_interruptible_operation_t * operation, - psa_key_id_t key, - psa_algorithm_t alg); -psa_status_t psa_sign_interruptible_setup_complete(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_update(psa_sign_interruptible_operation_t * operation, - const uint8_t * input, - size_t input_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, @@ -730,22 +728,22 @@ psa_status_t psa_verify_hash(psa_key_id_t key, size_t hash_length, const uint8_t * signature, size_t signature_length); -psa_status_t psa_verify_interruptible_abort(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_complete(psa_verify_interruptible_operation_t * operation); -uint32_t psa_verify_interruptible_get_num_ops(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_hash(psa_verify_interruptible_operation_t * operation, - const uint8_t * hash, - size_t hash_length); -psa_verify_interruptible_operation_t psa_verify_interruptible_operation_init(void); -psa_status_t psa_verify_interruptible_setup(psa_verify_interruptible_operation_t * operation, - psa_key_id_t key, - psa_algorithm_t alg, - const uint8_t * signature, - size_t signature_length); -psa_status_t psa_verify_interruptible_setup_complete(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_update(psa_verify_interruptible_operation_t * operation, - const uint8_t * input, - size_t input_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/policy.rst b/doc/crypto/api/keys/policy.rst index 91f90a8a..48ab382c 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -181,7 +181,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_compute()` * `psa_mac_sign_setup()` * `psa_sign_message()` - * `psa_sign_interruptible_setup()`, when signing a message. + * `psa_sign_iop_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -196,7 +196,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_verify()` * `psa_mac_verify_setup()` * `psa_verify_message()` - * `psa_verify_interruptible_setup()`, when verifying the signature of a message. + * `psa_verify_iop_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -209,7 +209,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to sign a pre-computed message hash in an asymmetric signature operation. The flag must be present on keys used with the following APIs: * `psa_sign_hash()` - * `psa_sign_interruptible_setup()` when signing a pre-computed hash. + * `psa_sign_iop_setup()` when signing a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_SIGN_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_SIGN_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_SIGN_MESSAGE`, and the flag `PSA_KEY_USAGE_SIGN_MESSAGE` will also be present when the application queries the usage flags of the key. @@ -224,7 +224,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to verify a pre-computed message hash in an asymmetric signature verification operation. The flag must be present on keys used with the following APIs: * `psa_verify_hash()` - * `psa_verify_interruptible_setup()`, when verifying the signature of a pre-computed hash. + * `psa_verify_iop_setup()`, when verifying the signature of a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_VERIFY_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_VERIFY_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_VERIFY_MESSAGE`, and the flag `PSA_KEY_USAGE_VERIFY_MESSAGE` will also be present when the application queries the usage flags of the key. diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index 00f548a9..c01b81e8 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -82,23 +82,23 @@ Using an interruptible operation, an application can perform an expensive crypto See :secref:`interruptible-operations`. -.. function:: psa_interruptible_set_max_ops +.. 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. .. param:: uint32_t max_ops - The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. + The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_IOP_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. .. return:: void Interruptible functions use this value to limit the computation that is done in any single call to the function. If this limit is reached, the function will return :code:`PSA_OPERATION_INCOMPLETE`, and the caller must repeat the function call until a different status code is returned, or abort the operation. - After initialization of the implementation, the maximum *ops* defaults to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_interruptible_set_max_ops()` to set a different limit. + After initialization of the implementation, the maximum *ops* defaults to `PSA_IOP_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_iop_set_max_ops()` to set a different limit. .. note:: - The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_interruptible_get_num_ops()` and `psa_verify_interruptible_get_num_ops()` are provided to help with tuning this value. + The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_iop_get_num_ops()` and `psa_verify_iop_get_num_ops()` are provided to help with tuning this value. .. admonition:: Implementation note @@ -107,7 +107,7 @@ See :secref:`interruptible-operations`. .. warning:: With implementations that interpret this number as a hard limit, setting this number too small can result in an infinite loop, whereby each call results in immediate return with no computation done. -.. function:: psa_interruptible_get_max_ops +.. 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. @@ -115,13 +115,13 @@ See :secref:`interruptible-operations`. .. return:: uint32_t Maximum number of *ops* allowed to be executed by an interruptible function in a single call. - This returns the value last set in a call to `psa_interruptible_set_max_ops()`. + This returns the value last set in a call to `psa_iop_set_max_ops()`. -.. macro:: PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED +.. macro:: PSA_IOP_MAX_OPS_UNLIMITED :definition: UINT32_MAX .. summary:: - Maximum value for use with `psa_interruptible_set_max_ops()`. + Maximum value for use with `psa_iop_set_max_ops()`. - Using this value in a call to `psa_interruptible_set_max_ops()` will cause interruptible functions to complete their calculation before returning. + Using this value in a call to `psa_iop_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 8a7a89f9..51631347 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -66,7 +66,7 @@ The |API| provides several functions for calculating and verifying signatures: These functions can also be used on the specialized signature algorithms, with a hash or encoded-hash as input. See also `PSA_ALG_IS_SIGN_HASH()`. -* The pair of `interruptible operations `, `psa_sign_interruptible_operation_t` and `psa_verify_interruptible_operation_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible_sign` and :secref:`interruptible_verify` for details on how to use these operations. +* The pair of `interruptible operations `, `psa_sign_iop_t` and `psa_verify_iop_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible-sign` and :secref:`interruptible-verify` for details on how to use these operations. .. _sign-algorithms: @@ -326,7 +326,7 @@ Asymmetric signature algorithms * Edwards448: the Ed448 algorithm is computed with an empty string as the context. The output signature is a 114-byte string: the concatenation of :math:`R` and :math:`S` as defined by :RFC:`8032#5.2.6`. .. note:: - When using an interruptible asymmetric signature operation with this algorithm, it is not possible to fragment the message data when calculating the signature. The message must be passed in a single call to `psa_sign_interruptible_update()`. + 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. @@ -642,7 +642,7 @@ Single-part asymmetric signature functions Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. -.. _interruptible_sign: +.. _interruptible-sign: Interruptible asymmetric signature operations --------------------------------------------- @@ -651,10 +651,10 @@ Interruptible asymmetric signature operations Decide how to calculate the signature of the zero-length message using the interruptible API. Either: - * Implicitly, if neither `psa_sign_interruptible_hash()`, nor `psa_sign_interruptible_update()`, is called; OR - * Require that `psa_sign_interruptible_update()` is called with a zero-length input. + * Implicitly, if neither `psa_sign_iop_hash()`, nor `psa_sign_iop_update()`, is called; OR + * Require that `psa_sign_iop_update()` is called with a zero-length input. - In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_interruptible_complete()`. + In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_iop_complete()`. :issue:`Current preference for the latter` @@ -662,19 +662,19 @@ The interruptible asymmetric signature operation calculates the signature of a m An interruptible asymmetric signature operation is used as follows: -1. Allocate an interruptible asymmetric signature operation object, of type `psa_sign_interruptible_operation_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_interruptible_operation_t`, for example, `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`. -#. Call `psa_sign_interruptible_setup()` to specify the algorithm and key. -#. Call `psa_sign_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +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_interruptible_hash()` with a pre-computed hash of the message to sign; or - 2. Call `psa_sign_interruptible_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_interruptible_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_interruptible_abort()`. + 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_interruptible_operation_t +.. typedef:: /* implementation-defined type */ psa_sign_iop_t .. summary:: The type of the state data structure for an interruptible asymmetric signature operation. @@ -685,67 +685,67 @@ An interruptible asymmetric signature operation is used as follows: .. code-block:: xref - psa_sign_interruptible_operation_t operation; + 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_interruptible_operation_t operation; + static psa_sign_iop_t operation; - * Initialize the object to the initializer `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`, for example: + * Initialize the object to the initializer `PSA_SIGN_IOP_INIT`, for example: .. code-block:: xref - psa_sign_interruptible_operation_t operation = PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT; + psa_sign_iop_t operation = PSA_SIGN_IOP_INIT; - * Assign the result of the function `psa_sign_interruptible_operation_init()` to the object, for example: + * Assign the result of the function `psa_sign_iop_init()` to the object, for example: .. code-block:: xref - psa_sign_interruptible_operation_t operation; - operation = psa_sign_interruptible_operation_init(); + 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_INTERRUPTIBLE_OPERATION_INIT +.. 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_interruptible_operation_t`. + This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_iop_t`. -.. function:: psa_sign_interruptible_operation_init +.. function:: psa_sign_iop_init .. summary:: Return an initial value for an interruptible asymmetric signature operation object. - .. return:: psa_sign_interruptible_operation_t + .. return:: psa_sign_iop_t -.. function:: psa_sign_interruptible_get_num_ops +.. function:: psa_sign_iop_get_num_ops .. summary:: Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. - .. param:: psa_sign_interruptible_operation_t * operation + .. 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_interruptible_setup()` or `psa_sign_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_setup +.. function:: psa_sign_iop_setup .. summary:: Begin the setup of an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation - The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_interruptible_operation_t` and not yet in use. + .. 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 @@ -754,7 +754,7 @@ An interruptible asymmetric signature operation is used as follows: .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The operation setup must now be completed by calling `psa_sign_interruptible_setup_complete()`. + 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 @@ -780,21 +780,21 @@ 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 asymmetric verification operation, see :secref:`interruptible-verify`. - After a successful call to `psa_sign_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_interruptible_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()`, 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_interruptible_complete()`. - * A call to `psa_sign_interruptible_abort()`. + * A successful call to `psa_sign_iop_complete()`. + * A call to `psa_sign_iop_abort()`. - If `psa_sign_interruptible_setup()` returns an error, the operation object is unchanged. + If `psa_sign_iop_setup()` returns an error, the operation object is unchanged. -.. function:: psa_sign_interruptible_setup_complete +.. function:: psa_sign_iop_setup_complete .. summary:: Finish setting up an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. 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 @@ -819,18 +819,18 @@ 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_interruptible_hash()` or `psa_sign_interruptible_update()`. + 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_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_hash +.. function:: psa_sign_iop_hash .. summary:: Input a pre-computed hash to an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. 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. @@ -872,16 +872,16 @@ An interruptible asymmetric signature operation is used as follows: 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_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + 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_interruptible_abort()`. + 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_interruptible_update +.. function:: psa_sign_iop_update .. summary:: Add a message fragment to an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. 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. @@ -918,18 +918,18 @@ An interruptible asymmetric signature operation is used as follows: 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_interruptible_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_interruptible_update()`. + 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_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + 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_interruptible_abort()`. + 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_interruptible_complete +.. function:: psa_sign_iop_complete .. summary:: Attempt to finish the interruptible calculation of an asymmetric signature. - .. param:: psa_sign_interruptible_operation_t * operation + .. 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. @@ -950,7 +950,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_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * 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. @@ -968,16 +968,16 @@ 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 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_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_abort +.. function:: psa_sign_iop_abort .. summary:: Abort an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible signature operation to abort. .. return:: psa_status_t @@ -989,14 +989,14 @@ An interruptible asymmetric 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_interruptible_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()` again. - This function can be called at any time after the operation object has been initialized as described in `psa_sign_interruptible_operation_t`. + 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_interruptible_abort()` twice, or to call `psa_sign_interruptible_abort()` on an operation that has not been set up. + 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-verify: Interruptible asymmetric verification operations ------------------------------------------------ @@ -1005,19 +1005,19 @@ The interruptible asymmetric verification operation verifies the signature of a An interruptible asymmetric verification operation is used as follows: -1. Allocate an interruptible asymmetric verification operation object, of type `psa_verify_interruptible_operation_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_interruptible_operation_t`, for example, `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`. -#. Call `psa_verify_interruptible_setup()` to specify the algorithm, key, and the signature to verify. -#. Call `psa_verify_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +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_interruptible_hash()` with a pre-computed hash of the message to verify; or - 2. Call `psa_verify_interruptible_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_interruptible_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_interruptible_abort()`. + 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_interruptible_operation_t +.. typedef:: /* implementation-defined type */ psa_verify_iop_t .. summary:: The type of the state data structure for an interruptible asymmetric verification operation. @@ -1028,67 +1028,67 @@ An interruptible asymmetric verification operation is used as follows: .. code-block:: xref - psa_verify_interruptible_operation_t operation; + 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_interruptible_operation_t operation; + static psa_verify_iop_t operation; - * Initialize the object to the initializer `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`, for example: + * Initialize the object to the initializer `PSA_VERIFY_IOP_INIT`, for example: .. code-block:: xref - psa_verify_interruptible_operation_t operation = PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT; + psa_verify_iop_t operation = PSA_VERIFY_IOP_INIT; - * Assign the result of the function `psa_verify_interruptible_operation_init()` to the object, for example: + * Assign the result of the function `psa_verify_iop_init()` to the object, for example: .. code-block:: xref - psa_verify_interruptible_operation_t operation; - operation = psa_verify_interruptible_operation_init(); + 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_INTERRUPTIBLE_OPERATION_INIT +.. 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_interruptible_operation_t`. + This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_iop_t`. -.. function:: psa_verify_interruptible_operation_init +.. function:: psa_verify_iop_init .. summary:: Return an initial value for an interruptible asymmetric verification operation object. - .. return:: psa_verify_interruptible_operation_t + .. return:: psa_verify_iop_t -.. function:: psa_verify_interruptible_get_num_ops +.. function:: psa_verify_iop_get_num_ops .. summary:: Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. - .. param:: psa_verify_interruptible_operation_t * operation + .. 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_interruptible_setup()` or `psa_verify_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_setup +.. function:: psa_verify_iop_setup .. summary:: Begin the setup of an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation - The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_interruptible_operation_t` and not yet in use. + .. 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 @@ -1101,7 +1101,7 @@ An interruptible asymmetric verification operation is used as follows: .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The operation setup must now be completed by calling `psa_verify_interruptible_setup_complete()`. + 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 @@ -1129,21 +1129,21 @@ An interruptible asymmetric verification operation is used as follows: .. 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 asymmetric signature operation, see :secref:`interruptible-sign`. - After a successful call to `psa_verify_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_interruptible_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()`, 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_interruptible_complete()`. - * A call to `psa_verify_interruptible_abort()`. + * A successful call to `psa_verify_iop_complete()`. + * A call to `psa_verify_iop_abort()`. - If `psa_verify_interruptible_setup()` returns an error, the operation object is unchanged. + If `psa_verify_iop_setup()` returns an error, the operation object is unchanged. -.. function:: psa_verify_interruptible_setup_complete +.. function:: psa_verify_iop_setup_complete .. summary:: Finish setting up an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. 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 @@ -1169,18 +1169,18 @@ 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_interruptible_hash()` or `psa_verify_interruptible_update()`. + 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_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_hash +.. function:: psa_verify_iop_hash .. summary:: Input a pre-computed hash to an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. 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. @@ -1221,17 +1221,17 @@ An interruptible asymmetric verification operation is used as follows: 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_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + 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_interruptible_abort()`. + 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_interruptible_update +.. function:: psa_verify_iop_update .. summary:: Add a message fragment to an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. 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. @@ -1267,18 +1267,18 @@ An interruptible asymmetric verification operation is used as follows: 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_interruptible_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_interruptible_update()`. + 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_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + 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_interruptible_abort()`. + 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_interruptible_complete +.. function:: psa_verify_iop_complete .. summary:: Attempt to finish the interruptible verification of an asymmetric signature. - .. param:: psa_verify_interruptible_operation_t * operation + .. 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 @@ -1290,7 +1290,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_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * 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. @@ -1306,16 +1306,16 @@ An interruptible asymmetric verification operation is used as follows: 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_interruptible_abort()`. + 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_interruptible_set_max_ops()`. + 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_interruptible_abort +.. function:: psa_verify_iop_abort .. summary:: Abort an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to abort. .. return:: psa_status_t @@ -1327,11 +1327,11 @@ 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_interruptible_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()` again. - This function can be called at any time after the operation object has been initialized as described in `psa_verify_interruptible_operation_t`. + 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_interruptible_abort()` twice, or to call `psa_verify_interruptible_abort()` on an operation that has not been set up. + In particular, it is valid to call `psa_verify_iop_abort()` twice, or to call `psa_verify_iop_abort()` on an operation that has not been set up. Support macros -------------- diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index fcc78ed2..ce7631a0 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -159,10 +159,10 @@ The typical sequence of actions with a multi-part operation is as follows: #. **Initialize:** Initialize or assign the 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_INIT`` macro as the type is implementation-defined. + - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_OPERATION_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_INIT``. - - Assign the result of calling the associated function ``psa_xxx_init()``. + - Assign the value of the associated macro ``PSA_xxx_OPERATION_INIT``. + - Assign the result of calling the associated function ``psa_xxx_operation_init()``. The resulting object is now *inactive*. @@ -254,10 +254,10 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Initialize:** Initialize or assign the 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_INIT`` macro as the type is implementation-defined. + - 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_INIT``. - - Assign the result of calling the associated function ``psa_xxx_init()``. + - 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 operation object that is in *active* or *error* states. This can leak memory or other resources. @@ -282,11 +282,11 @@ The typical sequence of actions with a interruptible operation is as follows: An application needs to repeat this step until the completion function completes with success or an error status. -#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_interruptible_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. +#. **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 and return the operation object to the *inactive* state. - Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_interruptible_abort()`` function to release the operation resources and return the object to the *inactive* state. + Any error that occurs to an operation while it is not in an *inactive* 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_interruptible_abort()`` can be called on an *inactive* operation, and this has no effect. + ``psa_xxx_iop_abort()`` can be called on an *inactive* 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 applicable setup functions again. @@ -303,7 +303,7 @@ It is safe to move an interruptible operation object to a different memory locat Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. -See :secref:`interruptible_sign` for an example of using an interruptible operation. +See :secref:`interruptible-sign` for an example of using an interruptible operation. Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ From 916d0a136d942601862435d5f7a4e77ffc436ceb Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Jun 2024 14:23:10 +0100 Subject: [PATCH 11/12] Resolve the TODO for interruptible signature. Require that one of psa_xxx_iop_hash() or psa_xxx_iop_update() MUST be called in an interruptible signature operation. --- doc/crypto/api/ops/signature.rst | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 51631347..75167616 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -647,17 +647,6 @@ Single-part asymmetric signature functions Interruptible asymmetric signature operations --------------------------------------------- -.. todo:: - - Decide how to calculate the signature of the zero-length message using the interruptible API. Either: - - * Implicitly, if neither `psa_sign_iop_hash()`, nor `psa_sign_iop_update()`, is called; OR - * Require that `psa_sign_iop_update()` is called with a zero-length input. - - In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_iop_complete()`. - - :issue:`Current preference for the latter` - 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: @@ -894,7 +883,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 hash value input. + * 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. @@ -924,6 +913,10 @@ 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:: @@ -1244,7 +1237,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 hash value input. + * 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. @@ -1273,6 +1266,10 @@ 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:: From 531a4297e49740727255d3716e85d17309663c7d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 18 Jun 2024 11:02:21 +0100 Subject: [PATCH 12/12] Fix: missing BAD_STATE errors in iop setup functions --- doc/crypto/api/ops/signature.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 75167616..62d0f98c 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -761,6 +761,11 @@ An interruptible asymmetric signature operation is used as follows: * ``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 @@ -1113,6 +1118,11 @@ An interruptible asymmetric verification operation is used as follows: * ``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