diff --git a/Makefile b/Makefile index e0eb7a58c5aa..f794482aae21 100644 --- a/Makefile +++ b/Makefile @@ -14,7 +14,7 @@ programs: lib $(MAKE) -C programs lib: - $(MAKE) -C library + $(MAKE) -C library "PSA_DRIVERS=$(abspath $(PSA_DRIVERS))" tests: lib $(MAKE) -C tests diff --git a/docs/architecture/Makefile b/docs/architecture/Makefile index ab22fb16df19..c57810930f3f 100644 --- a/docs/architecture/Makefile +++ b/docs/architecture/Makefile @@ -4,6 +4,7 @@ default: all all_markdown = \ mbed-crypto-storage-specification.md \ + psa-driver-interface.md \ testing/driver-interface-test-strategy.md \ testing/test-framework.md \ # This line is intentionally left blank diff --git a/docs/architecture/psa-driver-interface.md b/docs/architecture/psa-driver-interface.md new file mode 100644 index 000000000000..22aef0a517fc --- /dev/null +++ b/docs/architecture/psa-driver-interface.md @@ -0,0 +1,561 @@ +PSA Cryptoprocessor Driver Interface +==================================== + +This document describes an interface for cryptoprocessor drivers in the PSA cryptography API. It describes the design of the PSA interface as well as the reference implementation in Mbed TLS and provides instructions and guidelines to driver writers. + +**This is work in progress**. This document is still incomplete and **may change or may be abandoned at any time**. The interface is not fully implemented in Mbed TLS yet and is disabled by default; you can enable the experimental work in progress by setting `MBEDTLS_PSA_CRYPTO_DRIVERS` in the compile-time configuration. + +Time-stamp: "2020/05/26 21:44:21 GMT" + +## Introduction + +### Purpose of the driver interface + +The PSA Cryptography API defines an interface that allows applications to perform cryptographic operations in a uniform way regardless of how the operations are performed. Under the hood, different keys may be processed in different hardware or in different logical partitions, and different algorithms may involve different hardware or software components. + +The driver interface allows implementations of the PSA Crypytography API to be built compositionally. An implementation of the PSA Cryptography API is composed of a **core** and zero or more **drivers**. The core handles key management, enforces key usage policies, and dispatches cryptographic operations either to the applicable driver or to built-in code. + +Functions in the PSA Cryptography API invoke functions in the core. Code from the core calls drivers as described in the present document. + +### Types of drivers + +The PSA Cryptography driver interface supports two types of cryptoprocessors, and accordingly two types of drivers. + +* **Transparent** drivers implement cryptographic operations on keys that are provided in cleartext at the beginning of each operation. They are typically used for hardware **accelerators**. When a transparent driver is available for a particular combination of parameters (cryptographic algorithm, key type and size, etc.), it is used instead of the default software implementation. Transparent drivers can also be pure software implementations that are distributed as plug-ins to a PSA Crypto implementation. +* **Opaque** drivers implement cryptographic operations on keys that can only be used inside a protected environment such as a **secure element**, a hardware security module, a smartcard, a secure enclave, etc. An opaque driver is invoked for the specific key location that the driver is registered for: the dispatch is based on the key's lifetime. + +### Requirements + +The present specification was designed to fulfil the following high-level requirements. + +[Req.plugins] It is possible to combine multiple drivers from different providers into the same implementation, without any prior arrangement other than choosing certain names and values from disjoint namespaces. + +[Req.compile] It is possible to compile the code of each driver and of the core separately, and link them together. A small amount of glue code may need to be compiled once the list of drivers is available. + +[Req.types] Support drivers for the following types of hardware: accelerators that operate on keys in cleartext; cryptoprocessors that can wrap keys with a built-in keys but not store user keys; and cryptoprocessors that store key material. + +[Req.portable] The interface between drivers and the core does not involve any platform-specific consideration. Driver calls are simple C functions. Interactions between driver code and hardware happen inside the driver (and in fact a driver need not involve any hardware at all). + +[Req.location] Applications can tell which location values correspond to which secure element drivers. + +[Req.fallback] Accelerator drivers can specify that they do not fully support a cryptographic mechanism and that a fallback to core code may be necessary. Conversely, if an accelerator fully supports cryptographic mechanism, the core does not need to include code for this mechanism. + +[Req.mechanisms] Drivers can specify which mechanisms they support. A driver's code will not be invoked for cryptographic mechanisms that it does not support. + +## Overview of drivers + +### Deliverables for a driver + +To write a driver, you need to implement some functions with C linkage, and to declare these functions in a **driver description file**. The driver description file declares which functions the driver implements and what cryptographic mechanisms they support. Depending on the driver type, you may also need to define some C types and macros in a header file. + +The concrete syntax for a driver description file is JSON. The structure of this JSON file is specified in the section [“Driver description syntax”](#driver-description-syntax). + +A driver therefore consists of: + +* A driver description file (in JSON format). +* C header files defining the types required by the driver description. The names of these header files is declared in the driver description file. +* An object file compiled for the target platform defining the functions required by the driver description. Implementations may allow drivers to be provided as source files and compiled with the core instead of being pre-compiled. + +How to provide the driver description file, the C header files and the object code is implementation-dependent. + +Implementations should support multiple drivers. + +### Driver description syntax + +The concrete syntax for a driver description file is JSON. + +#### Driver description top-level element + +A driver description is a JSON object containing the following properties: + +* `"prefix"` (mandatory, string). This must be a valid prefix for a C identifier. All the types and functions provided by the driver have a name that starts with this prefix unless overridden with a `"name"` element in the applicable capability as described below. +* `"type"` (mandatory, string). One of `"transparent"` or `"opaque"`. +* `"headers"` (optional, array of strings). A list of header files. These header files must define the types provided by the driver and may declare the functions provided by the driver. They may include other PSA headers and standard headers of the platform. Whether they may include other headers is implementation-specific. If omitted, the list of headers is empty. +* `"capabilities"` (mandatory, array of [capabilities](#driver-description-capability)). +A list of **capabilities**. Each capability describes a family of functions that the driver implements for a certain class of cryptographic mechanisms. +* `"key_context"` (not permitted for transparent drivers, mandatory for opaque drivers): information about the [representation of keys](#key-format-for-opaque-drivers). +* `"persistent_state_size"` (not permitted for transparent drivers, optional for opaque drivers, integer or string). The size in bytes of the [persistent state of the driver](#opaque-driver-persistent-state). This may be either a non-negative integer or a C constant expression of type `size_t`. +* `"location"` (not permitted for transparent drivers, optional for opaque drivers, integer or string). The location value for which this driver is invoked. This may be either a non-negative integer or a C constant expression of type `psa_key_location_t`. + +#### Driver description capability + +A capability declares a family of functions that the driver implements for a certain class of cryptographic mechanisms. The capability specifies which key types and algorithms are covered and the names of the types and functions that implement it. + +A capability is a JSON object containing the following properties: + +* `"functions"` (optional, list of strings). Each element is the name of a [driver function](#driver-functions) or driver function family. If specified, the core will invoke this capability of the driver only when performing one of the specified operations. If omitted, the `"algorithms"` property is mandatory and the core will invoke this capability of the driver for all operations that are applicable to the specified algorithms. The driver must implement all the specified or implied functions, as well as the types if applicable. +* `"algorithms"` (optional, list of strings). Each element is an [algorithm specification](#algorithm-specifications). If specified, the core will invoke this capability of the driver only when performing one of the specified algorithms. If omitted, the core will invoke this capability for all applicable algorithms. +* `"key_types"` (optional, list of strings). Each element is a [key type specification](#key-type-specifications). If specified, the core will invoke this capability of the driver only for operations involving a key with one of the specified key types. If omitted, the core will invoke this capability of the driver for all applicable key types. +* `"key_sizes"` (optional, list of integers). If specified, the core will invoke this capability of the driver only for operations involving a key with one of the specified key sizes. If omitted, the core will invoke this capability of the driver for all applicable key sizes. Key sizes are expressed in bits. +* `"names"` (optional, object). A mapping from function names described by the `"functions"` property, to the name of the C function in the driver that implements the corresponding function. If a function is not listed here, name of the driver function that implements it is the driver's prefix followed by an underscore (`_`) followed by the function name. If this property is omitted, it is equivalent to an empty object (so each function *suffix* is implemented by a function with called *prefix*`_`*suffix*). +* `"fallback"` (optional for transparent drivers, not permitted for opaque drivers, boolean). If present and true, the driver may return `PSA_ERROR_NOT_SUPPORTED`, in which case the core should call another driver or use built-in code to perform this operation. If absent or false, the core should not include built-in code to perform this particular cryptographic mechanism. + +Example: the following capability declares that the driver can perform deterministic ECDSA signatures using SHA-256 or SHA-384 with a SECP256R1 or SECP384R1 private key (with either hash being possible in combinatio with either curve). If the prefix of this driver is `"acme"`, the function that performs the signature is called `acme_sign_hash`. +``` +{ + "functions": ["sign_hash"], + "algorithms": ["PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_256)", + "PSA_ALG_DETERMINISTIC_ECDSA(PSA_ALG_SHA_384)"], + "key_types": ["PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP_R1)"], + "key_sizes": [256, 384] +} +``` + +### Algorithm and key specifications + +#### Algorithm specifications + +An algorithm specification is a string consisting of a `PSA_ALG_xxx` macro that specifies a cryptographic algorithm defined by the PSA Cryptography API. If the macro takes arguments, the string must have the syntax of a C macro call and each argument must be an algorithm specification or a decimal or hexadecimal literal with no suffix, depending on the expected type of argument. + +Spaces are optional after commas. Whether other whitespace is permitted is implementation-specific. + +Valid examples: +``` +PSA_ALG_SHA_256 +PSA_ALG_HMAC(PSA_ALG_SHA_256) +PSA_ALG_KEY_AGREEMENT(PSA_ALG_ECDH, PSA_ALG_HKDF(PSA_ALG_SHA_256)) +``` + +#### Key type specifications + +An algorithm specification is a string consisting of a `PSA_KEY_TYPE_xxx` macro that specifies a key type defined by the PSA Cryptography API. If the macro takes an argument, the string must have the syntax of a C macro call and each argument must be the name of a constant of suitable type (curve or group). + +The name `_` may be used instead of a curve or group to indicate that the capability concerns all curves or groups. + +Valid examples: +``` +PSA_KEY_TYPE_AES +PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_CURVE_SECP_R1) +PSA_KEY_TYPE_ECC_KEY_PAIR(_) +``` + +### Driver functions + +#### Overview of driver functions + +Drivers define functions, each of which implements an aspect of a capability of a driver, such as a cryptographic operation, a part of a cryptographic operation, or a key management action. Most driver functions correspond to a particular function in the PSA Cryptography API. For example, if a call to `psa_sign_hash()` is dispatched to a driver, it invokes the driver's `sign_hash` function. + +All driver functions return a status of type `psa_status_t` which should use the status codes documented for PSA services in general and for PSA Crypto in particular: `PSA_SUCCESS` indicates that the function succeeded, and `PSA_ERROR_xxx` values indicate that an error occurred. + +The signature of a driver function generally looks like the signature of the PSA Crypto API that it implements, with some modifications. This section gives an overview of modifications that apply to whole classes of functions. Refer to the reference section for each function or function family for details. + +* For functions that operate on an existing key, the `psa_key_id_t` parameter (`psa_key_handle_t` in versions of Mbed TLS that are compatible with PSA Crypto 1.0 beta 3) is replaced by a sequence of three parameters that describe the key: + 1. `const psa_key_attributes_t *attributes`: the key attributes. + 2. `const uint8_t *key_buffer`: a key material or key context buffer. + 3. `size_t key_buffer_size`: the size of the key buffer in bytes. + + For transparent drivers, the key buffer contains the key material, in the same format as defined for `psa_export_key()` and `psa_export_public_key()` in the PSA Cryptography API. For opaque drivers, the content of the key buffer is entirely up to the driver. + +* For functions that involve a multi-part operation, the operation state type (`psa_XXX_operation_t`) is replaced by a driver-specific operation state type (*prefix*`_XXX_operation_t`). + +Some functions are grouped in families that must be implemented as a whole. If a driver supports a function family, it must provide all the functions in the family. + +#### General considerations on driver function parameters + +Buffer parameters for driver functions obey the following conventions: + +* An input buffer has the type `const uint8_t *` and is immediately followed by a parameter of type `size_t` that indicates the buffer size. +* An output buffer has the type `uint8_t *` and is immediately followed by a parameter of type `size_t` that indicates the buffer size. A third parameter of type `size_t *` is provided to report the actual buffer size if the function succeeds. +* An in-out buffer has the type `uint8_t *` and is immediately followed by a parameter of type `size_t` that indicates the buffer size. Note that the buffer size does not change. + +Buffers of size 0 may be represented with either a null pointer or a non-null pointer. + +Input buffers and other input-only parameters (`const` pointers) may be in read-only memory. Overlap is possible between input buffers, and between an input buffer and an output buffer, but not between two output buffers or between a non-buffer parameter and another parameter. + +#### Driver functions for single-part cryptographic operations + +The following driver functions perform a cryptographic operation in one shot (single-part operation): + +* `"hash_compute"` (transparent drivers only): calculation of a hash. Called by `psa_hash_compute()` and `psa_hash_compare()`. To verify a hash with `psa_hash_compare()`, the core calls the driver's `"hash_compute"` function and compares the result with the reference hash value. +* `"mac_compute"`: calculation of a MAC. Called by `psa_mac_compute()` and possibly `psa_mac_verify()`. To verify a mac with `psa_mac_verify()`, the core calls an applicable driver's `"mac_verify"` function if there is one, otherwise the core calls an applicable driver's `"mac_compute"` function and compares the result with the reference MAC value. +* `"mac_verify"`: verification of a MAC. Called by `psa_mac_verify()`. This function is mainly useful for drivers of secure elements that verify a MAC without revealing the correct MAC. Although transparent drivers may implement this function in addition to `"mac_compute"`, it is generally not useful because the core can call the `"mac_compute"` function and compare with the expected MAC value. +* `"cipher_encrypt"`: unauthenticated symmetric cipher encryption. Called by `psa_cipher_encrypt()`. +* `"cipher_decrypt"`: unauthenticated symmetric cipher decryption. Called by `psa_cipher_decrypt()`. +* `"aead_encrypt"`: authenticated encryption with associated data. Called by `psa_aead_encrypt()`. +* `"aead_decrypt"`: authenticated decryption with associated data. Called by `psa_aead_decrypt()`. +* `"asymmetric_encrypt"`: asymmetric encryption. Called by `psa_asymmetric_encrypt()`. +* `"asymmetric_decrypt"`: asymmetric decryption. Called by `psa_asymmetric_decrypt()`. +* `"sign_hash"`: signature of an already calculated hash. Called by `psa_sign_hash()` and possibly `psa_sign_message()`. To sign a message with `psa_sign_message()`, the core calls an applicable driver's `"sign_message"` function if there is one, otherwise the core calls an applicable driver's `"hash_compute"` function followed by an applicable driver's `"sign_hash"` function. +* `"verify_hash"`: verification of an already calculated hash. Called by `psa_verify_hash()` and possibly `psa_verify_message()`. To verify a message with `psa_verify_message()`, the core calls an applicable driver's `"verify_message"` function if there is one, otherwise the core calls an applicable driver's `"hash_compute"` function followed by an applicable driver's `"verify_hash"` function. +* `"sign_message"`: signature of a message. Called by `psa_sign_message()`. +* `"verify_message"`: verification of a message. Called by `psa_verify_message()`. +* `"key_agreement"`: key agreement without a subsequent key derivation. Called by `psa_raw_key_agreement()` and possibly `psa_key_derivation_key_agreement()`. + +### Driver functions for multi-part operations + +#### General considerations on multi-part operations + +The functions that implement each step of a multi-part operation are grouped into a family. A driver that implements a multi-part operation must define all of the functions in this family as well as a type that represents the operation context. The lifecycle of a driver operation context is similar to the lifecycle of an API operation context: + +1. The core initializes operation context objects to either all-bits-zero or to logical zero (`{0}`), at its discretion. +1. The core calls the `xxx_setup` function for this operation family. If this fails, the core destroys the operation context object without calling any other driver function on it. +1. The core calls other functions that manipulate the operation context object, respecting the constraints. +1. If any function fails, the core calls the driver's `xxx_abort` function for this operation family, then destroys the operation context object without calling any other driver function on it. +1. If a “finish” function fails, the core destroys the operation context object without calling any other driver function on it. The finish functions are: *prefix*`_mac_sign_finish`, *prefix*`_mac_verify_finish`, *prefix*`_cipher_fnish`, *prefix*`_aead_finish`, *prefix*`_aead_verify`. + +If a driver implements a multi-part operation but not the corresponding single-part operation, the core calls the driver's multipart operation functions to perform the single-part operation. + +#### Multi-part operation function family `"hash_multipart"` + +This family corresponds to the calculation of a hash in one multiple parts. + +This family applies to transparent drivers only. + +This family requires the following type and functions: + +* Type `"hash_operation_t"`: the type of a hash operation context. It must be possible to copy a hash operation context byte by byte, therefore hash operation contexts must not contain any embedded pointers (except pointers to global data that do not change after the setup step). +* `"hash_setup"`: called by `psa_hash_setup()`. +* `"hash_update"`: called by `psa_hash_update()`. +* `"hash_finish"`: called by `psa_hash_finish()` and `psa_hash_verify()`. +* `"hash_abort"`: called by all multi-part hash functions. + +To verify a hash with `psa_hash_verify()`, the core calls the driver's *prefix`_hash_finish` function and compares the result with the reference hsah value. + +For example, a driver with the prefix `"acme"` that implements the `"hash_multipart"` function family must define the following type and functions (assuming that the capability does not use the `"names"` property to declare different type and function names): + +``` +typedef ... acme_hash_operation_t; +psa_status_t acme_hash_setup(acme_hash_operation_t *operation, + psa_algorithm_t alg); +psa_status_t acme_hash_update(acme_hash_operation_t *operation, + const uint8_t *input, + size_t input_length); +psa_status_t acme_hash_finish(acme_hash_operation_t *operation, + uint8_t *hash, + size_t hash_size, + size_t *hash_length); +psa_status_t acme_hash_abort(acme_hash_operation_t *operation); +``` + +#### Operation family `"mac_multipart"` + +TODO + +#### Operation family `"mac_verify_multipart"` + +TODO + +#### Operation family `"cipher_encrypt_multipart"` + +TODO + +#### Operation family `"cipher_decrypt_multipart"` + +TODO + +#### Operation family `"aead_encrypt_multipart"` + +TODO + +#### Operation family `"aead_decrypt_multipart"` + +TODO + +#### Operation family `"key_derivation"` + +This family requires the following type and functions: + +* Type `"key_derivation_operation_t"`: the type of a key derivation operation context. +* `"key_derivation_setup"`: called by `psa_key_derivation_setup()`. +* `"key_derivation_set_capacity"`: called by `psa_key_derivation_set_capacity()`. The core will always enforce the capacity, therefore this function does not need to do anything for algorithms where the output stream only depends on the effective generated length and not on the capacity. +* `"key_derivation_input_bytes"`: called by `psa_key_derivation_input_bytes()` and `psa_key_derivation_input_key()`. For transparent drivers, when processing a call to `psa_key_derivation_input_key()`, the core always calls the applicable driver's `"key_derivation_input_bytes"` function. +* `"key_derivation_input_key"` (opaque drivers only) +* `"key_derivation_output_bytes"`: called by `psa_key_derivation_output_bytes()`; also by `psa_key_derivation_output_key()` for transparent drivers. +* `"key_derivation_abort"`: called by all key derivation functions. + +TODO: key input and output for opaque drivers; deterministic key generation for transparent drivers + +TODO + +### Driver functions for key management + +The driver functions for key management differs significantly between [transparent drivers](#key-management-with-transparent-drivers) and [opaque drivers](#key-management-with-transparent-drivers). Refer to the applicable section for each driver type. + +### Miscellaneous driver functions + +#### Driver initialization + +An opaque driver may declare an `"init"` function in a capability with no algorithm, key type or key size. If so, the driver calls this function once during the initialization of the PSA Crypto subsystem. If the init function of any driver fails, the initialization of the PSA Crypto subsystem fails. + +When multiple drivers have an init function, the order in which they are called is unspecified. It is also unspecified whether other drivers' init functions are called if one or more init function fails. + +On platforms where the PSA Crypto implementation is a subsystem of a single application, the initialization of the PSA Crypto subsystem takes place during the call to `psa_crypto_init()`. On platforms where the PSA Crypto implementation is separate from the application or applications, the initialization the initialization of the PSA Crypto subsystem takes place before or during the first time an application calls `psa_crypto_init()`. + +For transparent drivers, the init function does not take any parameter. + +For opaque drivers, the init function takes the following parameters: + +* `uint8_t *persistent_state`: the driver's persistent state. On the first boot of the device, this contains all-bits-zero. On subsequent boots, the core loads the last saved state. +* `size_t persistent_state_size`: the size of the persistent state in bytes. + +For an opaque driver, if the init function succeeds, the core saves the updated persistent state. If the init function fails, the persistent state is unchanged. + +### Combining multiple drivers + +To declare a cryproprocessor can handle both cleartext and plaintext keys, you need to provide two driver descriptions, one for a transparent driver and one for an opaque driver. You can use the mapping in capabilities' `"names"` property to arrange for driver functions to map to the same C function. + +## Transparent drivers + +### Key format for transparent drivers + +The format of a key for transparent drivers is the same as in applications. Refer to the documentation of `psa_export_key()` and `psa_export_public_key()`. + +### Key management with transparent drivers + +Transparent drivers may provide the following key management functions: + +* `"generate_key"`: called by `psa_generate_key()`, only when generating a key pair (key such that `PSA_KEY_TYPE_IS_ASYMMETRIC` is true). +* `"derive_key"`: called by `psa_key_derivation_output_key()`, only when deriving a key pair (key such that `PSA_KEY_TYPE_IS_ASYMMETRIC` is true). +* `"export_public_key"`: called by the core to obtain the public key of a key pair. The core may call this function at any time to obtain the public key, which can be for `psa_export_public_key()` but also at other times, including during a cryptographic operation that requires the public key such as a call to `psa_verify_message()` on a key pair object. + +Transparent drivers are not involved when importing, exporting, copying or destroying keys, or when generating or deriving symmetric keys. + +### Fallback + +If a transparent driver function is part of a capability which has a true `"fallback"` property and returns `PSA_ERROR_NOT_SUPPORTED`, the built-in software implementation will be called instead. Any other value (`PSA_SUCCESS` or a different error code) is returned to the application. + +If there are multiple available transparent drivers, the core tries them in turn until one is declared without a true `"fallback"` property or returns a status other than `PSA_ERROR_NOT_SUPPORTED`. + +If a transparent driver function is part of a capability where the `"fallback"` property is false or omitted, the core should not include any other code for this capability, whether built in or in another transparent driver. + +## Opaque drivers + +Opaque drivers allow a PSA Cryptography implementation to delegate cryptographic operations to a separate environment that might not allow exporting key material in cleartext. The opaque driver interface is designed so that the core never inspects the representation of a key. The opaque driver interface is designed to support two subtypes of cryptoprocessors: + +* Some cryptoprocessors do not have persistent storage for individual keys. The representation of a key is the key material wrapped with a master key which is located in the cryptoprocessor and never exported from it. The core stores this wrapped key material on behalf of the cryptoprocessor. +* Some cryptoprocessors have persistent storage for individual keys. The representation of a key is an identifier such as label or slot number. The core stores this identifier. + +### Key format for opaque drivers + +The format of a key for opaque drivers is an opaque blob. The content of this blob is fully up to the driver. The core merely stores this blob. + +Note that since the core stores the key context blob as it is in memory, it must only contain data that is meaningful after a reboot. In particular, it must not contain any pointers or transient handles. + +The `"key_context"` property in the [driver description](#driver-description-top-level-element) specifies how to calculate the size of the key context as a function of the key type and size. This is an object with the following properties: + +* `"base_size"` (integer or string, optional): this many bytes are included in every key context. If omitted, this value defaults to 0. +* `"key_pair_size"` (integer or string, optional): this many bytes are included in every key context for a key pair. If omitted, this value defaults to 0. +* `"public_key_size"` (integer or string, optional): this many bytes are included in every key context for a key pair. If omitted, this value defaults to 0. +* `"symmetric_factor"` (integer or string, optional): every key context for a symmetric key includes this many times the key size. If omitted, this value defaults to 0. +* `"store_public_key"` (boolean, optional): If specified and true, for a key pair, the key context includes space for the public key. If omitted or false, no additional space is added for the public key. +* `"size_function"` (string, optional): the name of a function that returns the number of bytes that the driver needs in a key context for a key. This may be a pointer to function. This must be a C identifier; more complex expressions are not permitted. If the core uses this function, it supersedes all the other properties. + +The integer properties must be C language constants. A typical value for `"base_size"` is `sizeof(acme_key_context_t)` where `acme_key_context_t` is a type defined in a driver header file. + +#### Size of a dynamically allocated key context + +If the core supports dynamic allocation for the key context and chooses to use it, and the driver specification includes the `"size_function"` property, the size of the key context is at least +``` +size_function(key_type, key_bits) +``` +where `size_function` is the function named in the `"size_function"` property, `key_type` is the key type and `key_bits` is the key size in bits. The prototype of the size function is +``` +size_t size_function(psa_key_type_t key_type, size_t key_bits); +``` + +#### Size of a statically allocated key context + +If the core does not support dynamic allocation for the key context or chooses not to use it, or if the driver specification does not include the `"size_function"` property, the size of the key context for a key of type `key_type` and of size `key_bits` bits is: + +* For a key pair (`PSA_KEY_TYPE_IS_KEY_PAIR(key_type)` is true): + ``` + base_size + key_pair_size + public_key_overhead + ``` + where `public_key_overhead = PSA_EXPORT_PUBLIC_KEY_MAX_SIZE(key_type, key_bits)` if the `"store_public_key"` property is true and `public_key_overhead = 0` otherwise. + +* For a public key (`PSA_KEY_TYPE_IS_PUBLIC_KEY(key_type)` is true): + ``` + base_size + public_key_size + ``` + +* For a symmetric key (not a key pair or public key): + ``` + base_size + symmetric_factor * key_bytes + ``` + where `key_bytes = ((key_bits + 7) / 8)` is the key size in bytes. + +#### Key context size for a secure element with storage + +If the key is stored in the secure element and the driver only needs to store a label for the key, use `"base_size"` as the size of the label plus any other metadata that the driver needs to store, and omit the other properties. + +If the key is stored in the secure element, but the secure element does not store the public part of a key pair and cannot recompute it on demand, additionally use the `"store_public_key"` property with the value `true`. Note that this only influences the size of the key context: the driver code must copy the public key to the key context and retrieve it on demand in its `export_public_key` function. + +#### Key context size for a secure element without storage + +If the key is stored in wrapped form outside the secure element, and the wrapped form of the key plus any metadata has up to *N* bytes of overhead, use *N* as the value of the `"base_size"` property and set the `"symmetric_factor"` property to 1. Set the `"key_pair_size"` and `"public_key_size"` properties appropriately for the largest supported key pair and the largest supported public key respectively. + +### Key management with opaque drivers + +Transparent drivers may provide the following key management functions: + +* `"allocate_key"`: called by `psa_import_key()`, `psa_generate_key()`, `psa_key_derivation_output_key()` or `psa_copy_key()` before creating a key in the location of this driver. +* `"import_key"`: called by `psa_import_key()`, or by `psa_copy_key()` when copying a key from another location. +* `"export_key"`: called by `psa_export_key()`, or by `psa_copy_key()` when copying a key from to location. +* `"export_public_key"`: called by the core to obtain the public key of a key pair. The core may call this function at any time to obtain the public key, which can be for `psa_export_public_key()` but also at other times, including during a cryptographic operation that requires the public key such as a call to `psa_verify_message()` on a key pair object. +* `"copy_key"`: called by `psa_copy_key()` when copying a key within the same location. +* `"destroy_key"`: called by `psa_destroy_key()`. +* `"generate_key"`: called by `psa_generate_key()`. +* `"derive_key"`: called by `psa_key_derivation_output_key()`. + +#### Key creation in a secure element without storage + +This section describes the key creation process for secure elements that do not store the key material. The driver must obtain a wrapped form of the key material which the core will store. A driver for such a secure element has no `"allocate_key"` function. + +When creating a key with an opaque driver which does not have an `"allocate_key"` function: + +1. The core allocates memory for the key context. +2. The core calls the driver's import, generate, derive or copy function. +3. The core saves the resulting wrapped key material and any other data that the key context may contain. + +#### Key creation in a secure element with storage + +This section describes the key creation process for secure elements that have persistent storage for the key material. A driver for such a secure element has an `"allocate_key"` function whose intended purpose is to obtain an identifier for the key. This may be, for example, a unique label or a slot number. + +When creating a persistent key with an opaque driver which does not have an `"allocate_key"` function: + +1. The core calls the driver's `"allocate_key"` function. This function typically allocates an identifier for the key without modifying the state of the secure element and stores the identifier in the key context. This function should not modify the state of the secure element. + +1. The core saves the key context to persistent storage. + +1. The core saves the driver's persistent state. + +1. The core calls the driver's key creation function. + +If a failure occurs after the `"allocate_key"` step but before the call to the second driver function, the core will do one of the following: + +* Fail the creation of the key without indicating this to the driver. This can happen, in particular, if the device loses power immediately after the key allocation function returns. +* Call the driver's `"destroy_key"` function. + +TODO: explain the individual key management functions + +### Opaque driver persistent state + +The core maintains persistent state on behalf of an opaque driver. This persistent state consists of a single byte array whose size is given by the `"persistent_state_size"` property in the [driver description](#driver-description-top-level-element). + +TODO: how the state is passed to the driver; which driver functions can modify the state and how; when the core saves the updated state + +## How to use drivers from an application + +### Declaring which cryptographic mechanism an application needs + +TODO: an application requirements description, broadly similar to driver capabilities. + +### Using transparent drivers + +Transparent drivers linked into the library are automatically used for the mechanisms that they implement. + +### Using opaque drivers + +Each opaque driver is assigned a location. The driver is invoked for all actions that use a key in that location. A key's location is indicated by its lifetime. The application chooses the key's lifetime when it creates the key. + +For example, the following snippet creates an AES-GCM key which is only accessible inside a secure element. +``` +psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; +psa_set_key_lifetime(&attributes, PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION( + PSA_KEY_PERSISTENCE_DEFAULT, PSA_KEY_LOCATION_ACME_SECURE_ELEMENT)); +psa_set_key_identifer(&attributes, 42); +psa_set_key_type(&attributes, PSA_KEY_TYPE_AES); +psa_set_key_size(&attributes, 128); +psa_set_key_algorithm(&attributes, PSA_ALG_GCM); +psa_set_key_usage_flags(&attributes, PSA_KEY_USAGE_ENCRYPT | PSA_KEY_USAGE_DECRYPT); +psa_key_handle_t handle = 0; +psa_generate_key(&attributes, &handle); +``` + +TODO: how does the application know which location value to use? + +## How to build Mbed TLS with drivers + +To build Mbed TLS with drivers: + +1. Activate `MBEDTLS_PSA_CRYPTO_DRIVERS` in the library configuration. + + ``` + cd /path/to/mbedtls + scripts/config.py set MBEDTLS_PSA_CRYPTO_DRIVERS + ``` + +2. Pass the [driver description files](#driver-description-files) through the Make variable `PSA_DRIVERS` when building the library. + + ``` + cd /path/to/mbedtls + make PSA_DRIVERS="/path/to/acme/driver.json /path/to/nadir/driver.json" lib + ``` + +3. Link your application with the implementation of the driver functions. + + ``` + cd /path/to/application + ld myapp.o -L/path/to/acme -lacmedriver -L/path/to/nadir -lnadirdriver -L/path/to/mbedtls -lmbedcrypto + ``` + +## Mbed TLS internal architecture + +TODO + +## Open questions + +### Driver declarations + +#### Declaring driver functions + +The core may want to provide declarations for the driver functions so that it can compile code using them. At the time of writing this paragraph, the driver headers must define types but there is no obligation for them to declare functions. The core knows what the function names and argument types are, so it can generate prototypes. + +It should be ok for driver functions to be function-like macros or function pointers. + +#### Driver location values + +How does a driver author decide which location values to use? It should be possible to combine drivers from different sources. Use the same vendor assignment as for PSA services? + +Can the driver assembly process generate distinct location values as needed? This can be convenient, but it's also risky: if you upgrade a device, you need the location values to be the same between builds. + +### Driver function interfaces + +#### Driver function parameter conventions + +Should 0-size buffers be guaranteed to have a non-null pointers? + +Should drivers really have to cope with overlap? + +Should the core guarantee that the output buffer size has the size indicated by the applicable buffer size macro (which may be an overestimation)? + +### Partial computations in drivers + +#### Substitution points + +Earlier drafts of the driver interface had a concept of _substitution points_: places in the calculation where a driver may be called. Some hardware doesn't do the whole calculation, but only the “main” part. This goes both for transparent and opaque drivers. Some common examples: + +* A processor that performs the RSA exponentiation, but not the padding. The driver should be able to leverage the padding code in the core. +* A processor that performs a block cipher operation only for a single block, or only in ECB mode, or only in CTR mode. The core would perform the block mode (CBC, CTR, CCM, ...). + +This concept, or some other way to reuse portable code such as specifying inner functions like `psa_rsa_pad` in the core, should be added to the specification. + +### Key management + +#### Mixing drivers in key derivation + +How does `psa_key_derivation_output_key` work when the extraction part and the expansion part use different drivers? + +#### Public key not stored + +ECC key pairs are stored as the private key value only. The public key needs to be calculated from that. + +It's fairly common for secure elements to also store only the private value. When a key is generated randomly or derived, the hardware reports the public key, and it is up to the software to store it. The current design makes this the job of the driver. Should it be the work of the core instead? + +Note that a solution also has to work for transparent keys, and when importing a private key into a secure element. If the core already has code to calculate the public key, it would make sense for the driver to be able to use it, especially in these cases. + +### Opaque drivers + +#### Opaque driver persistent state + +Should the driver be able to update it at any time? + + diff --git a/include/mbedtls/check_config.h b/include/mbedtls/check_config.h index fa3caa7c41a2..33e47736f6d5 100644 --- a/include/mbedtls/check_config.h +++ b/include/mbedtls/check_config.h @@ -559,6 +559,10 @@ #error "MBEDTLS_PSA_CRYPTO_SE_C defined, but not all prerequisites" #endif +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) && ! defined(MBEDTLS_PSA_CRYPTO_C) +#error "MBEDTLS_PSA_CRYPTO_SE_C defined, but not all prerequisites" +#endif + #if defined(MBEDTLS_PSA_CRYPTO_STORAGE_C) && \ ! defined(MBEDTLS_PSA_CRYPTO_C) #error "MBEDTLS_PSA_CRYPTO_STORAGE_C defined, but not all prerequisites" diff --git a/include/mbedtls/config.h b/include/mbedtls/config.h index 901e26d897c4..94aa14c74545 100644 --- a/include/mbedtls/config.h +++ b/include/mbedtls/config.h @@ -1307,6 +1307,17 @@ */ #define MBEDTLS_PKCS1_V21 +/** \def MBEDTLS_PSA_CRYPTO_DRIVERS + * + * Enable support for the experimental PSA crypto driver interface. + * + * Requires: MBEDTLS_PSA_CRYPTO_C. + * + * \warning This interface is experimental and may change or be removed + * without notice. + */ +//#define MBEDTLS_PSA_CRYPTO_DRIVERS + /** * \def MBEDTLS_PSA_CRYPTO_SPM * diff --git a/include/psa/crypto_driver.h b/include/psa/crypto_driver.h new file mode 100644 index 000000000000..aa1754dcfaa2 --- /dev/null +++ b/include/psa/crypto_driver.h @@ -0,0 +1,52 @@ +/** + * \file psa/crypto_driver.h + * \brief PSA cryptoprocessor driver interface + * + * This header declares types and function signatures for cryptoprocessor + * drivers. + * + * This file is part of the PSA Crypto Driver Model, containing functions for + * driver developers to implement to enable hardware to be called in a + * standardized way by a PSA Cryptographic API implementation. The functions + * comprising the driver model, which driver authors implement, are not + * intended to be called by application developers. + */ + +/* + * Copyright (C) 2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef PSA_CRYPTO_DRIVER_H +#define PSA_CRYPTO_DRIVER_H + +#include +#include + +/* Include type definitions (psa_status_t, psa_algorithm_t, + * psa_key_type_t, etc.) and macros to build and analyze values + * of these types. */ +#include "crypto_types.h" +#include "crypto_values.h" + +#ifdef __cplusplus +extern "C" { +#endif + + +#ifdef __cplusplus +} +#endif + +#endif /* PSA_CRYPTO_DRIVER_H */ diff --git a/include/psa/crypto_sizes.h b/include/psa/crypto_sizes.h index 1f04222c2491..af3c5c3ae767 100644 --- a/include/psa/crypto_sizes.h +++ b/include/psa/crypto_sizes.h @@ -659,4 +659,41 @@ PSA_KEY_TYPE_IS_ECC_PUBLIC_KEY(key_type) ? PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(key_bits) : \ 0) +#define PSA_VENDOR_RSA_MAX_PUBLIC_KEY_SIZE \ + PSA_KEY_EXPORT_RSA_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) +#define PSA_VENDOR_RSA_MAX_KEY_PAIR_SIZE \ + PSA_KEY_EXPORT_RSA_KEY_PAIR_MAX_SIZE(PSA_VENDOR_RSA_MAX_KEY_BITS) +#define PSA_VENDOR_ECC_MAX_PUBLIC_KEY_SIZE \ + PSA_KEY_EXPORT_ECC_PUBLIC_KEY_MAX_SIZE(PSA_VENDOR_ECC_MAX_CURVE_BITS) +#define PSA_VENDOR_ECC_MAX_KEY_PAIR_SIZE \ + PSA_BITS_TO_BYTES(PSA_VENDOR_ECC_MAX_CURVE_BITS) + +/** Sufficient output buffer size for psa_export_key() on a public key or psa_export_public_key(). + */ +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE 0 +#if (defined(MBEDTLS_RSA_C) || defined(MBEDTLS_PK_RSA_ALT_SUPPORT)) && \ + PSA_VENDOR_RSA_MAX_PUBLIC_KEY_SIZE > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE +#undef PSA_EXPORT_PUBLIC_KEY_MAX_SIZE +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE PSA_VENDOR_RSA_MAX_PUBLIC_KEY_SIZE +#endif +#if defined(MBEDTLS_ECP_C) && \ + PSA_VENDOR_ECC_MAX_PUBLIC_KEY_SIZE > PSA_EXPORT_PUBLIC_KEY_MAX_SIZE +#undef PSA_EXPORT_PUBLIC_KEY_MAX_SIZE +#define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE PSA_VENDOR_ECC_MAX_PUBLIC_KEY_SIZE +#endif + +/** Sufficient output buffer size for psa_export_key() on a key pair. + */ +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE 0 +#if (defined(MBEDTLS_RSA_C) || defined(MBEDTLS_PK_RSA_ALT_SUPPORT)) && \ + PSA_VENDOR_RSA_MAX_KEY_PAIR_SIZE > PSA_EXPORT_KEY_PAIR_MAX_SIZE +#undef PSA_EXPORT_KEY_PAIR_MAX_SIZE +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE PSA_VENDOR_RSA_MAX_KEY_PAIR_SIZE +#endif +#if defined(MBEDTLS_ECP_C) && \ + PSA_VENDOR_ECC_MAX_KEY_PAIR_SIZE > PSA_EXPORT_KEY_PAIR_MAX_SIZE +#undef PSA_EXPORT_KEY_PAIR_MAX_SIZE +#define PSA_EXPORT_KEY_PAIR_MAX_SIZE PSA_VENDOR_ECC_MAX_KEY_PAIR_SIZE +#endif + #endif /* PSA_CRYPTO_SIZES_H */ diff --git a/include/psa/crypto_types.h b/include/psa/crypto_types.h index d96c66e5c4a1..b4557140900b 100644 --- a/include/psa/crypto_types.h +++ b/include/psa/crypto_types.h @@ -108,18 +108,117 @@ typedef uint32_t psa_algorithm_t; * The lifetime of a key indicates where it is stored and what system actions * may create and destroy it. * - * Keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE are automatically - * destroyed when the application terminates or on a power reset. + * Lifetime values have the following structure: + * - Bits 0-7 (#PSA_KEY_LIFETIME_GET_PERSISTENCE(\c lifetime)): + * persistence level. This value indicates what device management + * actions can cause it to be destroyed. In particular, it indicates + * whether the key is _volatile_ or _persistent_. + * See ::psa_key_persistence_t for more information. + * - Bits 8-31 (#PSA_KEY_LIFETIME_GET_LOCATION(\c lifetime)): + * location indicator. This value indicates where the key is stored + * and where operations on the key are performed. + * See ::psa_key_location_t for more information. + * + * Volatile keys are automatically destroyed when the application instance + * terminates or on a power reset of the device. Persistent keys are + * preserved until the application explicitly destroys them or until an + * implementation-specific device management event occurs (for example, + * a factory reset). * - * Keys with a lifetime other than #PSA_KEY_LIFETIME_VOLATILE are said - * to be _persistent_. - * Persistent keys are preserved if the application or the system restarts. * Persistent keys have a key identifier of type #psa_key_id_t. + * This identifier remains valid throughout the lifetime of the key, + * even if the application instance that created the key terminates. * The application can call psa_open_key() to open a persistent key that * it created previously. + * + * This specification defines two basic lifetime values: + * - Keys with the lifetime #PSA_KEY_LIFETIME_VOLATILE are volatile. + * All implementations should support this lifetime. + * - Keys with the lifetime #PSA_KEY_LIFETIME_PERSISTENT are persistent. + * All implementations that have access to persistent storage with + * appropriate security guarantees should support this lifetime. */ typedef uint32_t psa_key_lifetime_t; +/** Encoding of key persistence levels. + * + * What distinguishes different persistence levels is what device management + * events may cause keys to be destroyed. _Volatile_ keys are destroyed + * by a power reset. Persistent keys may be destroyed by events such as + * a transfer of ownership or a factory reset. What management events + * actually affect persistent keys at different levels is outside the + * scope of the PSA Cryptography specification. + * + * This specification defines the following values of persistence levels: + * - \c 0 = #PSA_KEY_PERSISTENCE_VOLATILE: volatile key. + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * - \c 1 = #PSA_KEY_PERSISTENCE_DEFAULT: + * persistent key with a default lifetime. + * Implementations should support this value if they support persistent + * keys at all. + * Applications should use this value if they have no specific needs that + * are only met by implementation-specific features. + * - \c 2-127: persistent key with a PSA-specified lifetime. + * The PSA Cryptography specification does not define the meaning of these + * values, but other PSA specifications may do so. + * - \c 128-254: persistent key with a vendor-specified lifetime. + * No PSA specification will define the meaning of these values, so + * implementations may choose the meaning freely. + * As a guideline, higher persistence levels should cause a key to survive + * more management events than lower levels. + * - \c 255 = #PSA_KEY_PERSISTENCE_READ_ONLY: + * read-only or write-once key. + * A key with this persistence level cannot be destroyed. + * Implementations that support such keys may either allow their creation + * through the PSA Cryptography API, preferably only to applications with + * the appropriate privilege, or only expose keys created through + * implementation-specific means such as a factory ROM engraving process. + * Note that keys that are read-only due to policy restrictions + * rather than due to physical limitations should not have this + * persistence levels. + * + * \note Key persistence levels are 8-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the persistence as the lower 8 bits of a 32-bit value. + */ +typedef uint8_t psa_key_persistence_t; + +/** Encoding of key location indicators. + * + * If an implementation of this API can make calls to external + * cryptoprocessors such as secure elements, the location of a key + * indicates which secure element performs the operations on the key. + * If an implementation offers multiple physical locations for persistent + * storage, the location indicator reflects at which physical location + * the key is stored. + * + * This specification defines the following values of location indicators: + * - \c 0: primary local storage. + * All implementations should support this value. + * The primary local storage is typically the same storage area that + * contains the key metadata. + * - \c 1: primary secure element. + * Implementations should support this value if there is a secure element + * attached to the operating environment. + * As a guideline, secure elements may provide higher resistance against + * side channel and physical attacks than the primary local storage, but may + * have restrictions on supported key types, sizes, policies and operations + * and may have different performance characteristics. + * - \c 2-0x7fffff: other locations defined by a PSA specification. + * The PSA Cryptography does not currently assign any meaning to these + * locations, but future versions of this specification or other PSA + * specifications may do so. + * - \c 0x800000-0xffffff: vendor-defined locations. + * No PSA specification will assign a meaning to locations in this range. + * + * \note Key location indicators are 24-bit values. Key management + * interfaces operate on lifetimes (type ::psa_key_lifetime_t) which + * encode the location as the upper 24 bits of a 32-bit value. + */ +typedef uint32_t psa_key_location_t; + /** Encoding of identifiers of persistent keys. * * - Applications may freely choose key identifiers in the range diff --git a/include/psa/crypto_values.h b/include/psa/crypto_values.h index baaabff1e0d3..18b2d5ad1378 100644 --- a/include/psa/crypto_values.h +++ b/include/psa/crypto_values.h @@ -1539,12 +1539,20 @@ * @{ */ -/** A volatile key only exists as long as the handle to it is not closed. +/** The default lifetime for volatile keys. + * + * A volatile key only exists as long as the handle to it is not closed. * The key material is guaranteed to be erased on a power reset. + * + * A key with this lifetime is typically stored in the RAM area of the + * PSA Crypto subsystem. However this is an implementation choice. + * If an implementation stores data about the key in a non-volatile memory, + * it must release all the resources associated with the key and erase the + * key material if the calling application terminates. */ #define PSA_KEY_LIFETIME_VOLATILE ((psa_key_lifetime_t)0x00000000) -/** The default storage area for persistent keys. +/** The default lifetime for persistent keys. * * A persistent key remains in storage until it is explicitly destroyed or * until the corresponding storage area is wiped. This specification does @@ -1555,9 +1563,77 @@ * This lifetime value is the default storage area for the calling * application. Implementations may offer other storage areas designated * by other lifetime values as implementation-specific extensions. + * See ::psa_key_lifetime_t for more information. */ #define PSA_KEY_LIFETIME_PERSISTENT ((psa_key_lifetime_t)0x00000001) +/** The persistence level of volatile keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_VOLATILE ((psa_key_persistence_t)0x00) + +/** The default persistence level for persistent keys. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_DEFAULT ((psa_key_persistence_t)0x01) + +/** A persistence level indicating that a key is never destroyed. + * + * See ::psa_key_persistence_t for more information. + */ +#define PSA_KEY_PERSISTENCE_READ_ONLY ((psa_key_persistence_t)0xff) + +#define PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) \ + ((psa_key_persistence_t)((lifetime) & 0x000000ff)) + +#define PSA_KEY_LIFETIME_GET_LOCATION(lifetime) \ + ((psa_key_location_t)((lifetime) >> 8)) + +/** Whether a key lifetime indicates that the key is volatile. + * + * A volatile key is automatically destroyed by the implementation when + * the application instance terminates. In particular, a volatile key + * is automatically destroyed on a power reset of the device. + * + * A key that is not volatile is persistent. Persistent keys are + * preserved until the application explicitly destroys them or until an + * implementation-specific device management event occurs (for example, + * a factory reset). + * + * \param lifetime The lifetime value to query (value of type + * ::psa_key_lifetime_t). + * + * \return \c 1 if the key is volatile, otherwise \c 0. + */ +#define PSA_KEY_LIFETIME_IS_VOLATILE(lifetime) \ + (PSA_KEY_LIFETIME_GET_PERSISTENCE(lifetime) == \ + PSA_KEY_LIFETIME_PERSISTENCE_VOLATILE) + +/** Construct a lifetime from a persistence level and a location. + * + * \param persistence The persistence level + * (value of type ::psa_key_persistence_t). + * \param location The location indicator + * (value of type ::psa_key_location_t). + * + * \return The constructed lifetime value. + */ +#define PSA_KEY_LIFETIME_FROM_PERSISTENCE_AND_LOCATION(persistence, location) \ + ((location) << 8 | (persistence)) + +/** The local storage area for persistent keys. + * + * This storage area is available on all systems that can store persistent + * keys without delegating the storage to a third-party cryptoprocessor. + * + * See ::psa_key_location_t for more information. + */ +#define PSA_KEY_LOCATION_LOCAL_STORAGE ((psa_key_location_t)0x000000) + +#define PSA_KEY_LOCATION_VENDOR_FLAG ((psa_key_location_t)0x800000) + /** The minimum value for a key identifier chosen by the application. */ #define PSA_KEY_ID_USER_MIN ((psa_app_key_id_t)0x00000001) diff --git a/library/.gitignore b/library/.gitignore index 3a63a63a43fd..57e487cc3266 100644 --- a/library/.gitignore +++ b/library/.gitignore @@ -2,3 +2,4 @@ libmbed* *.sln *.vcxproj +/psa_crypto_driver_wrappers.c diff --git a/library/Makefile b/library/Makefile index dbdd3b679abd..48c2a71f4243 100644 --- a/library/Makefile +++ b/library/Makefile @@ -63,6 +63,8 @@ DLEXT = dylib endif endif +GENERATED_SOURCES = + OBJS_CRYPTO= \ aes.o \ aesni.o \ @@ -174,6 +176,14 @@ static: libmbedcrypto.a libmbedx509.a libmbedtls.a shared: libmbedcrypto.$(DLEXT) libmbedx509.$(DLEXT) libmbedtls.$(DLEXT) +# Declare the generated source unconditionally so that "make clean" always works +GENERATED_SOURCES += psa_crypto_driver_wrappers.c +ifneq ($(strip $(PSA_DRIVERS)), ) +psa_crypto_driver_wrappers.c: ../scripts/psa_crypto_driver_wrappers.py $(PSA_DRIVERS) + $(PYTHON) ../scripts/psa_crypto_driver_wrappers.py -o $@ $(PSA_DRIVERS) +OBJS_CRYPTO += psa_crypto_driver_wrappers.o +endif + # tls libmbedtls.a: $(OBJS_TLS) echo " AR $@" @@ -262,9 +272,9 @@ libmbedcrypto.dll: $(OBJS_CRYPTO) clean: ifndef WINDOWS rm -f *.o libmbed* - rm -f $(THIRDPARTY_CRYPTO_OBJECTS) + rm -f $(THIRDPARTY_CRYPTO_OBJECTS) $(GENERATED_SOURCES) else if exist *.o del /Q /F *.o if exist libmbed* del /Q /F libmbed* - del /Q /F del_errors_out_if_the_file_list_is_empty_but_not_if_a_file_does_not_exist $(subst /,\,$(THIRDPARTY_CRYPTO_OBJECTS)) + del /Q /F del_errors_out_if_the_file_list_is_empty_but_not_if_a_file_does_not_exist $(subst /,\,$(THIRDPARTY_CRYPTO_OBJECTS)) $(GENERATED_SOURCES) endif diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 733a2e46c4dc..fcc847e1b1c5 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -40,6 +40,10 @@ * stored keys. */ #include "psa_crypto_storage.h" +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) +#include "psa_crypto_driver_wrappers.h" +#endif + #include #include #include @@ -1242,18 +1246,14 @@ static psa_status_t psa_get_rsa_public_exponent( #endif /* MBEDTLS_RSA_C */ /** Retrieve all the publicly-accessible attributes of a key. + * + * \param slot An occupied key slot. + * \param attributes A freshly initialized or reset attribute structure. */ -psa_status_t psa_get_key_attributes( psa_key_handle_t handle, - psa_key_attributes_t *attributes ) +psa_status_t psa_get_key_slot_attributes( const psa_key_slot_t *slot, + psa_key_attributes_t *attributes ) { - psa_key_slot_t *slot; - psa_status_t status; - - psa_reset_key_attributes( attributes ); - - status = psa_get_key_from_slot( handle, &slot, 0, 0 ); - if( status != PSA_SUCCESS ) - return( status ); + psa_status_t status = PSA_SUCCESS; attributes->core = slot->attr; attributes->core.flags &= ( MBEDTLS_PSA_KA_MASK_EXTERNAL_ONLY | @@ -1290,6 +1290,23 @@ psa_status_t psa_get_key_attributes( psa_key_handle_t handle, return( status ); } +/** Retrieve all the publicly-accessible attributes of a key. + */ +psa_status_t psa_get_key_attributes( psa_key_handle_t handle, + psa_key_attributes_t *attributes ) +{ + psa_key_slot_t *slot; + psa_status_t status; + + psa_reset_key_attributes( attributes ); + + status = psa_get_key_from_slot( handle, &slot, 0, 0 ); + if( status != PSA_SUCCESS ) + return( status ); + + return( psa_get_key_slot_attributes( slot, attributes ) ); +} + #if defined(MBEDTLS_PSA_CRYPTO_SE_C) psa_status_t psa_get_key_slot_number( const psa_key_attributes_t *attributes, @@ -1321,11 +1338,11 @@ static int pk_write_pubkey_simple( mbedtls_pk_context *key, } #endif /* defined(MBEDTLS_RSA_C) || defined(MBEDTLS_ECP_C) */ -static psa_status_t psa_internal_export_key( const psa_key_slot_t *slot, - uint8_t *data, - size_t data_size, - size_t *data_length, - int export_public_key ) +psa_status_t psa_internal_export_key( const psa_key_slot_t *slot, + uint8_t *data, + size_t data_size, + size_t *data_length, + int export_public_key ) { #if defined(MBEDTLS_PSA_CRYPTO_SE_C) const psa_drv_se_t *drv; @@ -3462,6 +3479,16 @@ psa_status_t psa_sign_hash( psa_key_handle_t handle, goto exit; } +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + status = psa_driver_wrapper_sign_hash( slot, alg, + hash, hash_length, + signature, signature_size, + signature_length ); + if( status != PSA_ERROR_NOT_SUPPORTED ) + return( status ); + +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ + #if defined(MBEDTLS_PSA_CRYPTO_SE_C) if( psa_get_se_driver( slot->attr.lifetime, &drv, &drv_context ) ) { diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index edf3ab6031c2..ee6e191c0ceb 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -172,4 +172,40 @@ psa_status_t psa_import_key_into_slot( psa_key_slot_t *slot, const uint8_t *data, size_t data_length ); +/** Retrieve all the publicly-accessible attributes of a key. + * + * \param slot An occupied key slot. + * \param attributes A freshly initialized or reset attribute structure. + */ +psa_status_t psa_get_key_slot_attributes( const psa_key_slot_t *slot, + psa_key_attributes_t *attributes ); + +/** Export a key or public key. + * + * This function does not check the key policy and works even with + * keys that do not have #PSA_KEY_USAGE_EXPORT. + * + * \param slot Slot containing the key to export. + * \param[out] data Buffer where the key data is to be written. + * \param data_size Size of the \p data buffer in bytes. + * \param[out] data_length On success, the number of bytes + * that make up the key data. + * \param export_public_key If true and the key is a key pair, export the + * public part only. + * If true and the key is a public key, export it. + * If true and the ky is not asymmetric, return + * #PSA_ERROR_INVALID_ARGUMENT. + * If false, export the key, whatever its type. + * + * \retval #PSA_SUCCESS + * \retval #PSA_ERROR_INVALID_ARGUMENT + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * \retval #PSA_ERROR_BUFFER_TOO_SMALL + */ +psa_status_t psa_internal_export_key( const psa_key_slot_t *slot, + uint8_t *data, + size_t data_size, + size_t *data_length, + int export_public_key ); + #endif /* PSA_CRYPTO_CORE_H */ diff --git a/library/psa_crypto_driver_wrappers.h b/library/psa_crypto_driver_wrappers.h new file mode 100644 index 000000000000..53e2d24521f0 --- /dev/null +++ b/library/psa_crypto_driver_wrappers.h @@ -0,0 +1,35 @@ +/* + * PSA crypto driver wrapper function declarations + */ +/* Copyright (C) 2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef PSA_CRYPTO_DRIVER_WRAPPERS_H +#define PSA_CRYPTO_DRIVER_WRAPPERS_H + +#include "psa/crypto_driver_common.h" + +psa_status_t psa_driver_wrapper_sign_hash( psa_key_slot_t *slot, + psa_algorithm_t alg, + const uint8_t *hash, + size_t hash_length, + uint8_t *signature, + size_t signature_size, + size_t *signature_length ); + +#endif /* PSA_CRYPTO_DRIVER_WRAPPERS_H */ diff --git a/library/version_features.c b/library/version_features.c index d16ad1bacdbf..c1b880103f04 100644 --- a/library/version_features.c +++ b/library/version_features.c @@ -438,6 +438,9 @@ static const char * const features[] = { #if defined(MBEDTLS_PKCS1_V21) "MBEDTLS_PKCS1_V21", #endif /* MBEDTLS_PKCS1_V21 */ +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + "MBEDTLS_PSA_CRYPTO_DRIVERS", +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ #if defined(MBEDTLS_PSA_CRYPTO_SPM) "MBEDTLS_PSA_CRYPTO_SPM", #endif /* MBEDTLS_PSA_CRYPTO_SPM */ diff --git a/programs/test/query_config.c b/programs/test/query_config.c index bd3f638a7cb2..f49355d6a7b3 100644 --- a/programs/test/query_config.c +++ b/programs/test/query_config.c @@ -1210,6 +1210,14 @@ int query_config( const char *config ) } #endif /* MBEDTLS_PKCS1_V21 */ +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + if( strcmp( "MBEDTLS_PSA_CRYPTO_DRIVERS", config ) == 0 ) + { + MACRO_EXPANSION_TO_STR( MBEDTLS_PSA_CRYPTO_DRIVERS ); + return( 0 ); + } +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ + #if defined(MBEDTLS_PSA_CRYPTO_SPM) if( strcmp( "MBEDTLS_PSA_CRYPTO_SPM", config ) == 0 ) { diff --git a/scripts/psa_crypto_driver_description.py b/scripts/psa_crypto_driver_description.py new file mode 100644 index 000000000000..c0ccf597e46d --- /dev/null +++ b/scripts/psa_crypto_driver_description.py @@ -0,0 +1,110 @@ +"""PSA Crypto driver description parser. +""" + +import enum +import json +import jsonschema + + + +################################################################ +#### Data types #### +################################################################ + +class BadCapability(Exception): + pass + +class Type(enum.Enum): + TRANSPARENT = 0 + OPAQUE = 1 + +class Capability: + def __init__(self, functions, algorithms, key_types): + self.functions = frozenset(functions) + self.algorithms = None if algorithms is None else frozenset(algorithms) + self.key_types = None if key_types is None else frozenset(key_types) + +class Driver: + def __init__(self): + self.prefix = None + self.type = None + self.capabilities = [] + + def is_transparent(self): + return self.type == Type.TRANSPARENT + + def is_opaque(self): + return self.type == Type.OPAQUE + + def has_function(self, name): + return any(name in cap.functions for cap in self.capabilities) + + +################################################################ +#### JSON parsing #### +################################################################ + +def js_enum(*values): + return {'type': 'string', 'enum': list(values)} + +ALGORITHM_JSON_SCHEMA = { + 'type': 'string', + 'pattern': '^PSA_ALG_[0-9A-Z_]+', +} + +KEY_TYPE_JSON_SCHEMA = { + 'type': 'string', + 'pattern': '^PSA_ALG_[0-9A-Z_]+$', +} + +FUNCTION_JSON_SCHEMA = { + 'type': 'string', + 'pattern': '^[0-9a-z_]+$', +} + +CAPABILITY_JSON_SCHEMA = { + 'type': 'object', + 'properties': { + 'algorithms': {'type': 'array', 'items': ALGORITHM_JSON_SCHEMA}, + 'functions': {'type': 'array', 'items': FUNCTION_JSON_SCHEMA}, + 'key_types': {'type': 'array', 'items': KEY_TYPE_JSON_SCHEMA}, + }, + 'required': ['functions'], +} + +EMPTY_OBJECT_JSON_SCHEMA = {'type': 'object', 'maxProperties': 0} + +DRIVER_JSON_SCHEMA = { + 'type': 'object', + 'properties': { + 'capabilities': {'type': 'array', + 'items': {'oneOf': [CAPABILITY_JSON_SCHEMA, + EMPTY_OBJECT_JSON_SCHEMA]}}, + 'prefix': {'type': 'string', 'pattern': '^[a-z][0-9_a-z]*$'}, + 'type': js_enum('transparent', 'opaque'), + }, + 'required': ['capabilities', 'prefix', 'type'], +} + +def capability_from_json_data(data): + algorithms = data.get('algorithms', None) + functions = data['functions'] + key_types = data.get('key_types', None) + return Capability(functions, algorithms, key_types) + +def from_json_data(data): + jsonschema.validate(data, DRIVER_JSON_SCHEMA) + driver = Driver() + driver.prefix = data['prefix'] + driver.type = Type[data['type'].upper()] + driver.capabilities = [capability_from_json_data(elt) + for elt in data['capabilities'] + if elt != {}] + return driver + +def from_json(string): + return from_json_data(json.loads(string)) + +def from_json_file(filename): + with open(filename, 'r') as stream: + return from_json_data(json.load(stream)) diff --git a/scripts/psa_crypto_driver_header.py b/scripts/psa_crypto_driver_header.py new file mode 100755 index 000000000000..d45942ca9cff --- /dev/null +++ b/scripts/psa_crypto_driver_header.py @@ -0,0 +1,121 @@ +#!/usr/bin/env python3 + +"""Generate a header file for a PSA Cryptography driver. +""" + +# Copyright (C) 2020, ARM Limited, All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This file is part of mbed TLS (https://tls.mbed.org) + +import argparse +import os +import re +import sys + +import psa_crypto_driver_description + +HEADER_HEADER = """/* PSA Crypto driver "{description}" header file */ +/* Automatically generated by {generator_script}. Do not edit. */ + +#ifndef {guard_symbol} +#define {guard_symbol} + +#ifdef __cplusplus +extern "C" {{ +#endif + +""" + +HEADER_FOOTER = """ +#ifdef __cplusplus +}} +#endif + +#endif /* {guard_symbol} */ +""" + +class Function: + def __init__(self, name, parameters, return_type='psa_status_t'): + self.name = name + self.parameters = parameters + self.return_type = return_type + +def dict_by_name(things): + return dict((thing.name, thing) for thing in things) + +INBUF = ['const uint8_t *', 'size_t'] +OUTBUF = ['uint8_t *', 'size_t', 'size_t *'] +INKEY = ['const psa_key_attributes_t *', *INBUF] + +functions = dict_by_name([ + Function('init', []), + Function('import_key', [*INKEY, *OUTBUF]), + Function('destroy_key', [*INKEY]), + Function('sign_hash', [*INKEY, 'psa_algorithm_t', *INBUF, *OUTBUF]), +]) + +def write_function(out, prefix, name): + function = functions[name] + parameters = ', '.join(function.parameters) + if not parameters: + parameters = 'void' + out.write('{} {}_{}({});\n' + .format(function.return_type, prefix, name, parameters)) + +def write_header_content(out, drv): + for cap in drv.capabilities: + for function in cap.functions: + write_function(out, drv.prefix, function) + +def _write_header_file_common(drivers, description, filename): + guard_symbol = re.sub(r'[^0-9A-Z_]', '_', + os.path.basename(filename).upper()) + data = { + 'filename': filename, + 'generator_script': __file__, + 'guard_symbol': guard_symbol, + 'description': description, + } + with open(filename, 'w') as out: + out.write(HEADER_HEADER.format(**data)) + for drv in drivers: + write_header_content(out, drv) + out.write(HEADER_FOOTER.format(**data)) + +def write_header_file(drv, filename=None): + if filename is None: + filename = drv.prefix + '.h' + return _write_header_file_common([drv], drv.prefix, filename) + +def write_shared_header_file(drivers, filename): + description = '+'.join(drv.prefix for drv in drivers) + return _write_header_file_common(drivers, description, filename) + +def main(): + parser = argparse.ArgumentParser(description=__doc__, add_help=False) + parser.add_argument('--output-file', '-o', + help="Name of the header file to generate (default: {PREFIX}.h)") + parser.add_argument('input_file', metavar='INPUT', + help="driver description file in JSON format (default: stdin)") + options = parser.parse_args() + if options.input_file: + drv = psa_crypto_driver_description.from_json_file(options.input_file) + else: + drv = psa_crypto_driver_description.from_json(json.load(sys.stdin)) + write_header_file(drv, options.output_file) + +if __name__ == '__main__': + main() diff --git a/scripts/psa_crypto_driver_wrappers.py b/scripts/psa_crypto_driver_wrappers.py new file mode 100755 index 000000000000..b253136cd464 --- /dev/null +++ b/scripts/psa_crypto_driver_wrappers.py @@ -0,0 +1,241 @@ +#!/usr/bin/env python3 + +"""Generate wrapper code for PSA Cryptography drivers. +""" + +# Copyright (C) 2020, ARM Limited, All Rights Reserved +# SPDX-License-Identifier: Apache-2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# This file is part of mbed TLS (https://tls.mbed.org) + +import argparse +import glob +import os +import re + +import psa_crypto_driver_description +import psa_crypto_driver_header + +def inbuf(name): + return ['const uint8_t *' + name, + 'size_t ' + name + '_length',] + +def outbuf(name): + return ['uint8_t *' + name, + 'size_t ' + name + '_size', + 'size_t *' + name + '_length'] + +class Wrapper: + def __init__(self, name, parameters): + self.name = name + self.parameters = parameters + + @staticmethod + def lines(lines, indent): + return ''.join([' ' * indent + line for line in lines]) + + FUNCTION_TEMPLATE = """ +psa_status_t psa_driver_wrapper_{name}({parameters}) +{{ + psa_status_t status = PSA_ERROR_NOT_SUPPORTED; +{use_parameters}{body} return status; +}} + """.rstrip(' ') + + def body(self, drivers): + raise NotImplementedError + + PARAMETER_NAME_RE = re.compile(r'\w+\Z') + @classmethod + def name_from_declaration(cls, decl): + return re.search(cls.PARAMETER_NAME_RE, decl).group(0) + + @classmethod + def gen_call(cls, name, named_parameters, parameters_with_types): + args = named_parameters + args += map(cls.name_from_declaration, parameters_with_types) + return '{}({})'.format(name, ', '.join(args)) + + def write(self, out, transparent_drivers, opaque_drivers): + body = self.body(transparent_drivers, opaque_drivers) + parameters = ('\n ' + ',\n '.join(self.parameters) + if self.parameters + else 'void') + use_parameters = ''.join(' (void){};\n'.format(param) + for param in map(self.name_from_declaration, + self.parameters)) + out.write(self.FUNCTION_TEMPLATE.format(name=self.name, + parameters=parameters, + use_parameters=use_parameters, + body=body)) + +class OperationWrapper(Wrapper): + """Wrapper for a single-part operation.""" + + def __init__(self, name, other_parameters, asymmetric=0): + parameters = ([ + 'psa_key_slot_t *slot', + 'psa_algorithm_t alg', + ] + other_parameters) + super().__init__(name, parameters) + self.asymmetric = asymmetric # 0=symmetric 1=public_key 2=key_pair + + START = """ + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + status = psa_get_key_slot_attributes(slot, &attributes); + if (status != PSA_SUCCESS) return status; + psa_key_location_t location = PSA_KEY_LIFETIME_GET_LOCATION(slot->attr.lifetime); + + switch (location) { + """[1:].rstrip(' ') + + END = """ + default: + break; + } + + goto exit; +exit: + psa_reset_key_attributes(&attributes); + """[1:].rstrip(' ') + + @staticmethod + def gen_case(value, lines): + return '\n '.join([' case ' + value + ':', + *lines, 'break;']) + '\n\n' + + def driver_call(self, driver): + name = driver.prefix + '_' + self.name + if driver.is_transparent() and self.asymmetric: + key_pointer = 'exported_key' + key_size = 'exported_key_length' + else: + key_pointer = 'slot->data.raw.data' + key_size = 'slot->data.raw.bytes' + call = self.gen_call(name, + ['&attributes', key_pointer, key_size], + self.parameters[1:]) + return 'status = {};'.format(call) + + def transparent_body(self, transparent_drivers): + case_body = [] + label = 'exit' + if self.asymmetric == 1: # public key + case_body += ['uint8_t exported_key[PSA_EXPORT_PUBLIC_KEY_MAX_SIZE];', + 'size_t exported_key_length = 0;', + 'status = psa_internal_export_key(slot, exported_key, sizeof(exported_key), &exported_key_length, 1);', + 'if (status != PSA_SUCCESS) goto exit;'] + elif self.asymmetric: # key pair + case_body += ['uint8_t exported_key[PSA_EXPORT_KEY_PAIR_MAX_SIZE];', + 'size_t exported_key_length = 0;', + 'status = psa_internal_export_key(slot, exported_key, sizeof(exported_key), &exported_key_length, 0);', + 'if (status != PSA_SUCCESS) goto transparent_exit;'] + label = 'transparent_exit' + for driver in transparent_drivers: + case_body += [self.driver_call(driver), + 'if (status != PSA_ERROR_NOT_SUPPORTED) goto {};'.format(label)] + if self.asymmetric > 1: + return (['{'] + + [' ' + line for line in case_body] + + ['transparent_exit:', + ' if (exported_key != NULL) {', + ' mbedtls_platform_zeroize(exported_key, exported_key_length);', + ' }', + '}']) + else: + return case_body + + def body(self, transparent_drivers, opaque_drivers): + if not (transparent_drivers or opaque_drivers): + return '' + cases = [] + if transparent_drivers: + case_body = self.transparent_body(transparent_drivers) + cases.append(('PSA_KEY_LOCATION_LOCAL_STORAGE', case_body)) + for driver in opaque_drivers: + case_body = [self.driver_call(driver)] + cases.append((driver.location_symbol, case_body)) + return ''.join([self.START] + + [self.gen_case(*case) for case in cases] + + [self.END]) + +WRAPPERS = [ + OperationWrapper('sign_hash', + [*inbuf('hash'), *outbuf('signature')], + asymmetric=2), +] + +def write_location_constants(out, drivers): + """Generate location constants and write them out. + + Record the name of the location constant in the driver structure. + """ + value = 0 + for driver in drivers: + value += 1 + symbol = 'PSA_KEY_LOCATION_DRIVER_' + driver.prefix.upper() + driver.location_symbol = symbol + out.write('#define {} {}\n'.format(symbol, value)) + out.write('\n') + +HEADER_TEMPLATE = """ +/* Automatically generated by {script}. Do not edit. */ + +#include "psa/crypto_driver_common.h" +#include "psa_crypto_core.h" +#include "psa_crypto_driver_wrappers.h" + +"""[1:] + +FOOTER = """ +/* End of automatically generated file. */ +""" + +def write_prototypes(out, drivers): + for drv in drivers: + out.write('/* Declare functions from the {} driver */\n' + .format(drv.prefix)) + psa_crypto_driver_header.write_header_content(out, drv) + out.write('\n') + +def write_driver_wrappers(output_file, drivers): + transparent_drivers = [drv for drv in drivers if drv.is_transparent()] + opaque_drivers = [drv for drv in drivers if drv.is_opaque()] + with open(output_file, 'w') as out: + out.write(HEADER_TEMPLATE.format(script=__file__)) + write_prototypes(out, transparent_drivers + opaque_drivers) + write_location_constants(out, opaque_drivers) + for wrapper in WRAPPERS: + transparent = [drv for drv in transparent_drivers + if drv.has_function(wrapper.name)] + opaque = [drv for drv in opaque_drivers + if drv.has_function(wrapper.name)] + wrapper.write(out, transparent, opaque), + out.write(FOOTER) + +def main(): + parser = argparse.ArgumentParser(description=__doc__, add_help=False) + parser.add_argument('--output-file', '-o', + default='library/psa_crypto_driver_wrappers.c', + help="Name of the output file") + parser.add_argument('input_files', metavar='INPUT', nargs='*', + help="driver description files in JSON format") + options = parser.parse_args() + drivers = list(map(psa_crypto_driver_description.from_json_file, + options.input_files)) + write_driver_wrappers(options.output_file, drivers) + +if __name__ == '__main__': + main() diff --git a/tests/Makefile b/tests/Makefile index e74bf9548952..73f1a4cdd86c 100644 --- a/tests/Makefile +++ b/tests/Makefile @@ -77,6 +77,21 @@ all: $(BINARIES) $(DEP): $(MAKE) -C ../library +EXTRA_OBJECTS = + +ifneq ($(strip $(PSA_DRIVERS)), ) +PSA_DRIVERS_C = \ + drivers/signature.c \ + # This line is intentionally left blank + +EXTRA_OBJECTS += $(PSA_DRIVERS_C:.c=.o) +LOCAL_CFLAGS += -Idrivers + +drivers/%.o: drivers/%.c + echo " CC $<" + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ +endif + C_FILES := $(addsuffix .c,$(APPS)) # Wildcard target for test code generation: @@ -105,9 +120,9 @@ C_FILES := $(addsuffix .c,$(APPS)) -o . -$(BINARIES): %$(EXEXT): %.c $(DEP) +$(BINARIES): %$(EXEXT): %.c $(DEP) $(EXTRA_OBJECTS) echo " CC $<" - $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ + $(CC) $(LOCAL_CFLAGS) $(CFLAGS) $< $(EXTRA_OBJECTS) $(LOCAL_LDFLAGS) $(LDFLAGS) -o $@ # Some test suites require additional header files. $(filter test_suite_psa_crypto%, $(BINARIES)): psa_crypto_helpers.h @@ -118,10 +133,12 @@ $(addprefix embedded_,$(filter test_suite_psa_%, $(APPS))): embedded_%: TESTS/mb clean: ifndef WINDOWS rm -rf $(BINARIES) *.c *.datax TESTS + rm -rf drivers/*.o else if exist *.c del /Q /F *.c if exist *.exe del /Q /F *.exe if exist *.datax del /Q /F *.datax + if exist drivers\*.o del /Q /F drivers\*.o ifneq ($(wildcard TESTS/.*),) rmdir /Q /S TESTS endif diff --git a/tests/drivers/opaque_signature.json b/tests/drivers/opaque_signature.json new file mode 100644 index 000000000000..637f828220ef --- /dev/null +++ b/tests/drivers/opaque_signature.json @@ -0,0 +1,9 @@ +{ + "prefix": "test_opaque_signature", + "type": "opaque", + "capabilities": [ + {"functions": ["import_key", "destroy_key"]}, + {"functions": ["sign_hash"]}, + {} + ] +} diff --git a/tests/drivers/signature.c b/tests/drivers/signature.c new file mode 100644 index 000000000000..fdb48c3d3da5 --- /dev/null +++ b/tests/drivers/signature.c @@ -0,0 +1,170 @@ +/* + * Test driver with both transparent and opaque signature functions + */ +/* Copyright (C) 2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#if defined(MBEDTLS_PSA_CRYPTO_DRIVERS) + +#include + +#include "psa/crypto.h" +#include "mbedtls/ecp.h" + + +#include "mbedtls/md.h" +#include "mbedtls/ecdsa.h" + +/* If non-null, on success, copy this to the output. */ +void *test_driver_forced_output = NULL; +size_t test_driver_forced_output_length = 0; + +psa_status_t test_transparent_signature_sign_hash_status = PSA_ERROR_NOT_SUPPORTED; +unsigned long test_transparent_signature_sign_hash_hit = 0; +psa_status_t test_transparent_signature_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key, size_t key_length, + psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length ) +{ + ++test_transparent_signature_sign_hash_hit; + + if( test_transparent_signature_sign_hash_status != PSA_SUCCESS ) + return( test_transparent_signature_sign_hash_status ); + + if( test_driver_forced_output != NULL ) + { + if( test_driver_forced_output_length > signature_size ) + return( PSA_ERROR_BUFFER_TOO_SMALL ); + memcpy( signature, test_driver_forced_output, + test_driver_forced_output_length ); + *signature_length = test_driver_forced_output_length; + return( PSA_SUCCESS ); + } + + psa_status_t status = PSA_ERROR_NOT_SUPPORTED; + +#if defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECDSA_DETERMINISTIC) && \ + defined(MBEDTLS_SHA256_C) + if( alg != PSA_ALG_DETERMINISTIC_ECDSA( PSA_ALG_SHA_256 ) ) + return( PSA_ERROR_NOT_SUPPORTED ); + mbedtls_ecp_group_id grp_id; + switch( psa_get_key_type( attributes ) ) + { + case PSA_ECC_CURVE_SECP_R1: + switch( psa_get_key_bits( attributes ) ) + { + case 256: + grp_id = MBEDTLS_ECP_DP_SECP256R1; + break; + case 384: + grp_id = MBEDTLS_ECP_DP_SECP384R1; + break; + case 521: + grp_id = MBEDTLS_ECP_DP_SECP521R1; + break; + default: + return( PSA_ERROR_NOT_SUPPORTED ); + } + break; + default: + return( PSA_ERROR_NOT_SUPPORTED ); + } + + /* Beyond this point, the driver is actually doing the work of + * calculating the signature. */ + + status = PSA_ERROR_GENERIC_ERROR; + int ret = 0; + mbedtls_mpi r, s; + mbedtls_mpi_init( &r ); + mbedtls_mpi_init( &s ); + mbedtls_ecp_keypair ecp; + mbedtls_ecp_keypair_init( &ecp ); + size_t curve_bytes = PSA_BITS_TO_BYTES( ecp.grp.pbits ); + + MBEDTLS_MPI_CHK( mbedtls_ecp_group_load( &ecp.grp, grp_id ) ); + MBEDTLS_MPI_CHK( mbedtls_ecp_point_read_binary( &ecp.grp, &ecp.Q, + key, key_length ) ); + + /* Code adapted from psa_ecdsa_sign() in psa_crypto.c. */ + mbedtls_md_type_t md_alg = MBEDTLS_MD_SHA256; + if( signature_size < 2 * curve_bytes ) + { + status = PSA_ERROR_BUFFER_TOO_SMALL; + goto cleanup; + } + MBEDTLS_MPI_CHK( mbedtls_ecdsa_sign_det( &ecp.grp, &r, &s, &ecp.d, + hash, hash_length, md_alg ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &r, + signature, + curve_bytes ) ); + MBEDTLS_MPI_CHK( mbedtls_mpi_write_binary( &s, + signature + curve_bytes, + curve_bytes ) ); +cleanup: + /* There's no easy way to translate the error code except through a + * library function that's not exported. Use a debugger. */ + if( ret == 0 ) + status = PSA_SUCCESS; + mbedtls_mpi_free( &r ); + mbedtls_mpi_free( &s ); + mbedtls_ecp_keypair_free( &ecp ); + if( status == PSA_SUCCESS ) + *signature_length = 2 * curve_bytes; +#else /* defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECDSA_DETERMINISTIC) && \ + defined(MBEDTLS_SHA256_C) */ + (void) attributes; + (void) key; + (void) key_length; + (void) alg; + (void) hash; + (void) hash_length; +#endif /* defined(MBEDTLS_ECDSA_C) && defined(MBEDTLS_ECDSA_DETERMINISTIC) && \ + defined(MBEDTLS_SHA256_C) */ + + return( status ); +} + +psa_status_t test_opaque_signature_sign_hash( + const psa_key_attributes_t *attributes, + const uint8_t *key, size_t key_length, + psa_algorithm_t alg, + const uint8_t *hash, size_t hash_length, + uint8_t *signature, size_t signature_size, size_t *signature_length ) +{ + (void) attributes; + (void) key; + (void) key_length; + (void) alg; + (void) hash; + (void) hash_length; + (void) signature; + (void) signature_size; + (void) signature_length; + return( PSA_ERROR_NOT_SUPPORTED ); +} + +#endif /* MBEDTLS_PSA_CRYPTO_DRIVERS */ diff --git a/tests/drivers/signature.h b/tests/drivers/signature.h new file mode 100644 index 000000000000..e9adeaf0a57c --- /dev/null +++ b/tests/drivers/signature.h @@ -0,0 +1,39 @@ +/* + * Test driver with both transparent and opaque signature functions + */ +/* Copyright (C) 2020, ARM Limited, All Rights Reserved + * SPDX-License-Identifier: Apache-2.0 + * + * Licensed under the Apache License, Version 2.0 (the "License"); you may + * not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * This file is part of mbed TLS (https://tls.mbed.org) + */ + +#ifndef MBEDTLS_PSA_CRYPTO_TEST_DRIVERS_SIGNATURE_H +#define MBEDTLS_PSA_CRYPTO_TEST_DRIVERS_SIGNATURE_H + +#if !defined(MBEDTLS_CONFIG_FILE) +#include "mbedtls/config.h" +#else +#include MBEDTLS_CONFIG_FILE +#endif + +#include + +extern void *test_driver_forced_output; +extern size_t test_driver_forced_output_length; + +extern psa_status_t test_transparent_signature_sign_hash_status; +extern unsigned long test_transparent_signature_sign_hash_hit; + +#endif /* MBEDTLS_PSA_CRYPTO_TEST_DRIVERS_SIGNATURE_H */ diff --git a/tests/drivers/transparent_signature.json b/tests/drivers/transparent_signature.json new file mode 100644 index 000000000000..d92b3fe300d5 --- /dev/null +++ b/tests/drivers/transparent_signature.json @@ -0,0 +1,8 @@ +{ + "prefix": "test_transparent_signature", + "type": "transparent", + "capabilities": [ + {"functions": ["sign_hash"]}, + {} + ] +} diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 5b70caa225fd..312bf9c00243 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -1397,6 +1397,16 @@ component_test_se_full () { make test } +component_test_driver_signature () { + msg "build: MBEDTLS_PSA_CRYPTO_DRIVERS, signature" + scripts/config.py set MBEDTLS_PSA_CRYPTO_DRIVERS + # Can't build programs because they wouldn't have the driver code. + make PSA_DRIVERS="tests/drivers/opaque_signature.json tests/drivers/transparent_signature.json" CC=gcc CFLAGS="$ASAN_CFLAGS -O2" LDFLAGS="$ASAN_CFLAGS" lib tests SHELL='sh -x' + + msg "test: MBEDTLS_PSA_CRYPTO_DRIVERS, signature" + make test +} + component_test_make_shared () { msg "build/test: make shared" # ~ 40s make SHARED=1 all check diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index bc95f6fb0a99..2d3cc2e65f01 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -1022,6 +1022,10 @@ static int exercise_export_key( psa_key_handle_t handle, PSA_ASSERT( psa_export_key( handle, exported, exported_size, &exported_length ) ); + if( PSA_KEY_TYPE_IS_KEY_PAIR( psa_get_key_type( &attributes ) ) ) + TEST_ASSERT( exported_length <= PSA_EXPORT_KEY_PAIR_MAX_SIZE ); + if( PSA_KEY_TYPE_IS_PUBLIC_KEY( psa_get_key_type( &attributes ) ) ) + TEST_ASSERT( exported_length <= PSA_EXPORT_PUBLIC_KEY_MAX_SIZE ); ok = exported_key_sanity_check( psa_get_key_type( &attributes ), psa_get_key_bits( &attributes ), exported, exported_length ); @@ -1058,6 +1062,8 @@ static int exercise_export_public_key( psa_key_handle_t handle ) PSA_ASSERT( psa_export_public_key( handle, exported, exported_size, &exported_length ) ); + if( PSA_KEY_TYPE_IS_PUBLIC_KEY( psa_get_key_type( &attributes ) ) ) + TEST_ASSERT( exported_length <= PSA_EXPORT_PUBLIC_KEY_MAX_SIZE ); ok = exported_key_sanity_check( public_type, psa_get_key_bits( &attributes ), exported, exported_length ); @@ -1710,8 +1716,9 @@ void import_export_public_key( data_t *data, size_t bits; PSA_ASSERT( psa_get_key_attributes( handle, &attributes ) ); bits = psa_get_key_bits( &attributes ); - TEST_ASSERT( expected_public_key->len <= + TEST_ASSERT( exported_length <= PSA_KEY_EXPORT_MAX_SIZE( public_type, bits ) ); + TEST_ASSERT( exported_length <= PSA_EXPORT_PUBLIC_KEY_MAX_SIZE ); ASSERT_COMPARE( expected_public_key->x, expected_public_key->len, exported, exported_length ); } diff --git a/tests/suites/test_suite_psa_crypto_driver_wrappers.data b/tests/suites/test_suite_psa_crypto_driver_wrappers.data new file mode 100644 index 000000000000..fd20b1872b92 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_driver_wrappers.data @@ -0,0 +1,11 @@ +sign_hash through transparent driver: calculate in driver +ecdsa_sign:PSA_SUCCESS:0:PSA_SUCCESS + +sign_hash through transparent driver: fallback +ecdsa_sign:PSA_ERROR_NOT_SUPPORTED:0:PSA_SUCCESS + +sign_hash through transparent driver: error +ecdsa_sign:PSA_ERROR_GENERIC_ERROR:0:PSA_ERROR_GENERIC_ERROR + +sign_hash through transparent driver: fake +ecdsa_sign:PSA_SUCCESS:1:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto_driver_wrappers.function b/tests/suites/test_suite_psa_crypto_driver_wrappers.function new file mode 100644 index 000000000000..5f5554a628e2 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_driver_wrappers.function @@ -0,0 +1,104 @@ +/* BEGIN_HEADER */ +#include "psa_crypto_helpers.h" + +#include "drivers/signature.h" + +uint8_t test_secp256r1_key_data[32] = { + 0xab, 0x45, 0x43, 0x57, 0x12, 0x64, 0x9c, 0xb3, + 0x0b, 0xbd, 0xda, 0xc4, 0x91, 0x97, 0xee, 0xbf, + 0x27, 0x40, 0xff, 0xc7, 0xf8, 0x74, 0xd9, 0x24, + 0x4c, 0x34, 0x60, 0xf5, 0x4f, 0x32, 0x2d, 0x3a, +}; +uint8_t test_hash_32[32] = { + 0x9a, 0xc4, 0x33, 0x5b, 0x46, 0x9b, 0xbd, 0x79, + 0x14, 0x39, 0x24, 0x85, 0x04, 0xdd, 0x0d, 0x49, + 0xc7, 0x13, 0x49, 0xa2, 0x95, 0xfe, 0xe5, 0xa1, + 0xc6, 0x85, 0x07, 0xf4, 0x5a, 0x9e, 0x1c, 0x7b, +}; +uint8_t test_signature_hash_32_with_secp256r1[64] = { + 0x6a, 0x33, 0x99, 0xf6, 0x94, 0x21, 0xff, 0xe1, + 0x49, 0x03, 0x77, 0xad, 0xf2, 0xea, 0x1f, 0x11, + 0x7d, 0x81, 0xa6, 0x3c, 0xf5, 0xbf, 0x22, 0xe9, + 0x18, 0xd5, 0x11, 0x75, 0xeb, 0x25, 0x91, 0x51, + 0xce, 0x95, 0xd7, 0xc2, 0x6c, 0xc0, 0x4e, 0x25, + 0x50, 0x3e, 0x2f, 0x7a, 0x1e, 0xc3, 0x57, 0x3e, + 0x3c, 0x24, 0x12, 0x53, 0x4b, 0xb4, 0xa1, 0x9b, + 0x3a, 0x78, 0x11, 0x74, 0x2f, 0x49, 0xf5, 0x0f, +}; + +uint8_t test_fake_output[] = "INJECTED OUTPUT"; + +typedef enum +{ + EXPECT_FAILURE, + EXPECT_CORRECT_OUTPUT, + EXPECT_FAKE_OUTPUT, +} expected_output_t; + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PSA_CRYPTO_C:MBEDTLS_PSA_CRYPTO_DRIVERS + * END_DEPENDENCIES + */ + +/* BEGIN_CASE depends_on:MBEDTLS_ECDSA_C:MBEDTLS_ECDSA_DETERMINISTIC:MBEDTLS_ECP_DP_SECP256R1_ENABLED:MBEDTLS_SHA256_C */ +void ecdsa_sign( int force_status_arg, + int fake_output, + int expected_status_arg ) +{ + psa_status_t force_status = force_status_arg; + psa_status_t expected_status = expected_status_arg; + psa_key_handle_t handle = 0; + psa_key_attributes_t attributes = PSA_KEY_ATTRIBUTES_INIT; + psa_algorithm_t alg = PSA_ALG_DETERMINISTIC_ECDSA( PSA_ALG_SHA_256 ); + uint8_t signature[64]; + size_t signature_length = 0xdeadbeef; + const uint8_t *expected_output; + size_t expected_output_length; + psa_status_t actual_status; + + PSA_ASSERT( psa_crypto_init( ) ); + psa_set_key_type( &attributes, + PSA_KEY_TYPE_ECC_KEY_PAIR( PSA_ECC_CURVE_SECP_R1 ) ); + psa_set_key_usage_flags( &attributes, PSA_KEY_USAGE_SIGN_HASH ); + psa_set_key_algorithm( &attributes, alg ); + psa_import_key( &attributes, + test_secp256r1_key_data, sizeof( test_secp256r1_key_data ), + &handle ); + + test_transparent_signature_sign_hash_hit = 0; + test_transparent_signature_sign_hash_status = force_status; + if( fake_output ) + { + expected_output = test_driver_forced_output = test_fake_output; + expected_output_length = test_driver_forced_output_length = + sizeof( test_fake_output ); + } + else + { + expected_output = test_signature_hash_32_with_secp256r1; + expected_output_length = sizeof( test_signature_hash_32_with_secp256r1 ); + } + + actual_status = psa_sign_hash( handle, alg, + test_hash_32, sizeof( test_hash_32 ), + signature, sizeof( signature ), + &signature_length ); + TEST_EQUAL( actual_status, expected_status ); + if( expected_status == PSA_SUCCESS ) + { + ASSERT_COMPARE( signature, signature_length, + expected_output, expected_output_length ); + } + TEST_EQUAL( test_transparent_signature_sign_hash_hit, 1 ); + +exit: + psa_reset_key_attributes( &attributes ); + psa_destroy_key( handle ); + PSA_DONE( ); + test_transparent_signature_sign_hash_status = PSA_ERROR_NOT_SUPPORTED; + test_driver_forced_output = NULL; + test_driver_forced_output_length = 0; +} +/* END_CASE */ diff --git a/visualc/VS2010/mbedTLS.vcxproj b/visualc/VS2010/mbedTLS.vcxproj index 07e046a5229a..19417981b83e 100644 --- a/visualc/VS2010/mbedTLS.vcxproj +++ b/visualc/VS2010/mbedTLS.vcxproj @@ -231,6 +231,7 @@ +