From bb5d907aa9b154ba2129b72dad8f54b228e54b6c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 11 Oct 2023 20:46:26 +0200 Subject: [PATCH 001/184] Automatically pick up all Markdown files Assume GNU make. We already do with the toplevel makefile. Signed-off-by: Gilles Peskine --- docs/architecture/Makefile | 15 +-------------- 1 file changed, 1 insertion(+), 14 deletions(-) diff --git a/docs/architecture/Makefile b/docs/architecture/Makefile index 6252ab0f91..5bee504c29 100644 --- a/docs/architecture/Makefile +++ b/docs/architecture/Makefile @@ -2,20 +2,7 @@ PANDOC = pandoc default: all -all_markdown = \ - alternative-implementations.md \ - mbed-crypto-storage-specification.md \ - psa-crypto-implementation-structure.md \ - psa-migration/psa-limitations.md \ - psa-migration/strategy.md \ - psa-migration/tasks-g2.md \ - psa-migration/testing.md \ - testing/driver-interface-test-strategy.md \ - testing/invasive-testing.md \ - testing/psa-storage-format-testing.md \ - testing/test-framework.md \ - tls13-support.md \ - # This line is intentionally left blank +all_markdown = $(wildcard *.md */*.md) html: $(all_markdown:.md=.html) pdf: $(all_markdown:.md=.pdf) From f7806ca7820ad4b24535778442973637ee78b738 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 12 Oct 2023 16:00:11 +0200 Subject: [PATCH 002/184] Analyze requirements for protection of arguments in shared memory Propose a dual-approach strategy where some buffers are copied and others can remain shared. Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 208 +++++++++++++++++++++++++ 1 file changed, 208 insertions(+) create mode 100644 docs/architecture/psa-shared-memory.md diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md new file mode 100644 index 0000000000..a7fe013fca --- /dev/null +++ b/docs/architecture/psa-shared-memory.md @@ -0,0 +1,208 @@ +PSA API functions and shared memory +=================================== + +## Introduction + +This document discusses the security architecture of systems where PSA API functions might receive arguments that are in memory that is shared with an untrusted process. On such systems, the untrusted process might access a shared memory buffer while the cryptography library is using it, and thus cause unexpected behavior in the cryptography code. + +### Core assumptions + +We assume the following scope limitations: + +* Only PSA Crypto API functions are in scope (including Mbed TLS extensions to the official API specification). Legacy crypto, X.509, TLS, or any other function which is not called `psa_xxx` is out of scope. +* We only consider [input buffers](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#input-buffer-sizes) and [output buffers](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#output-buffer-sizes). Any other data is assumed to be in non-shared memory. + +## System architecture discussion + +### Architecture overview + +We consider a system that memory separation between partitions: a partition can't access another partition's memory directly. Partitions are meant to be isolated from each other: a partition may only affect the integrity of another partition via well-defined system interfaces. For example, this can be a Unix/POSIX-like system that isolates processes, or isolation between the secure world and the non-secure world relying on a mechanism such as TrustZone, or isolation between secure-world applications on such a system. + +More precisely, we consider such a system where our PSA Crypto implementation is running inside one partition, called the **crypto service**. The crypto service receives remote procedure calls from other partitions, validates their arguments (e.g. validation of key identifier ownership), and calls a PSA Crypto API function. This document is concerned with environments where the arguments passed to a PSA Crypto API function may be in shared memory (as opposed to environments where the inputs are always copied into memory that is solely accessible by the crypto service before calling the API function, and likewise with output buffers after the function returns). + +When the data is accessible to another partition, there is a risk that this other partition will access it while the crypto implementation is working. Although this could be prevented by suspending the whole system while crypto is working, such a limitation is rarely desirable and most systems don't offer a way to do it. (Even systems that hav absolute thread priorities, and where crypto has a higher priority than any untrusted partition, may be vulnerable due to having multiple cores or asynchronous data transfers with peripherals.) + +### Risks and vulnerabilities + +We consider a security architecture with two or three entities: + +* a crypto service, which offers PSA crypto API calls over RPC (remote procedure call) using shared memory for some input or output arguments; +* a client of the crypto service, which makes a RPC to the crypto service; +* in some scenarios, a client of the client, which makes a RPC to the crypto client which re-shares the memory with the crypto service. + +#### Read-read inconsistency + +If an input argument is in shared memory, there is a risk of a **read-read inconsistency**: + +1. The crypto code reads part of the input and validates it, or injects it into a calculation. +2. The client (or client's client) modifies the input. +3. The crypto code reads the same part again, and performs an action which would be impossible if the input had had the same value all along. + +Vulnerability example: suppose the input contains data with a type-length-value or length-value encoding (for example, importing an RSA key). The crypto code reads the length field and checks that it fits within the buffer. (This could be the length of the overall data, or the length of an embedded field) Later, the crypto code reads the length again and uses it without validation. A malicious client can modify the length field in the shared memory between the two reads and thus cause a buffer overread on the second read. + +#### Write-read inconsistency + +If an output argument is in shared memory, there is a risk of a **write-read inconsistency**: + +1. The crypto code writes some intermediate data into the output buffer. +2. The client (or client's client) modifies the intermediate data. +3. The crypto code reads the intermediata data back and continues the calculation, leading to an outcome that would not be possible if the intermediate data had not been modified. + +Vulnerability example: suppose that an RSA signature function works by formatting the data in place in the output buffer, then applying the RSA private-key operation in place. (This is how `mbedtls_rsa_pkcs1_sign` works.) A malicious client may write badly formatted data into the buffer, so that the private-key operation is not a valid signature (e.g. it could be a decryption), violating the RSA key's usage policy. + +Vulnerability example with chained calls: we consider the same RSA signature operation as before. In this example, we additionally assume that the data to sign comes from an attestation application which signs some data on behalf of a final client: the key and the data to sign are under the attestation application's control, and the final client must not be able to obtain arbitrary signatures. The final client shares an output buffer for the signature with the attestation application, and the attestation application re-shares this buffer with the crypto service. A malicious final client can modify the intermediate data and thus sign arbitrary data. + +#### Write-write disclosure + +If an output argument is in shared memory, there is a risk of a **write-write disclosure**: + +1. The crypto code writes some intermediate data into the output buffer. This intermediate data must remain confidential. +2. The client (or client's client) reads the intermediate data. +3. The crypto code overwrites the intermediate data. + +Vulnerability example with chained calls: we consider a provisioning application that provides a data encryption service on behalf of multiple clients, using a single shared key. Clients are not allowed to access each other's data. The provisioning application isolates clients by including the client identity in the associated data. Suppose that an AEAD decryption function processes the ciphertext incrementally by simultaneously writing the plaintext to the output buffer and calculating the tag. (This is how AEAD decryption usually works.) At the end, if the tag is wrong, the decryption function wipes the output buffer. Assume that the output buffer for the plaintext is shared from the client to the provisioning application, which re-shares it with the crypto service. A malicious client can read another client (the victim)'s encrypted data by passing the ciphertext to the provisioning application, which will attempt to decrypt it with associated data identifying the requesting client. Although the operation will fail beacuse the tag is wrong, the malicious client still reads the victim plaintext. + +### Possible countermeasures + +In this section, we briefly discuss generic countermeasures. + +#### Copying + +Copying is a valid countermeasure. It is conceptually simple. However, it is often unattractive because it requires additional memory and time. + +Note that although copying is very easy to write into a program, there is a risk that a compiler (especially with whole-program optimization) may optimize the copy away, if it does not understand that copies between shared memory and non-shared memory are semantically meaningful. + +Example: the PSA Firmware Framework 1.0 forbids shared memory between partitions. This restriction is lifted in version 1.1 due to concerns over RAM usage. + +#### Careful accesses + +The following rules guarantee that shared memory cannot result in a security violation: + +* Never read the same input twice at the same index. +* Never read back from an output. +* Once potentially confidential data has been written to an output, it may not be overwritten. (This rule is more complex to allow writing non-confidential data first, for example to pre-initialize an output to zero for robustness.) + +These rules are very difficult to enforce. + +Example: these are the rules that a GlobalPlatform TEE Trusted Application (application running on the secure side of TrustZone on Cortex-A) must follow. + +## Protection requirements + +### Responsibility for protection + +A call to a crypto service to perform a crypto operation involes the following components: + +1. The remote procedure call framework provided by the operating system. +2. The code of the crypto service. +3. The code of the PSA Crypto dispatch layer (also known as the core), which is provided by Mbed TLS. +4. The implementation of the cryptographic mechanism, which may be provided by Mbed TLS or by a third-party driver. + +The [PSA Crypto API specification](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#stability-of-parameters) puts the responsibility for protection on the implementation of the PSA Crypto API, i.e. (3) or (4). + +> In an environment with multiple threads or with shared memory, the implementation carefully accesses non-overlapping buffer parameters in order to prevent any security risk resulting from the content of the buffer being modified or observed during the execution of the function. (...) + +In Mbed TLS 2.x and 3.x up to and including 3.5.0, there is no defense against buffers in shared memory. The responsibility shifts to (1) or (2), but this is not documented. + +In the remainder of this chapter, we will discuss how to implement this high-level requirement where it belongs: inside the implementation of the PSA Crypto API. Note that this allows two possible levels: in the dispatch layer (independently of the implementation of each mechanism) or in the driver (specific to each implementation). + +#### Protection in the dispatch layer + +The dispatch layer has no control over how the driver layer will access buffers. Therefore the only possible protection at this layer method is to ensure that drivers have no access to shared memory. This means that any buffer located in shared memory must be copied into or out of a buffer in memory owned by the crypto service (heap or stack). This adds inefficiency, mostly in terms of RAM usage. + +For buffers with a small static size limit, this is something we often do for convenience, especially with output buffers. However, as of Mbed TLS 3.5.0, it is not done systematically. + +It is ok to skip the copy if it is known for sure that a buffer is not in shared memory. However, the location of the buffer is not under the control of Mbed TLS. This means skipping the copy would have to be a compile-time or run-time option which has to be set by the application using Mbed TLS. This is both an additional maintenance cost (more code to analyze, more testing burden), and a residual security risk in case the party who is responsible for setting this option does not set it correctly. As a consequence, Mbed TLS will not offer this configurability unless there is a compelling argument. + +#### Protection in the driver layer + +Putting the responsibility for protection in the driver layer increases the overall amount of work since there are more driver implementations than dispatch implementations. (This is true even inside Mbed TLS: almost all API functions have multiple underlying implementations, one for each algorithm.) It also increases the risk to the ecosystem since some drivers might not protect correctly. Therefore having drivers be responsible for protection is only a good choice if there is a definite benefit to it, compared to allocating an internal buffer and copying. An expected benefit in some cases is that there are practical protection methods other than copying. + +Some cryptographic mechanisms are naturally implemented by processing the input in a single pass, with a low risk of ever reading the same byte twice, and by writing the final output directly into the output buffer. For such mechanism, it is sensible to mandate that drivers respect these rules. + +In the next section, we will analyze how susceptible various cryptographic mechanisms are to shared memory vulnerabilities. + +### Susceptibility of different mechanisms + +#### Operations involving small buffers + +For operations involving **small buffers**, the cost of copying is low. For many of those, the risk of not copying is high: + +* Any parsing of formatted data has a high risk of [read-read inconsistency](#read-read-inconsistency). +* An internal review shows that for RSA operations, it is natural for an implementation to have a [write-read inconsistency](#write-read-inconsistency) or a [write-write disclosure](#write-write-disclosure). + +Note that in this context, a “small buffer” is one with a size limit that is known at compile time, and small enough that copying the data is not prohibitive. For example, an RSA key fits in a small buffer. A hash input is not a small buffer, even if it happens to be only a few bytes long in one particular call. + +The following buffers are considered small buffers: + +* Any input or output from asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export. +* The output of a hash or MAC operation. +* Cooked key derivation output. + +**Design decision: the dispatch layer shall copy all small buffers**. + +#### Symmetric cryptography inputs + +Message inputs to hash, MAC, cipher (plaintext or ciphertext), AEAD (associated data, plaintext, ciphertext) and key derivation operations are at a low risk of [read-read inconsistency](#read-read-inconsistency) because they are unformatted data, and for all specified algorithms, it is natural to process the input one byte at a time. + +**Design decision: require symmetric cryptography drivers to read their input without a risk of read-read inconsistency**. + +TODO: what about IV/nonce inputs? They are typically small, but don't necessarily have a static size limit (e.g. GCM recommends a 12-byte nonce, but also allows large nonces). + +#### Key derivation outputs + +Key derivation typically emits its output as a stream, with no error condition detected after setup other than operational failures (e.g. communication failure with an accelerator) or running out of data to emit (which can easily be checked before emitting any data, since the data size is known in advance). + +(Note that this is about raw byte output, not about cooked key derivation, i.e. deriving a structured key, which is considered a [small buffer](#operations-involving-small-buffers).) + +**Design decision: require key derivation drivers to emit their output without reading back from the output buffer**. + +#### Cipher and AEAD outputs + +AEAD decryption is at risk of [write-write disclosure](#write-write-disclosure) when the tag does not match. + +Cipher and AEAD outputs are at risk of [write-write disclosure](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). + +**Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD outputs**. + +## Design of shared memory protection + +This section explains how Mbed TLS implements the shared memory protection stragegy summarized below. + +### Shared memory protection strategy + +* The core (dispatch layer) shall make a copy of the following buffers, so that drivers do not receive arguments that are in shared memory: + * Any input or output from asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export. + * The output of a hash or MAC operation. + * Cooked key derivation output. + +* A document shall explain the requirements on drivers for arguments whose access needs to be protected: + * Hash and MAC input. + * Cipher/AEAD IV/nonce (to be confirmed). + * Key derivation input (excluding key agreement). + * Raw key derivation output (excluding cooked key derivation output). + +* The built-in implementations of cryptographic mechanisms with arguments whose access needs to be protected shall protect those arguments. + +Justification: see “[Susceptibility of different mechanisms](susceptibility-of-different-mechanisms)”. + +### Implementation of copying + +Copy what needs copying. This seems straightforward. + +### Validation of copying + +TODO + +Proposed general idea: have tests where the test code calling API functions allocates memory in a certain pool, and code in the library allocates memory in a different pool. Test drivers check that needs-copying arguments are within the library pool, not within the test pool. + +### Shared memory protection requirements + +TODO: write document and reference it here. + +### Validation of protection for built-in mechanisms + +TODO + +## Analysis of argument protection in built-in mechanisms + +TODO From 8daedaeac987c278cc6c52b401d1465d2f709358 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 18:47:29 +0200 Subject: [PATCH 003/184] Fix typos and copypasta Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index a7fe013fca..6e3a44ee02 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -16,11 +16,11 @@ We assume the following scope limitations: ### Architecture overview -We consider a system that memory separation between partitions: a partition can't access another partition's memory directly. Partitions are meant to be isolated from each other: a partition may only affect the integrity of another partition via well-defined system interfaces. For example, this can be a Unix/POSIX-like system that isolates processes, or isolation between the secure world and the non-secure world relying on a mechanism such as TrustZone, or isolation between secure-world applications on such a system. +We consider a system that has memory separation between partitions: a partition can't access another partition's memory directly. Partitions are meant to be isolated from each other: a partition may only affect the integrity of another partition via well-defined system interfaces. For example, this can be a Unix/POSIX-like system that isolates processes, or isolation between the secure world and the non-secure world relying on a mechanism such as TrustZone, or isolation between secure-world applications on such a system. More precisely, we consider such a system where our PSA Crypto implementation is running inside one partition, called the **crypto service**. The crypto service receives remote procedure calls from other partitions, validates their arguments (e.g. validation of key identifier ownership), and calls a PSA Crypto API function. This document is concerned with environments where the arguments passed to a PSA Crypto API function may be in shared memory (as opposed to environments where the inputs are always copied into memory that is solely accessible by the crypto service before calling the API function, and likewise with output buffers after the function returns). -When the data is accessible to another partition, there is a risk that this other partition will access it while the crypto implementation is working. Although this could be prevented by suspending the whole system while crypto is working, such a limitation is rarely desirable and most systems don't offer a way to do it. (Even systems that hav absolute thread priorities, and where crypto has a higher priority than any untrusted partition, may be vulnerable due to having multiple cores or asynchronous data transfers with peripherals.) +When the data is accessible to another partition, there is a risk that this other partition will access it while the crypto implementation is working. Although this could be prevented by suspending the whole system while crypto is working, such a limitation is rarely desirable and most systems don't offer a way to do it. (Even systems that have absolute thread priorities, and where crypto has a higher priority than any untrusted partition, may be vulnerable due to having multiple cores or asynchronous data transfers with peripherals.) ### Risks and vulnerabilities @@ -46,7 +46,7 @@ If an output argument is in shared memory, there is a risk of a **write-read inc 1. The crypto code writes some intermediate data into the output buffer. 2. The client (or client's client) modifies the intermediate data. -3. The crypto code reads the intermediata data back and continues the calculation, leading to an outcome that would not be possible if the intermediate data had not been modified. +3. The crypto code reads the intermediate data back and continues the calculation, leading to an outcome that would not be possible if the intermediate data had not been modified. Vulnerability example: suppose that an RSA signature function works by formatting the data in place in the output buffer, then applying the RSA private-key operation in place. (This is how `mbedtls_rsa_pkcs1_sign` works.) A malicious client may write badly formatted data into the buffer, so that the private-key operation is not a valid signature (e.g. it could be a decryption), violating the RSA key's usage policy. @@ -90,7 +90,7 @@ Example: these are the rules that a GlobalPlatform TEE Trusted Application (appl ### Responsibility for protection -A call to a crypto service to perform a crypto operation involes the following components: +A call to a crypto service to perform a crypto operation involves the following components: 1. The remote procedure call framework provided by the operating system. 2. The code of the crypto service. @@ -160,13 +160,13 @@ Key derivation typically emits its output as a stream, with no error condition d AEAD decryption is at risk of [write-write disclosure](#write-write-disclosure) when the tag does not match. -Cipher and AEAD outputs are at risk of [write-write disclosure](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). +Cipher and AEAD outputs are at risk of [write-read-inconsistency](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). **Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD outputs**. ## Design of shared memory protection -This section explains how Mbed TLS implements the shared memory protection stragegy summarized below. +This section explains how Mbed TLS implements the shared memory protection strategy summarized below. ### Shared memory protection strategy From 60c453ee725311cacc0d25f7651d81ad57f9f620 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 19:07:56 +0200 Subject: [PATCH 004/184] Expand explanations of the vulnerabilities Add a few more examples. Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 6e3a44ee02..61d37dbff1 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -18,10 +18,12 @@ We assume the following scope limitations: We consider a system that has memory separation between partitions: a partition can't access another partition's memory directly. Partitions are meant to be isolated from each other: a partition may only affect the integrity of another partition via well-defined system interfaces. For example, this can be a Unix/POSIX-like system that isolates processes, or isolation between the secure world and the non-secure world relying on a mechanism such as TrustZone, or isolation between secure-world applications on such a system. -More precisely, we consider such a system where our PSA Crypto implementation is running inside one partition, called the **crypto service**. The crypto service receives remote procedure calls from other partitions, validates their arguments (e.g. validation of key identifier ownership), and calls a PSA Crypto API function. This document is concerned with environments where the arguments passed to a PSA Crypto API function may be in shared memory (as opposed to environments where the inputs are always copied into memory that is solely accessible by the crypto service before calling the API function, and likewise with output buffers after the function returns). +More precisely, we consider such a system where our PSA Crypto implementation is running inside one partition, called the **crypto service**. The crypto service receives remote procedure calls (RPC) from other partitions, validates their arguments (e.g. validation of key identifier ownership), and calls a PSA Crypto API function. This document is concerned with environments where the arguments passed to a PSA Crypto API function may be in shared memory (as opposed to environments where the inputs are always copied into memory that is solely accessible by the crypto service before calling the API function, and likewise with output buffers after the function returns). When the data is accessible to another partition, there is a risk that this other partition will access it while the crypto implementation is working. Although this could be prevented by suspending the whole system while crypto is working, such a limitation is rarely desirable and most systems don't offer a way to do it. (Even systems that have absolute thread priorities, and where crypto has a higher priority than any untrusted partition, may be vulnerable due to having multiple cores or asynchronous data transfers with peripherals.) +The crypto service must guarantee that it behaves as if the rest of the world was suspended while it is executed. A behavior that is only possible if an untrusted entity accesses a buffer while the crypto service is processing the data is a security violation. + ### Risks and vulnerabilities We consider a security architecture with two or three entities: @@ -30,6 +32,8 @@ We consider a security architecture with two or three entities: * a client of the crypto service, which makes a RPC to the crypto service; * in some scenarios, a client of the client, which makes a RPC to the crypto client which re-shares the memory with the crypto service. +The behavior of RPC is defined for in terms of values of inputs and outputs. This models an ideal world where the content of input and output buffers is not accessible outside the crypto service while it is processing an RPC. It is a security violation if the crypto service behaves in a way that cannot be achieved by setting the inputs before the RPC call, and reading the outputs after the RPC call is finished. + #### Read-read inconsistency If an input argument is in shared memory, there is a risk of a **read-read inconsistency**: @@ -38,7 +42,12 @@ If an input argument is in shared memory, there is a risk of a **read-read incon 2. The client (or client's client) modifies the input. 3. The crypto code reads the same part again, and performs an action which would be impossible if the input had had the same value all along. -Vulnerability example: suppose the input contains data with a type-length-value or length-value encoding (for example, importing an RSA key). The crypto code reads the length field and checks that it fits within the buffer. (This could be the length of the overall data, or the length of an embedded field) Later, the crypto code reads the length again and uses it without validation. A malicious client can modify the length field in the shared memory between the two reads and thus cause a buffer overread on the second read. +Vulnerability example (parsing): suppose the input contains data with a type-length-value or length-value encoding (for example, importing an RSA key). The crypto code reads the length field and checks that it fits within the buffer. (This could be the length of the overall data, or the length of an embedded field) Later, the crypto code reads the length again and uses it without validation. A malicious client can modify the length field in the shared memory between the two reads and thus cause a buffer overread on the second read. + +Vulnerability example (dual processing): consider an RPC to perform authenticated encryption, using a mechanism with an encrypt-and-MAC structure. The authenticated encryption implementation separately calculates the ciphertext and the MAC from the plantext. A client sets the plaintext input to `"PPPP"`, then starts the RPC call, then changes the input buffer to `"QQQQ"` while the crypto service is working. + +* Any of `enc("PPPP")+mac("PPPP")`, `enc("PPQQ")+mac("PPQQ")` or `enc("QQQQ")+mac("QQQQ")` are valid outputs: they are outputs that can be produced by this authenticated encryption RPC. +* If the authenticated encryption calculates the ciphertext before the client changes the output buffer and calculates the MAC after that change, reading the input buffer again each time, the output will be `enc("PPPP")+mac("QQQQ")`. There is no input that can lead to this output, hence this behavior violates the security guarantees of the crypto service. #### Write-read inconsistency @@ -60,7 +69,9 @@ If an output argument is in shared memory, there is a risk of a **write-write di 2. The client (or client's client) reads the intermediate data. 3. The crypto code overwrites the intermediate data. -Vulnerability example with chained calls: we consider a provisioning application that provides a data encryption service on behalf of multiple clients, using a single shared key. Clients are not allowed to access each other's data. The provisioning application isolates clients by including the client identity in the associated data. Suppose that an AEAD decryption function processes the ciphertext incrementally by simultaneously writing the plaintext to the output buffer and calculating the tag. (This is how AEAD decryption usually works.) At the end, if the tag is wrong, the decryption function wipes the output buffer. Assume that the output buffer for the plaintext is shared from the client to the provisioning application, which re-shares it with the crypto service. A malicious client can read another client (the victim)'s encrypted data by passing the ciphertext to the provisioning application, which will attempt to decrypt it with associated data identifying the requesting client. Although the operation will fail beacuse the tag is wrong, the malicious client still reads the victim plaintext. +Vulnerability example with chained calls (temporary exposure): an application encrypts some data, and lets its clients store the ciphertext. Clients may not have access to the plaintext. To save memory, when it calls the crypto service, it passes an output buffer that is in the final client's memory. Suppose the encryption mechanism works by copying its input to the output buffer then encrypting in place (for example, to simplify considerations related to overlap, or because the implementation relies on a low-level API that works in place). In this scenario, the plaintext is exposed to the final client while the encryption in progress, which violates the confidentiality of the plaintext. + +Vulnerability example with chained calls (backtrack): we consider a provisioning application that provides a data encryption service on behalf of multiple clients, using a single shared key. Clients are not allowed to access each other's data. The provisioning application isolates clients by including the client identity in the associated data. Suppose that an AEAD decryption function processes the ciphertext incrementally by simultaneously writing the plaintext to the output buffer and calculating the tag. (This is how AEAD decryption usually works.) At the end, if the tag is wrong, the decryption function wipes the output buffer. Assume that the output buffer for the plaintext is shared from the client to the provisioning application, which re-shares it with the crypto service. A malicious client can read another client (the victim)'s encrypted data by passing the ciphertext to the provisioning application, which will attempt to decrypt it with associated data identifying the requesting client. Although the operation will fail beacuse the tag is wrong, the malicious client still reads the victim plaintext. ### Possible countermeasures From 352095ca865a4ff4478838a80ca5a678d8899634 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 19:56:22 +0200 Subject: [PATCH 005/184] Simplify the relaxed output-output rule Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 61d37dbff1..e71eed405b 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -91,7 +91,8 @@ The following rules guarantee that shared memory cannot result in a security vio * Never read the same input twice at the same index. * Never read back from an output. -* Once potentially confidential data has been written to an output, it may not be overwritten. (This rule is more complex to allow writing non-confidential data first, for example to pre-initialize an output to zero for robustness.) +* Never write to the output twice at the same index. + * This rule can usefully be relaxed in many circumstances. It is ok to write data that is independent of the inputs (and not otherwise confidential), then overwrite it. For example, it is ok to zero the output buffer before starting to process the input. These rules are very difficult to enforce. From db00543b3ae281443ad925c36bf8c0cd3ff872da Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 19:57:53 +0200 Subject: [PATCH 006/184] Add a section on write-read feedback It's a security violation, although it's not clear whether it really needs to influence the design. Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index e71eed405b..5d3273a1a7 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -73,6 +73,18 @@ Vulnerability example with chained calls (temporary exposure): an application en Vulnerability example with chained calls (backtrack): we consider a provisioning application that provides a data encryption service on behalf of multiple clients, using a single shared key. Clients are not allowed to access each other's data. The provisioning application isolates clients by including the client identity in the associated data. Suppose that an AEAD decryption function processes the ciphertext incrementally by simultaneously writing the plaintext to the output buffer and calculating the tag. (This is how AEAD decryption usually works.) At the end, if the tag is wrong, the decryption function wipes the output buffer. Assume that the output buffer for the plaintext is shared from the client to the provisioning application, which re-shares it with the crypto service. A malicious client can read another client (the victim)'s encrypted data by passing the ciphertext to the provisioning application, which will attempt to decrypt it with associated data identifying the requesting client. Although the operation will fail beacuse the tag is wrong, the malicious client still reads the victim plaintext. +#### Write-read feedback + +If a function both has an input argument and an output argument in shared memory, and processes its input incrementally to emit output incrementally, the following sequence of events is possible: + +1. The crypto code processes part of the input and writes the corresponding part of the output. +2. The client reads the early output and uses that to calculate the next part of the input. +3. The crypto code processes the rest of the input. + +There are cryptographic mechanisms for which this breaks security properties. An example is [CBC encryption](https://link.springer.com/content/pdf/10.1007/3-540-45708-9_2.pdf): if the client can choose the content of a plaintext block after seeing the immediately preceding ciphertext block, this gives the client a decryption oracle. This is a security violation if the key policy only allowed the client to encrypt, not to decrypt. + +TODO: is this a risk we want to take into account? Although this extends the possible behaviors of the one-shot interface, the client can do the same thing legitimately with the multipart interface. + ### Possible countermeasures In this section, we briefly discuss generic countermeasures. @@ -87,7 +99,7 @@ Example: the PSA Firmware Framework 1.0 forbids shared memory between partitions #### Careful accesses -The following rules guarantee that shared memory cannot result in a security violation: +The following rules guarantee that shared memory cannot result in a security violation other than [write-read feedback](#write-read feedback): * Never read the same input twice at the same index. * Never read back from an output. From 2859267a27e23f1eb551ba01ba7c76c74c70489c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 20:01:36 +0200 Subject: [PATCH 007/184] Clarify terminology: built-in driver Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 5d3273a1a7..57341b09ef 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -119,7 +119,7 @@ A call to a crypto service to perform a crypto operation involves the following 1. The remote procedure call framework provided by the operating system. 2. The code of the crypto service. 3. The code of the PSA Crypto dispatch layer (also known as the core), which is provided by Mbed TLS. -4. The implementation of the cryptographic mechanism, which may be provided by Mbed TLS or by a third-party driver. +4. The driver implementing the cryptographic mechanism, which may be provided by Mbed TLS (built-in driver) or by a third-party driver. The [PSA Crypto API specification](https://arm-software.github.io/psa-api/crypto/1.1/overview/conventions.html#stability-of-parameters) puts the responsibility for protection on the implementation of the PSA Crypto API, i.e. (3) or (4). @@ -223,10 +223,10 @@ Proposed general idea: have tests where the test code calling API functions allo TODO: write document and reference it here. -### Validation of protection for built-in mechanisms +### Validation of protection for built-in drivers TODO -## Analysis of argument protection in built-in mechanisms +## Analysis of argument protection in built-in drivers TODO From 9cad3b3a705888f86b5dc0c90d31fa91c37e33fa Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 20:03:18 +0200 Subject: [PATCH 008/184] Design change for cipher/AEAD There are many reasons why a driver might violate the security requirements for plaintext or ciphertext buffers, so mandate copying. Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 57341b09ef..d1a4b2bdcc 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -164,9 +164,9 @@ The following buffers are considered small buffers: **Design decision: the dispatch layer shall copy all small buffers**. -#### Symmetric cryptography inputs +#### Symmetric cryptography inputs with small output -Message inputs to hash, MAC, cipher (plaintext or ciphertext), AEAD (associated data, plaintext, ciphertext) and key derivation operations are at a low risk of [read-read inconsistency](#read-read-inconsistency) because they are unformatted data, and for all specified algorithms, it is natural to process the input one byte at a time. +Message inputs to hash, MAC and key derivation operations are at a low risk of [read-read inconsistency](#read-read-inconsistency) because they are unformatted data, and for all specified algorithms, it is natural to process the input one byte at a time. **Design decision: require symmetric cryptography drivers to read their input without a risk of read-read inconsistency**. @@ -180,13 +180,25 @@ Key derivation typically emits its output as a stream, with no error condition d **Design decision: require key derivation drivers to emit their output without reading back from the output buffer**. -#### Cipher and AEAD outputs +#### Cipher and AEAD AEAD decryption is at risk of [write-write disclosure](#write-write-disclosure) when the tag does not match. +AEAD encryption and decryption are at risk of [read-read inconsistency](#read-read-inconsistency) if they process the input multiple times, which is natural in a number of cases: + +* when encrypting with an encrypt-and-authenticate or authenticate-then-encrypt structure (one read to calculate the authentication tag and another read to encrypt); +* when decrypting with an encrypt-then-authenticate structure (one read to decrypt and one read to calculate the authentication tag); +* with SIV modes (not yet present in the PSA API, but likely to come one day) (one full pass to calculate the IV, then another full pass for the core authenticated encryption); + Cipher and AEAD outputs are at risk of [write-read-inconsistency](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). -**Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD outputs**. +**Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD plaintext/ciphertext inputs and outputs**. + +Note that this can be a single buffer for the input and the output if the driver supports in-place operation (which it is supposed to, since it is supposed to support arbitrary overlap, although this is not always the case in Mbed TLS, a [known issue](https://github.com/Mbed-TLS/mbedtls/issues/3266)). A side benefit of doing this intermediate copy is that overlap will be supported. + +For all currently implemented AEAD modes, the associated data is only processed once to calculate an intermedidate value of the authentication tag. + +**Design decision: for now, require AEAD drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk. ## Design of shared memory protection @@ -196,12 +208,14 @@ This section explains how Mbed TLS implements the shared memory protection strat * The core (dispatch layer) shall make a copy of the following buffers, so that drivers do not receive arguments that are in shared memory: * Any input or output from asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export. + * Plaintext/ciphertext inputs and outputs for cipher and AEAD. * The output of a hash or MAC operation. * Cooked key derivation output. * A document shall explain the requirements on drivers for arguments whose access needs to be protected: * Hash and MAC input. * Cipher/AEAD IV/nonce (to be confirmed). + * AEAD associated data (to be confirmed). * Key derivation input (excluding key agreement). * Raw key derivation output (excluding cooked key derivation output). From 35de1f7a7dd90503aa692f5c0c3f973d7c547a79 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 20:04:16 +0200 Subject: [PATCH 009/184] Distinguish whole-message signature from other asymmetric cryptography Whole-message signature may process the message multiple times (EdDSA signature does it). Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index d1a4b2bdcc..da6da2212f 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -158,9 +158,10 @@ Note that in this context, a “small buffer” is one with a size limit that is The following buffers are considered small buffers: -* Any input or output from asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export. -* The output of a hash or MAC operation. +* Any input or output directly related to asymmetric cryptography (signature, encryption/decryption, key exchange, PAKE), including key import and export. + * Note that this does not include inputs or outputs that are not processed by an asymmetric primitives, for example the message input to `psa_sign_message` or `psa_verify_message`. * Cooked key derivation output. +* The output of a hash or MAC operation. **Design decision: the dispatch layer shall copy all small buffers**. @@ -200,6 +201,12 @@ For all currently implemented AEAD modes, the associated data is only processed **Design decision: for now, require AEAD drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk. +#### Message signature + +For signature algorithms with a hash-and-sign framework, the input to sign/verify-message is passed to a hash, and thus can follow the same rules as [symmetric cryptography inputs with small output](#symmetric-cryptography-inputs-with-small-output). This is also true for `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`, which is the only non-hash-and-sign signature mechanism implemented in Mbed TLS 3.5. This is not true for PureEdDSA (`#PSA_ALG_PURE_EDDSA`), which is not yet implemented: [PureEdDSA signature](https://www.rfc-editor.org/rfc/rfc8032#section-5.1.6) processes the message twice. (However, PureEdDSA verification only processes the message once.) + +**Design decision: for now, require sign/verify-message drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting PureEdDSA, at which point the dispatch layer shall copy the input for algorithms such as PureEdDSA that are not known to be low-risk. + ## Design of shared memory protection This section explains how Mbed TLS implements the shared memory protection strategy summarized below. From 7bc1bb65e9dc89f80c251d3b417f6a9710323869 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 20:05:25 +0200 Subject: [PATCH 010/184] Short explanations of what is expected in the design sections Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index da6da2212f..440281705f 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -236,18 +236,20 @@ Copy what needs copying. This seems straightforward. ### Validation of copying -TODO +TODO: how to we validate that we didn't forget to copy? Proposed general idea: have tests where the test code calling API functions allocates memory in a certain pool, and code in the library allocates memory in a different pool. Test drivers check that needs-copying arguments are within the library pool, not within the test pool. +Note: we could also validate by unmapping or poisoning the memory containing the API function arguments while the driver is working. But how would we validate that we have poison/unmap calls in the right places? + ### Shared memory protection requirements TODO: write document and reference it here. ### Validation of protection for built-in drivers -TODO +TODO: when there is a requirement on drivers, how to we validate that our built-in implementation meets these requirements? (This may be through testing, review, static analysis or any other means or a combination.) ## Analysis of argument protection in built-in drivers -TODO +TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? From 6998721c695db1773768269c9861eed8d6d877a4 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 20:05:32 +0200 Subject: [PATCH 011/184] Add a section skeleton for copy bypass It's something we're likely to want to do at some point. Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 440281705f..f705e9542e 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -253,3 +253,11 @@ TODO: when there is a requirement on drivers, how to we validate that our built- ## Analysis of argument protection in built-in drivers TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? + +## Copy bypass + +For efficiency, we are likely to want mechanisms to bypass the copy and process buffers directly in builds that are not affected by shared memory considerations. + +Expand this section to document any mechanisms that bypass the copy. + +Make sure that such mechanisms preserve the guarantees when buffers overlap. From 1f2802c403ebd795c163073cb77705258e7fa7e5 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 13 Oct 2023 21:49:17 +0200 Subject: [PATCH 012/184] Suggest validating copy by memory poisoning Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 26 +++++++++++++++++++++++++- 1 file changed, 25 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index f705e9542e..ef8e4d9984 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -238,9 +238,33 @@ Copy what needs copying. This seems straightforward. TODO: how to we validate that we didn't forget to copy? +#### Validation of copying with memory pools + Proposed general idea: have tests where the test code calling API functions allocates memory in a certain pool, and code in the library allocates memory in a different pool. Test drivers check that needs-copying arguments are within the library pool, not within the test pool. -Note: we could also validate by unmapping or poisoning the memory containing the API function arguments while the driver is working. But how would we validate that we have poison/unmap calls in the right places? +#### Validation of copying by memory poisoning + +Proposed general idea: in test code, “poison” the memory area used by input and output parameters that must be copied. Poisoning means something that prevents accessing memory while it is poisoned. This could be via memory protection (allocate with `mmap` then disable access with `mprotect`), or some kind of poisoning for an analyzer such as MSan or Valgrind. + +In the library, the code that does the copying temporarily unpoisons the memory by calling a test hook. + +``` +static void copy_to_user(void *copy_buffer, void *const input_buffer, size_t length) { +#if defined(MBEDTLS_TEST_HOOKS) + if (mbedtls_psa_core_poison_memory != NULL) { + mbedtls_psa_core_poison_memory(copy_buffer, length, 0); + } +#endif + memcpy(copy_buffer, input_buffer, length); +#if defined(MBEDTLS_TEST_HOOKS) + if (mbedtls_psa_core_poison_memory != NULL) { + mbedtls_psa_core_poison_memory(copy_buffer, length, 1); + } +#endif +} +``` + +The reason to poison the memory before calling the library, rather than after the copy-in (and symmetrically for output buffers) is so that the test will fail if we forget to copy, or we copy the wrong thing. This would not be the case if we relied on the library's copy function to do the poisoning: that would only validate that the driver code does not access the memory on the condition that the copy is done as expected. ### Shared memory protection requirements From a3ce6437bf488d84151c48f79e0ccbeaca67e76f Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 16 Oct 2023 15:39:37 +0200 Subject: [PATCH 013/184] Typos Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index ef8e4d9984..890918c0e2 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -44,7 +44,7 @@ If an input argument is in shared memory, there is a risk of a **read-read incon Vulnerability example (parsing): suppose the input contains data with a type-length-value or length-value encoding (for example, importing an RSA key). The crypto code reads the length field and checks that it fits within the buffer. (This could be the length of the overall data, or the length of an embedded field) Later, the crypto code reads the length again and uses it without validation. A malicious client can modify the length field in the shared memory between the two reads and thus cause a buffer overread on the second read. -Vulnerability example (dual processing): consider an RPC to perform authenticated encryption, using a mechanism with an encrypt-and-MAC structure. The authenticated encryption implementation separately calculates the ciphertext and the MAC from the plantext. A client sets the plaintext input to `"PPPP"`, then starts the RPC call, then changes the input buffer to `"QQQQ"` while the crypto service is working. +Vulnerability example (dual processing): consider an RPC to perform authenticated encryption, using a mechanism with an encrypt-and-MAC structure. The authenticated encryption implementation separately calculates the ciphertext and the MAC from the plaintext. A client sets the plaintext input to `"PPPP"`, then starts the RPC call, then changes the input buffer to `"QQQQ"` while the crypto service is working. * Any of `enc("PPPP")+mac("PPPP")`, `enc("PPQQ")+mac("PPQQ")` or `enc("QQQQ")+mac("QQQQ")` are valid outputs: they are outputs that can be produced by this authenticated encryption RPC. * If the authenticated encryption calculates the ciphertext before the client changes the output buffer and calculates the MAC after that change, reading the input buffer again each time, the output will be `enc("PPPP")+mac("QQQQ")`. There is no input that can lead to this output, hence this behavior violates the security guarantees of the crypto service. @@ -191,13 +191,13 @@ AEAD encryption and decryption are at risk of [read-read inconsistency](#read-re * when decrypting with an encrypt-then-authenticate structure (one read to decrypt and one read to calculate the authentication tag); * with SIV modes (not yet present in the PSA API, but likely to come one day) (one full pass to calculate the IV, then another full pass for the core authenticated encryption); -Cipher and AEAD outputs are at risk of [write-read-inconsistency](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). +Cipher and AEAD outputs are at risk of [write-read inconsistency](#write-read-inconsistency) and [write-write disclosure](#write-write-disclosure) if they are implemented by copying the input into the output buffer with `memmove`, then processing the data in place. In particular, this approach makes it easy to fully support overlapping, since `memmove` will take care of overlapping cases correctly, which is otherwise hard to do portably (C99 does not offer an efficient, portable way to check whether two buffers overlap). **Design decision: the dispatch layer shall allocate an intermediate buffer for cipher and AEAD plaintext/ciphertext inputs and outputs**. Note that this can be a single buffer for the input and the output if the driver supports in-place operation (which it is supposed to, since it is supposed to support arbitrary overlap, although this is not always the case in Mbed TLS, a [known issue](https://github.com/Mbed-TLS/mbedtls/issues/3266)). A side benefit of doing this intermediate copy is that overlap will be supported. -For all currently implemented AEAD modes, the associated data is only processed once to calculate an intermedidate value of the authentication tag. +For all currently implemented AEAD modes, the associated data is only processed once to calculate an intermediate value of the authentication tag. **Design decision: for now, require AEAD drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk. From 87889ebe86ee5a122dd73fd0f34dd207a626779b Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 16 Oct 2023 15:40:02 +0200 Subject: [PATCH 014/184] Fix editorial error with semantic consequences Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 890918c0e2..3bb49d9d29 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -199,7 +199,7 @@ Note that this can be a single buffer for the input and the output if the driver For all currently implemented AEAD modes, the associated data is only processed once to calculate an intermediate value of the authentication tag. -**Design decision: for now, require AEAD drivers to read their input without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk. +**Design decision: for now, require AEAD drivers to read the additional data without a risk of read-read inconsistency**. Make a note to revisit this when we start supporting an SIV mode, at which point the dispatch layer shall copy the input for modes that are not known to be low-risk. #### Message signature From 8ebeb9c180a91c220ab746a1af6f16ffa48c1989 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 16 Oct 2023 18:35:54 +0200 Subject: [PATCH 015/184] Test for read-read inconsistency with mprotect and ptrace/gdb Signed-off-by: Gilles Peskine --- docs/architecture/psa-shared-memory.md | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 3bb49d9d29..783b118841 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -274,6 +274,30 @@ TODO: write document and reference it here. TODO: when there is a requirement on drivers, how to we validate that our built-in implementation meets these requirements? (This may be through testing, review, static analysis or any other means or a combination.) +Note: focusing on read-read inconsistencies for now, as most of the cases where we aren't copying are inputs. + +#### Linux mprotect+ptrace + +Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use `ptrace` from a parent process to react to SIGSEGV from a denied access. On SIGSEGV happening in the faulting region: + +1. Use `ptrace` to execute a `mprotect` system call in the child to enable access. TODO: How? `ptrace` can modify registers and memory in the child, which includes changing parameters of a syscall that's about to be executed, but not directly cause the child process to execute a syscall that it wasn't about to execute. +2. Use `ptrace` with `PTRACE_SINGLESTEP` to re-execute the failed load/store instrution. +3. Use `ptrace` to execute a `mprotect` system call in the child to disable access. +4. Use `PTRACE_CONT` to resume the child execution. + +Record the addresses that are accessed. Mark the test as failed if the same address is read twice. + +#### Debugger + mprotect + +Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use a debugger to handle SIGSEGV (Gdb: set signal catchpoint). If the segfault was due to accessing the protected region: + +1. Execute `mprotect` to allow access. +2. Single-step the load/store instruction. +3. Execute `mprotect` to disable access. +4. Continue execution. + +Record the addresses that are accessed. Mark the test as failed if the same address is read twice. This part might be hard to do in the gdb language, so we may want to just log the addresses and then use a separate program to analyze the logs, or do the gdb tasks from Python. + ## Analysis of argument protection in built-in drivers TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? From 23661cc2327d06b12dfe2962a1f82d97feb88d5e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 16 Oct 2023 19:31:41 +0100 Subject: [PATCH 016/184] Detailed design of memory protection strategy Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 176 ++++++++++++++++++++++++- 1 file changed, 173 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 783b118841..a65ed26ac0 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -232,11 +232,60 @@ Justification: see “[Susceptibility of different mechanisms](susceptibility-of ### Implementation of copying -Copy what needs copying. This seems straightforward. +Copy what needs copying. This is broadly straightforward, however there are a few things to consider. + +#### Compiler optimization of copies + +It is unclear whether the compiler will attempt to optimize away copying operations. + +Once the copying code is implemented, it should be evaluated to see whether compiler optimization is a problem. Specifically, for the major compilers and platforms supported by Mbed TLS: +* Build Mbed TLS with the compiler set to a high level of optimization (e.g. `-O2` for `gcc`). +* Inspect the generated code with `objdump` or a similar tool to see if copying operations are preserved. + +If copying behaviour is preserved by all major compilers and platforms then assume that compiler optimization is not a problem. + +If copying behaviour is optimized away by the compiler, further investigation is needed. Experiment with using the `volatile` keyword to force the compiler not to optimize accesses to the copied buffers. + +**Open questions: Will the compiler optimize away copies? If so, can it be prevented from doing so in a portable way?** + +#### Copying code + +We may either copy buffers on an ad-hoc basis using `memcpy()` in each PSA function, or use a unified set of functions for copying input and output data. The advantages of the latter are obvious: + +* Any test hooks need only be added in one place. +* Copying code must only be reviewed for correctness in one place, rather than in all functions where it occurs. +* Copy bypass is simpler as we can just replace these functions with no-ops in a single place. +* Any complexity needed to prevent the compiler optimizing copies away does not have to be duplicated. + +On the other hand, the only advantage of ad-hoc copying is slightly greater flexibility. + +**Design decision: Create a unified set of functions for copying input and output data.** + +#### Copying in multipart APIs + +Multipart APIs may follow one of 2 possible approaches for copying of input: + +##### 1. Allocate a buffer and copy input on each call to `update()` + +This is simple and mirrors the approach for one-shot APIs nicely. However, allocating memory in the middle of a multi-part operation is likely to be bad for performance. Multipart APIs are designed in part for systems that do not have time to perform an operation at once, so introducing poor performance may be a problem here. + +**Open question: Does memory allocation in `update()` cause a performance problem? If so, to what extent?** + +##### 2. Allocate a buffer at the start of the operation and subdivide calls to `update()` + +In this approach, input and output buffers are allocated at the start of the operation that are large enough to hold the expected average call to `update()`. When `update()` is called with larger buffers than these, the PSA API layer makes multiple calls to the driver, chopping the input into chunks of the temporary buffer size and filling the output from the results until the operation is finished. + +This would be more complicated than approach (1) and introduces some extra issues. For example, if one of the intermediate calls to the driver's `update()` returns an error, it is not possible for the driver's state to be rolled back to before the first call to `update()`. It is unclear how this could be solved. + +However, this approach would reduce memory usage in some cases and prevent memory allocation during an operation. Additionally, since the input and output buffers would be fixed-size it would be possible to allocate them statically, avoiding the need for any dynamic memory allocation at all. + +**Design decision: Initially use approach (1) and treat approach (2) as an optimization to be done if necessary.** ### Validation of copying -TODO: how to we validate that we didn't forget to copy? +#### Validation of copying by review + +This is fairly self-explanatory. Review all functions that use shared memory and ensure that they each copy memory. This is the simplest strategy to implement but is less reliable than automated validation. #### Validation of copying with memory pools @@ -263,9 +312,62 @@ static void copy_to_user(void *copy_buffer, void *const input_buffer, size_t len #endif } ``` - The reason to poison the memory before calling the library, rather than after the copy-in (and symmetrically for output buffers) is so that the test will fail if we forget to copy, or we copy the wrong thing. This would not be the case if we relied on the library's copy function to do the poisoning: that would only validate that the driver code does not access the memory on the condition that the copy is done as expected. +Note: Extra work may be needed when allocating buffers to ensure that each shared buffer lies in its own separate page, allowing its permissions to be set independently. + +**Question: Should we try to build memory poisoning validation on existing Mbed TLS tests, or write new tests for this?** + +##### Validation with existing tests + +It should be possible to integrate memory poisoning validation with existing tests. This has two main advantages: +* All of the tests are written already, potentially saving development time. +* The code coverage of these tests is already much greater than would be achievable writing new tests from scratch. + +In an ideal world, we would be able to take a grand, encompassing approach whereby we would simply replace the implementation of `mbedtls_calloc()` and all tests would transparently run with memory poisoning enabled. Unfortunately, there are some significant difficulties with this idea: +* We cannot automatically distinguish which allocated buffers are shared buffers that need memory poisoning enabled. +* Some input buffers to tested functions may be stack allocated so cannot be poisoned automatically. + +Instead, consider a more modest strategy. Create a function: +```c +uint8_t *mbedtls_test_get_poisoned_copy(uint8_t *buffer, size_t len) +``` +that creates a poisoned copy of a buffer ready to be passed to the PSA function. Also create: +```c +uint8_t *mbedtls_test_copy_free_poisoned_buffer(uint8_t *poisoned_buffer, uint8_t *original_buffer, size_t len) +``` +which copies the poisoned buffer contents back into the original buffer and frees the poisoned copy. + +In each test case, manually wrap any calls to PSA functions in code that substitutes a poisoned buffer. For example, the code: +```c +psa_api_do_some_operation(input, input_len, output, output_len); +``` +Would be transformed to: +```c +input_poisoned = mbedtls_test_get_poisoned_copy(input, input_len); +output_poisoned = mbedtls_test_get_poisoned_copy(output, output_len); +psa_api_do_some_operation(input_poisoned, input_len, output_poisoned, output_len); +mbedtls_test_copy_free_poisoned_buffer(input_poisoned, input, input_len); +mbedtls_test_copy_free_poisoned_buffer(output_poisoned, output, output_len); +``` +Further interface design or careful use of macros may make this a little less cumbersome than it seems in this example. + +The poison copying functions should be written so as to evaluate to no-ops based on the value of a config option. They also need not be added to all tests, only to a 'covering set' of important tests. + +##### Validation with new tests + +Validation with newly created tests is initially simpler to implement than using the existing tests, since the tests can know about memory poisoning from the start. However, re-implementing testing for most PSA interfaces (even only basic positive testing) is a large undertaking. Furthermore, not much is gained over the previous approach, given that it seems straightforward to wrap PSA function calls in existing tests with poisoning code. + +**Design decision: Add memory poisoning code to existing tests rather than creating new ones, since this is simpler and produces greater coverage.** + +#### Discussion + +Of all discussed approaches, validation by memory poisoning appears as the best. This is because it: +* Does not require complex linking against different versions of `malloc()` (as is the case with the memory pool approach). +* Allows automated testing (unlike the review approach). + +**Design decision: Use a memory poisoning approach to validate copying.** + ### Shared memory protection requirements TODO: write document and reference it here. @@ -298,6 +400,12 @@ Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or ree Record the addresses that are accessed. Mark the test as failed if the same address is read twice. This part might be hard to do in the gdb language, so we may want to just log the addresses and then use a separate program to analyze the logs, or do the gdb tasks from Python. +#### Discussion + +The best approach for validating the correctness of memory accesses is an open question that requires further investigation and prototyping. The above sections discuss some possibilities. + +However, there is one additional consideration that may make this easier. The careful-access approach to memory protection is only planned for hash and MAC algorithms. These lend themselves to a linear access pattern on input data; it may be simpler to test that a linear pattern is followed, rather than a random-access single-access-per-location pattern. + ## Analysis of argument protection in built-in drivers TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? @@ -309,3 +417,65 @@ For efficiency, we are likely to want mechanisms to bypass the copy and process Expand this section to document any mechanisms that bypass the copy. Make sure that such mechanisms preserve the guarantees when buffers overlap. + +## Detailed design + +### Copying functions + +As discussed above, it is simpler to use a single unified API for copying. Therefore, we create the following functions: + +* `psa_crypto_copy_input(const uint8_t *input, size_t input_length, uint8_t *input_copy, size_t input_copy_length)` +* `psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_length, uint8_t *output, size_t output_length)` + +These seem to be a repeat of the same function, however it is useful to retain two separate functions for input and output parameters so that we can use different test hooks in each when using memory poisoning for tests. + +Given that the majority of functions will be allocating memory on the heap to copy, it may help to build convenience functions that allocate the memory as well. One function allocates and copies the buffers: + +* `psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_length, uint8_t *output, size_t output_length, struct {uint8_t *inp, uint8_t *out} *buffers)` + +This function allocates an input and output buffer in `buffers` and copy the input from the user-supplied input buffer to `buffers->inp`. + +An analogous function is needed to copy and free the buffers: + +* `psa_crypto_copy_and_free(struct {uint8_t *inp, uint8_t *out} buffers, const uint8_t *input, size_t input_length, const uint8_t *output, size_t output_length)` + +This function would first copy the `buffers->out` buffer to the user-supplied output buffer and then free `buffers->inp` and `buffers->out`. + +Some PSA functions may not use these convenience functions as they may have local optimizations that reduce memory usage. For example, ciphers may be able to use a single intermediate buffer for both input and output. + +### Implementation by module + +Module | Input protection strategy | Output protection strategy | Notes +---|---|---|--- +Hash and MAC | Careful access | Careful access | Low risk of multiple-access as the input and output are raw unformatted data. +Cipher | Copying | Copying | +AEAD | Copying (careful access for additional data) | Copying | +Key derivation | Careful access | Careful access | +Asymmetric signature | Careful access | Copying | Inputs to signatures are passed to a hash. This will no longer hold once PureEdDSA support is implemented. +Asymmetric encryption | Copying | Copying | +Key agreement | Copying | Copying | +PAKE | Copying | Copying | +Key import / export | Copying | Copying | Keys may be imported and exported in DER format, which is a structured format and therefore susceptible to read-read inconsistencies and potentially write-read inconsistencies. + +### Validation of copying + +As discussed above, the best strategy for validation of copies appears to be validation by memory poisoning. + +To implement this validation, we need several things: +1. The ability to allocate memory in individual pages. +2. The ability to poison memory pages in the copy functions. +3. Tests that exercise this functionality. + +We can implement (1) as a test helper function that allocates full pages of memory so that we can safely set permissions on them: +```c +unsigned char *mbedtls_test_get_buffer_poisoned_page(size_t nmemb, size_t size) +``` +This allocates a buffer of the requested size that is guaranteed to lie entirely within its own memory page. It also calls `mprotect()` so that the page is inaccessible. + +Requirement (2) can be implemented by creating a function as alluded to above: +```c +void mbedtls_psa_core_poison_memory(unsigned char *buffer, size_t len, int poisoned) +``` +This function should call `mprotect()` on the buffer to prevent it from being accessed (when `poisoned == 1`) or to allow it to be accessed (when `poisoned == 0`). Note that `mprotect()` requires a page-aligned address, so the function may have to do some preliminary work to find the correct page-aligned address that contains `buffer`. + +### Validation of protection by careful access From 0bd87f59595316b5656dda310b9445e0810282c7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 13:45:21 +0100 Subject: [PATCH 017/184] Change unsigned int to uint8_t Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index a65ed26ac0..d0c19b002a 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -468,13 +468,13 @@ To implement this validation, we need several things: We can implement (1) as a test helper function that allocates full pages of memory so that we can safely set permissions on them: ```c -unsigned char *mbedtls_test_get_buffer_poisoned_page(size_t nmemb, size_t size) +uint8_t *mbedtls_test_get_buffer_poisoned_page(size_t nmemb, size_t size) ``` This allocates a buffer of the requested size that is guaranteed to lie entirely within its own memory page. It also calls `mprotect()` so that the page is inaccessible. Requirement (2) can be implemented by creating a function as alluded to above: ```c -void mbedtls_psa_core_poison_memory(unsigned char *buffer, size_t len, int poisoned) +void mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t len, int poisoned) ``` This function should call `mprotect()` on the buffer to prevent it from being accessed (when `poisoned == 1`) or to allow it to be accessed (when `poisoned == 0`). Note that `mprotect()` requires a page-aligned address, so the function may have to do some preliminary work to find the correct page-aligned address that contains `buffer`. From dae0ad439f71ae4114e1fc547a7e19d903dee59d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 14:03:51 +0100 Subject: [PATCH 018/184] Add more detail in design of memory poisoning Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index d0c19b002a..b68c874ff8 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -472,10 +472,24 @@ uint8_t *mbedtls_test_get_buffer_poisoned_page(size_t nmemb, size_t size) ``` This allocates a buffer of the requested size that is guaranteed to lie entirely within its own memory page. It also calls `mprotect()` so that the page is inaccessible. +We also need a function to reset the permissions and free the memory: +```c +void mbedtls_test_free_buffer_poisoned_page(uint8_t *buffer, size_t len) +``` +This calls `mprotect()` to restore read and write permissions to the pages of the buffer and then frees the buffer. + +On top of this function we can build the functions for testing mentioned above: +```c +uint8_t *mbedtls_test_get_poisoned_copy(uint8_t *buffer, size_t len) +uint8_t *mbedtls_test_copy_free_poisoned_buffer(uint8_t *poisoned_buffer, uint8_t *original_buffer, size_t len) +``` + Requirement (2) can be implemented by creating a function as alluded to above: ```c void mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t len, int poisoned) ``` This function should call `mprotect()` on the buffer to prevent it from being accessed (when `poisoned == 1`) or to allow it to be accessed (when `poisoned == 0`). Note that `mprotect()` requires a page-aligned address, so the function may have to do some preliminary work to find the correct page-aligned address that contains `buffer`. +Requirement (3) is implemented by wrapping calls to PSA functions with code that creates poisoned copies of its inputs and outputs as described above. + ### Validation of protection by careful access From 3f7e42a7501b43f5efbb1420fdb10e09b2412b77 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 15:14:20 +0100 Subject: [PATCH 019/184] Move implementation by module table earlier Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 28 +++++++++++++------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index b68c874ff8..279e8651df 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -420,6 +420,20 @@ Make sure that such mechanisms preserve the guarantees when buffers overlap. ## Detailed design +### Implementation by module + +Module | Input protection strategy | Output protection strategy | Notes +---|---|---|--- +Hash and MAC | Careful access | Careful access | Low risk of multiple-access as the input and output are raw unformatted data. +Cipher | Copying | Copying | +AEAD | Copying (careful access for additional data) | Copying | +Key derivation | Careful access | Careful access | +Asymmetric signature | Careful access | Copying | Inputs to signatures are passed to a hash. This will no longer hold once PureEdDSA support is implemented. +Asymmetric encryption | Copying | Copying | +Key agreement | Copying | Copying | +PAKE | Copying | Copying | +Key import / export | Copying | Copying | Keys may be imported and exported in DER format, which is a structured format and therefore susceptible to read-read inconsistencies and potentially write-read inconsistencies. + ### Copying functions As discussed above, it is simpler to use a single unified API for copying. Therefore, we create the following functions: @@ -443,20 +457,6 @@ This function would first copy the `buffers->out` buffer to the user-supplied ou Some PSA functions may not use these convenience functions as they may have local optimizations that reduce memory usage. For example, ciphers may be able to use a single intermediate buffer for both input and output. -### Implementation by module - -Module | Input protection strategy | Output protection strategy | Notes ----|---|---|--- -Hash and MAC | Careful access | Careful access | Low risk of multiple-access as the input and output are raw unformatted data. -Cipher | Copying | Copying | -AEAD | Copying (careful access for additional data) | Copying | -Key derivation | Careful access | Careful access | -Asymmetric signature | Careful access | Copying | Inputs to signatures are passed to a hash. This will no longer hold once PureEdDSA support is implemented. -Asymmetric encryption | Copying | Copying | -Key agreement | Copying | Copying | -PAKE | Copying | Copying | -Key import / export | Copying | Copying | Keys may be imported and exported in DER format, which is a structured format and therefore susceptible to read-read inconsistencies and potentially write-read inconsistencies. - ### Validation of copying As discussed above, the best strategy for validation of copies appears to be validation by memory poisoning. From a72b4ca734b80758e23d7c87c2d6409d153e8f5c Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 15:22:15 +0100 Subject: [PATCH 020/184] Modify optimize-testing instructions Mention -flto and whole-program optimization as this is the most important aspect. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 279e8651df..081095f0d1 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -239,7 +239,8 @@ Copy what needs copying. This is broadly straightforward, however there are a fe It is unclear whether the compiler will attempt to optimize away copying operations. Once the copying code is implemented, it should be evaluated to see whether compiler optimization is a problem. Specifically, for the major compilers and platforms supported by Mbed TLS: -* Build Mbed TLS with the compiler set to a high level of optimization (e.g. `-O2` for `gcc`). +* Write a small program that uses a PSA function which copies inputs or outputs. +* Build the program with link-time optimization / full-program optimization enabled (e.g. `-flto` with `gcc`). * Inspect the generated code with `objdump` or a similar tool to see if copying operations are preserved. If copying behaviour is preserved by all major compilers and platforms then assume that compiler optimization is not a problem. From 05ca3d9a1be46e5bc005b6292c64004eb3ddb67f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 16:45:37 +0100 Subject: [PATCH 021/184] Expand design for validation of careful access Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 081095f0d1..278a6fee45 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -373,13 +373,27 @@ Of all discussed approaches, validation by memory poisoning appears as the best. TODO: write document and reference it here. -### Validation of protection for built-in drivers +### Validation of careful access for built-in drivers -TODO: when there is a requirement on drivers, how to we validate that our built-in implementation meets these requirements? (This may be through testing, review, static analysis or any other means or a combination.) +For PSA functions whose inputs and outputs are not copied, it is important that we validate that the builtin drivers are correctly accessing their inputs and outputs so as not to cause a security issue. Specifically, we must check that each memory location in a shared buffer is not accessed more than once by a driver function. In this section we examine various possible methods for performing this validation. -Note: focusing on read-read inconsistencies for now, as most of the cases where we aren't copying are inputs. +Note: We are focusing on read-read inconsistencies for now, as most of the cases where we aren't copying are inputs. -#### Linux mprotect+ptrace +#### Review + +As with validation of copying, the simplest method of validation we can implement is careful code review. This is the least desirable method of validation for several reasons: +1. It is tedious for the reviewers. +2. Reviewers are prone to make mistakes (especially when performing tedious tasks). +3. It requires engineering time linear in the number of PSA functions to be tested. +4. It cannot assure the quality of third-party drivers, whereas automated tests can be ported to any driver implementation in principle. + +If all other approaches turn out to be prohibitively difficult, code review exists as a fallback option. However, it should be understood that this is far from ideal. + +#### Tests using `mprotect()` + +Checking that a memory location is not accessed more than once may be achieved by using `mprotect()` on a Linux system to cause a segmentation fault whenever a memory access happens. Tests based on this approach are sketched below. + +##### Linux mprotect+ptrace Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use `ptrace` from a parent process to react to SIGSEGV from a denied access. On SIGSEGV happening in the faulting region: @@ -390,7 +404,7 @@ Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or ree Record the addresses that are accessed. Mark the test as failed if the same address is read twice. -#### Debugger + mprotect +##### Debugger + mprotect Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or reenable access. Use a debugger to handle SIGSEGV (Gdb: set signal catchpoint). If the segfault was due to accessing the protected region: From 4e54abf182a4525540b160938f43a8e8722cee79 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 19 Oct 2023 17:59:45 +0100 Subject: [PATCH 022/184] Add section on possible use of Valgrind tracing Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 278a6fee45..7e2c280202 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -415,6 +415,24 @@ Idea: call `mmap` to allocate memory for arguments and `mprotect` to deny or ree Record the addresses that are accessed. Mark the test as failed if the same address is read twice. This part might be hard to do in the gdb language, so we may want to just log the addresses and then use a separate program to analyze the logs, or do the gdb tasks from Python. +#### Instrumentation (Valgrind) + +An alternative approach is to use a dynamic instrumentation tool (the most obvious being Valgrind) to trace memory accesses and check that each of the important memory addresses is accessed no more than once. + +Valgrind has no tool specifically that checks the property that we are looking for. However, it is possible to generate a memory trace with Valgrind using the following: + +``` +valgrind --tool=lackey --trace-mem=yes --log-file=logfile ./myprogram +``` +This will execute `myprogram` and dump a record of every memory access to `logfile`, with its address and data width. If `myprogram` is a test that does the following: +1. Set up input and output buffers for a PSA function call. +2. Leak the start and end address of each buffer via `print()`. +3. Write data into the input buffer exactly once. +4. Call the PSA function. +5. Read data from the output buffer exactly once. + +Then it should be possible to parse the output from the program and from Valgrind and check that each location was accessed exactly twice: once by the program's setup and once by the PSA function. + #### Discussion The best approach for validating the correctness of memory accesses is an open question that requires further investigation and prototyping. The above sections discuss some possibilities. From 17b3716c5ae33d30229dc55f565878f971aa6b1c Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 20 Oct 2023 18:14:36 +0100 Subject: [PATCH 023/184] Tweak compiler optimization evaluation section * Remove references to the platform - this is unlikely to affect whether copies are optimized. * Note that the evaluation should test extreme optimisation settings. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 7e2c280202..a962839e1b 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -238,12 +238,12 @@ Copy what needs copying. This is broadly straightforward, however there are a fe It is unclear whether the compiler will attempt to optimize away copying operations. -Once the copying code is implemented, it should be evaluated to see whether compiler optimization is a problem. Specifically, for the major compilers and platforms supported by Mbed TLS: +Once the copying code is implemented, it should be evaluated to see whether compiler optimization is a problem. Specifically, for the major compilers supported by Mbed TLS: * Write a small program that uses a PSA function which copies inputs or outputs. -* Build the program with link-time optimization / full-program optimization enabled (e.g. `-flto` with `gcc`). +* Build the program with link-time optimization / full-program optimization enabled (e.g. `-flto` with `gcc`). Try also enabling the most extreme optimization options such as `-Ofast` (`gcc`) and `-Oz` (`clang`). * Inspect the generated code with `objdump` or a similar tool to see if copying operations are preserved. -If copying behaviour is preserved by all major compilers and platforms then assume that compiler optimization is not a problem. +If copying behaviour is preserved by all major compilers then assume that compiler optimization is not a problem. If copying behaviour is optimized away by the compiler, further investigation is needed. Experiment with using the `volatile` keyword to force the compiler not to optimize accesses to the copied buffers. From 51fc6cf3783436a5d4a495d6a7c3aedb7fa75b29 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 20 Oct 2023 18:07:38 +0100 Subject: [PATCH 024/184] Explore sanitizers for memory poisoning Consider MSan, ASan and Valgrind as options for implementing memory poisoning tests. Come to the altered conclusion that Valgrind is the best option. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index a962839e1b..94c2fbc3a1 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -315,7 +315,25 @@ static void copy_to_user(void *copy_buffer, void *const input_buffer, size_t len ``` The reason to poison the memory before calling the library, rather than after the copy-in (and symmetrically for output buffers) is so that the test will fail if we forget to copy, or we copy the wrong thing. This would not be the case if we relied on the library's copy function to do the poisoning: that would only validate that the driver code does not access the memory on the condition that the copy is done as expected. -Note: Extra work may be needed when allocating buffers to ensure that each shared buffer lies in its own separate page, allowing its permissions to be set independently. +##### Options for implementing poisoning + +There are several different ways that poisoning could be implemented: +1. Using Valgrind's memcheck tool. Valgrind provides a macro `VALGRIND_MAKE_MEM_NO_ACCESS` that allows manual memory poisoning. Valgrind memory poisoning is already used for constant-flow testing in Mbed TLS. +2. Using Memory Sanitizer (MSan), which allows us to mark memory as uninitialized. This is also used for constant-flow testing. It is suitable for input buffers only, since it allows us to detect when a poisoned buffer is read but not when it is written. +3. Using Address Sanitizer (ASan). This provides `ASAN_POISON_MEMORY_REGION` which marks memory as inaccessible. +4. Allocating buffers separate pages and calling `mprotect()` to set pages as inaccessible. This has the disadvantage that we will have to manually ensure that buffers sit in their own pages, which likely means making a copy. + +Approach (2) is insufficient for the full testing we require as we need to be able to check both input and output buffers. + +All three remaining approaches are suitable for our purposes. However, approach (4) is more complex than the other two. To implement it, we would need to allocate poisoned buffers in separate memory pages. They would require special handling and test code would likely have to be designed around this special handling. + +Meanwhile, approaches (1) and (3) are much more convenient. We are simply required to call a special macro on some buffer that was allocated by us and the sanitizer takes care of everything else. Of these two, ASan appears to have a limitation related to buffer alignment. From code comments quoted in [the documentation](https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning): + +> This function is not guaranteed to poison the whole region - it may poison only subregion of [addr, addr+size) due to ASan alignment restrictions. + +It is not clear how easy it would be to work around this. Valgrind does not appear to have this limitation (unless Valgrind is simply more poorly documented). Valgrind is therefore slightly preferable compared with ASan, but ASan is an acceptable fallback option. + +**Design decision: Use Valgrind's memcheck tool to implement memory poisoning tests.** **Question: Should we try to build memory poisoning validation on existing Mbed TLS tests, or write new tests for this?** From be868347f4757e42b75261f45baac7ae5bc2d1d6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 20 Oct 2023 19:25:11 +0100 Subject: [PATCH 025/184] Rewrite design exploration of copy validation Main changes: * New tests are easier to write than first stated * Use of existing tests is ledd beneficial * But using existing tests is a benefit if it can be done transparently Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 55 ++++++++------------------ 1 file changed, 17 insertions(+), 38 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 94c2fbc3a1..31722337e0 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -337,47 +337,26 @@ It is not clear how easy it would be to work around this. Valgrind does not appe **Question: Should we try to build memory poisoning validation on existing Mbed TLS tests, or write new tests for this?** -##### Validation with existing tests - -It should be possible to integrate memory poisoning validation with existing tests. This has two main advantages: -* All of the tests are written already, potentially saving development time. -* The code coverage of these tests is already much greater than would be achievable writing new tests from scratch. - -In an ideal world, we would be able to take a grand, encompassing approach whereby we would simply replace the implementation of `mbedtls_calloc()` and all tests would transparently run with memory poisoning enabled. Unfortunately, there are some significant difficulties with this idea: -* We cannot automatically distinguish which allocated buffers are shared buffers that need memory poisoning enabled. -* Some input buffers to tested functions may be stack allocated so cannot be poisoned automatically. - -Instead, consider a more modest strategy. Create a function: -```c -uint8_t *mbedtls_test_get_poisoned_copy(uint8_t *buffer, size_t len) -``` -that creates a poisoned copy of a buffer ready to be passed to the PSA function. Also create: -```c -uint8_t *mbedtls_test_copy_free_poisoned_buffer(uint8_t *poisoned_buffer, uint8_t *original_buffer, size_t len) -``` -which copies the poisoned buffer contents back into the original buffer and frees the poisoned copy. - -In each test case, manually wrap any calls to PSA functions in code that substitutes a poisoned buffer. For example, the code: -```c -psa_api_do_some_operation(input, input_len, output, output_len); -``` -Would be transformed to: -```c -input_poisoned = mbedtls_test_get_poisoned_copy(input, input_len); -output_poisoned = mbedtls_test_get_poisoned_copy(output, output_len); -psa_api_do_some_operation(input_poisoned, input_len, output_poisoned, output_len); -mbedtls_test_copy_free_poisoned_buffer(input_poisoned, input, input_len); -mbedtls_test_copy_free_poisoned_buffer(output_poisoned, output, output_len); -``` -Further interface design or careful use of macros may make this a little less cumbersome than it seems in this example. - -The poison copying functions should be written so as to evaluate to no-ops based on the value of a config option. They also need not be added to all tests, only to a 'covering set' of important tests. - ##### Validation with new tests -Validation with newly created tests is initially simpler to implement than using the existing tests, since the tests can know about memory poisoning from the start. However, re-implementing testing for most PSA interfaces (even only basic positive testing) is a large undertaking. Furthermore, not much is gained over the previous approach, given that it seems straightforward to wrap PSA function calls in existing tests with poisoning code. +Validation with newly created tests would be simpler to implement than using existing tests, since the tests can be written to take into account memory poisoning. It is also possible to build such a testsuite on top of existing tests - `mbedtls_test_psa_exercise_key` is a test helper that already exercises most PSA interfaces, so implementing the tests could be as simple as extending it. -**Design decision: Add memory poisoning code to existing tests rather than creating new ones, since this is simpler and produces greater coverage.** +Additionally, we can ensure that all functions are exercised by automatically generating test data files. + +##### Validation with existing tests + +An alternative approach would be to integrate memory poisoning validation with existing tests. This has two main advantages: +* All of the tests are written already, potentially saving development time. +* The code coverage of these tests is greater than would be achievable writing new tests from scratch. In practice this advantage is small as buffer copying will take place in the dispatch layer. The tests are therefore independent of the values of parameters passed to the driver, so extra coverage in these parameters does not gain anything. + +It may be possible to transparently implement memory poisoning so that existing tests can work without modification. This would be achieved by replacing the implementation of `malloc()` with one that allocates poisoned buffers. However, there are some difficulties with this: +* Not all buffers allocated by tests are used as inputs and outputs to PSA functions being tested. +* Those buffers that are inputs to a PSA function need to be unpoisoned right up until the function is called, so that they can be filled with input data. +* Those buffers that are outputs from a PSA function need to be unpoisoned straight after the function returns, so that they can be read to check the output is correct. + +These issues may be solved by creating some kind of test wrapper around every PSA function call that poisons the memory. However, it is unclear how straightforward this will be in practice. If this is simple to achieve, the extra coverage and time saved on new tests will be a benefit. If not, writing new tests is the best strategy. + +**Design decision: Attempt to add memory poisoning transparently to existing tests. If this proves difficult, write new tests instead.** #### Discussion From 16dac00cb9b5d53b36541eb8132cbb935b32ada8 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 18:34:43 +0100 Subject: [PATCH 026/184] Add skeleton of detailed design rewrite In light of choosing Valgrind/ASan over mprotect()-based poisoning, update the detailed design of copy validation. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 42 +++++++++++--------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 31722337e0..0d48324262 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -489,37 +489,29 @@ Some PSA functions may not use these convenience functions as they may have loca ### Validation of copying -As discussed above, the best strategy for validation of copies appears to be validation by memory poisoning. +As discussed in the [design exploration of copying validation](#validation-of-copying), the best strategy for validation of copies appears to be validation by memory poisoning, implemented using Valgrind. -To implement this validation, we need several things: -1. The ability to allocate memory in individual pages. -2. The ability to poison memory pages in the copy functions. -3. Tests that exercise this functionality. - -We can implement (1) as a test helper function that allocates full pages of memory so that we can safely set permissions on them: +To perform memory poisoning, we must implement the function alluded to in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning): ```c -uint8_t *mbedtls_test_get_buffer_poisoned_page(size_t nmemb, size_t size) +mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t length, int should_poison); ``` -This allocates a buffer of the requested size that is guaranteed to lie entirely within its own memory page. It also calls `mprotect()` so that the page is inaccessible. +This should either poison or unpoison the given buffer based on the value of `should_poison`: +* When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)`. +* When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)`. -We also need a function to reset the permissions and free the memory: -```c -void mbedtls_test_free_buffer_poisoned_page(uint8_t *buffer, size_t len) -``` -This calls `mprotect()` to restore read and write permissions to the pages of the buffer and then frees the buffer. +We may choose one of two approaches. As discussed in [the design exploration](#validation-with-existing-tests), the first is preferred: +* Use transparent allocation-based memory poisoning. +* Use memory poisoning functions and a new testsuite. -On top of this function we can build the functions for testing mentioned above: -```c -uint8_t *mbedtls_test_get_poisoned_copy(uint8_t *buffer, size_t len) -uint8_t *mbedtls_test_copy_free_poisoned_buffer(uint8_t *poisoned_buffer, uint8_t *original_buffer, size_t len) -``` +We will specify the particularities of each approach's implementation below. -Requirement (2) can be implemented by creating a function as alluded to above: -```c -void mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t len, int poisoned) -``` -This function should call `mprotect()` on the buffer to prevent it from being accessed (when `poisoned == 1`) or to allow it to be accessed (when `poisoned == 0`). Note that `mprotect()` requires a page-aligned address, so the function may have to do some preliminary work to find the correct page-aligned address that contains `buffer`. +#### Transparent allocation-based memory poisoning + +In order to implement transparent memory poisoning we require a wrapper around all PSA function calls that poisons any input and output buffers. + +The easiest way to do this is to create a header that `#define`s PSA function names to be wrapped versions of themselves. + +#### Memory poisoning functions and a new testsuite -Requirement (3) is implemented by wrapping calls to PSA functions with code that creates poisoned copies of its inputs and outputs as described above. ### Validation of protection by careful access From ded14a2c0214d30744f719ed4a1892e2aff3cba6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 18:58:41 +0100 Subject: [PATCH 027/184] Add example wrapper function implementation Give an example wrapper foir psa_aead_update for the transparent testing option. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 22 +++++++++++++++++++++- 1 file changed, 21 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 0d48324262..b10953824e 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -509,7 +509,27 @@ We will specify the particularities of each approach's implementation below. In order to implement transparent memory poisoning we require a wrapper around all PSA function calls that poisons any input and output buffers. -The easiest way to do this is to create a header that `#define`s PSA function names to be wrapped versions of themselves. +The easiest way to do this is to create wrapper functions that poison the memory and then `#define` PSA function names to be wrapped versions of themselves. For example, to replace `psa_aead_update()`: +```c +psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + mbedtls_psa_core_poison_memory(input, input_length, 1); + mbedtls_psa_core_poison_memory(output, output_size, 1); + psa_status_t status = psa_aead_update(operation, input, input_length, + output, output_size, output_length); + mbedtls_psa_core_poison_memory(input, input_length, 0); + mbedtls_psa_core_poison_memory(output, output_size, 0); + + return status; +} + +#define psa_aead_update(...) mem_poison_psa_aead_update(__VA_ARGS__) +``` #### Memory poisoning functions and a new testsuite From f889e0fa0a534fc06277d693732545570bc29558 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 19:01:21 +0100 Subject: [PATCH 028/184] Replace vague 'above' with a reference for ease-of-navigation Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index b10953824e..2c2da8d883 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -466,7 +466,7 @@ Key import / export | Copying | Copying | Keys may be imported and exported in D ### Copying functions -As discussed above, it is simpler to use a single unified API for copying. Therefore, we create the following functions: +As discussed in [Copying code](#copying-code), it is simpler to use a single unified API for copying. Therefore, we create the following functions: * `psa_crypto_copy_input(const uint8_t *input, size_t input_length, uint8_t *input_copy, size_t input_copy_length)` * `psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_length, uint8_t *output, size_t output_length)` From cbf068dbee8abc647899953b93590c215c619703 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 19:03:10 +0100 Subject: [PATCH 029/184] Fix broken reference Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 2c2da8d883..f0f27811cd 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -99,7 +99,7 @@ Example: the PSA Firmware Framework 1.0 forbids shared memory between partitions #### Careful accesses -The following rules guarantee that shared memory cannot result in a security violation other than [write-read feedback](#write-read feedback): +The following rules guarantee that shared memory cannot result in a security violation other than [write-read feedback](#write-read-feedback): * Never read the same input twice at the same index. * Never read back from an output. From c61ddb2089c9d07ed6e9241620d5dc14c6134110 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 19:18:50 +0100 Subject: [PATCH 030/184] Add C language annotation to code block Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index f0f27811cd..5ae3ecbbe3 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -298,7 +298,7 @@ Proposed general idea: in test code, “poison” the memory area used by input In the library, the code that does the copying temporarily unpoisons the memory by calling a test hook. -``` +```c static void copy_to_user(void *copy_buffer, void *const input_buffer, size_t length) { #if defined(MBEDTLS_TEST_HOOKS) if (mbedtls_psa_core_poison_memory != NULL) { From 52df6207368f0cc51b720f882e32eb88bc12b6c1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 19:38:01 +0100 Subject: [PATCH 031/184] Use ASan for memory poisoning as well as Valgrind Also add information about ASan from Microsoft docs. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 5ae3ecbbe3..c3894a31f5 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -331,9 +331,11 @@ Meanwhile, approaches (1) and (3) are much more convenient. We are simply requir > This function is not guaranteed to poison the whole region - it may poison only subregion of [addr, addr+size) due to ASan alignment restrictions. -It is not clear how easy it would be to work around this. Valgrind does not appear to have this limitation (unless Valgrind is simply more poorly documented). Valgrind is therefore slightly preferable compared with ASan, but ASan is an acceptable fallback option. +Specifically, ASan will round the buffer size down to 8 bytes before poisoning due to details of its implementation. For more information on this, see [Microsoft documentation of this feature](https://learn.microsoft.com/en-us/cpp/sanitizers/asan-runtime?view=msvc-170#alignment-requirements-for-addresssanitizer-poisoning). -**Design decision: Use Valgrind's memcheck tool to implement memory poisoning tests.** +It should be possible to work around this by manually rounding buffer lengths up to the nearest multiple of 8 in the poisoning function, although it's remotely possible that this will cause other problems. Valgrind does not appear to have this limitation (unless Valgrind is simply more poorly documented). However, running tests under Valgrind causes a much greater slowdown compared with ASan. As a result, it would be beneficial to implement support for both Valgrind and ASan, to give the extra flexibility to choose either performance or accuracy as required. This should be simple as both have very similar memory poisoning interfaces. + +**Design decision: Implement memory poisoning tests with both Valgrind's memcheck and ASan manual poisoning.** **Question: Should we try to build memory poisoning validation on existing Mbed TLS tests, or write new tests for this?** @@ -489,15 +491,15 @@ Some PSA functions may not use these convenience functions as they may have loca ### Validation of copying -As discussed in the [design exploration of copying validation](#validation-of-copying), the best strategy for validation of copies appears to be validation by memory poisoning, implemented using Valgrind. +As discussed in the [design exploration of copying validation](#validation-of-copying), the best strategy for validation of copies appears to be validation by memory poisoning, implemented using Valgrind and ASan. To perform memory poisoning, we must implement the function alluded to in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning): ```c mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t length, int should_poison); ``` This should either poison or unpoison the given buffer based on the value of `should_poison`: -* When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)`. -* When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)`. +* When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)` or `ASAN_POISON_MEMORY_REGION(buffer, length)`. +* When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)` or `ASAN_UNPOISON_MEMORY_REGION(buffer, length)`. We may choose one of two approaches. As discussed in [the design exploration](#validation-with-existing-tests), the first is preferred: * Use transparent allocation-based memory poisoning. From 806055edbf5b953aa9b5d5cec5e161c885fbb2f2 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 19:53:30 +0100 Subject: [PATCH 032/184] Refactor note on preferred poison-test approach Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index c3894a31f5..9929573fc0 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -501,10 +501,12 @@ This should either poison or unpoison the given buffer based on the value of `sh * When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)` or `ASAN_POISON_MEMORY_REGION(buffer, length)`. * When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)` or `ASAN_UNPOISON_MEMORY_REGION(buffer, length)`. -We may choose one of two approaches. As discussed in [the design exploration](#validation-with-existing-tests), the first is preferred: +We may choose one of two approaches: * Use transparent allocation-based memory poisoning. * Use memory poisoning functions and a new testsuite. +As discussed in [the design exploration](#validation-with-existing-tests), the transparent approach is preferred. + We will specify the particularities of each approach's implementation below. #### Transparent allocation-based memory poisoning From 8f905c289d7ba60d339a7906836b3922f81a05a0 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 20:08:38 +0100 Subject: [PATCH 033/184] Add reference to test hooks in detailed design Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 9929573fc0..dd2f87f2a8 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -501,7 +501,9 @@ This should either poison or unpoison the given buffer based on the value of `sh * When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)` or `ASAN_POISON_MEMORY_REGION(buffer, length)`. * When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)` or `ASAN_UNPOISON_MEMORY_REGION(buffer, length)`. -We may choose one of two approaches: +The PSA copying function must then have test hooks implemented as outlined in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning). + +For test implementation, we may choose one of two approaches: * Use transparent allocation-based memory poisoning. * Use memory poisoning functions and a new testsuite. From 6c51207602ecb9482988c0b7829e4ff8f8550df5 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 20:25:14 +0100 Subject: [PATCH 034/184] Add notes about configuration of poisoning tests Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index dd2f87f2a8..874621b10d 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -539,5 +539,8 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #### Memory poisoning functions and a new testsuite +#### Configuration of poisoning tests + +Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `PSA_TEST_COPYING_ASAN` and `PSA_TEST_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling, these options should not be enabled in either the `default` or `full` configurations. Instead, as with the constant flow testing options, they should be enabled in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. ### Validation of protection by careful access From 730dea31cbc710fadb48bc4f7295eca0b51a581e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 20:35:35 +0100 Subject: [PATCH 035/184] Rewrite incorrect description of psa_exercise_key And clarify our potential use of it as a starting point for writing memory poisoning tests from scratch. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 874621b10d..36fc569dbc 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -341,7 +341,7 @@ It should be possible to work around this by manually rounding buffer lengths up ##### Validation with new tests -Validation with newly created tests would be simpler to implement than using existing tests, since the tests can be written to take into account memory poisoning. It is also possible to build such a testsuite on top of existing tests - `mbedtls_test_psa_exercise_key` is a test helper that already exercises most PSA interfaces, so implementing the tests could be as simple as extending it. +Validation with newly created tests would be simpler to implement than using existing tests, since the tests can be written to take into account memory poisoning. It is also possible to build such a testsuite using existing tests as a starting point - `mbedtls_test_psa_exercise_key` is a test helper that already exercises many PSA operations on a key. This would need to be extended to cover operations without keys (e.g. hashes) and multipart operations, but it provides a good base from which to build all of the required testing. Additionally, we can ensure that all functions are exercised by automatically generating test data files. From 09c84ef0cd39610c5070b93dbcbb095bc1e7ded1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 20:43:03 +0100 Subject: [PATCH 036/184] Add lengths to convenience interface sketch Add lengths to structs in the convenience functions to allocate and copy input and output buffers. It seems better to ensure we always store a buffer with its length. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 36fc569dbc..251c699b07 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -477,13 +477,13 @@ These seem to be a repeat of the same function, however it is useful to retain t Given that the majority of functions will be allocating memory on the heap to copy, it may help to build convenience functions that allocate the memory as well. One function allocates and copies the buffers: -* `psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_length, uint8_t *output, size_t output_length, struct {uint8_t *inp, uint8_t *out} *buffers)` +* `psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_length, uint8_t *output, size_t output_length, struct {uint8_t *inp, size_t inp_len, uint8_t *out, size_t out_len} *buffers)` This function allocates an input and output buffer in `buffers` and copy the input from the user-supplied input buffer to `buffers->inp`. An analogous function is needed to copy and free the buffers: -* `psa_crypto_copy_and_free(struct {uint8_t *inp, uint8_t *out} buffers, const uint8_t *input, size_t input_length, const uint8_t *output, size_t output_length)` +* `psa_crypto_copy_and_free(struct {uint8_t *inp, size_t inp_len, uint8_t *out, size_t out_len} buffers, const uint8_t *input, size_t input_length, const uint8_t *output, size_t output_length)` This function would first copy the `buffers->out` buffer to the user-supplied output buffer and then free `buffers->inp` and `buffers->out`. From 56aa1b3fbb5064b814fc8be88f314860b1de8bc7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 23 Oct 2023 21:20:01 +0100 Subject: [PATCH 037/184] Add exploration section on FVP testing Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 251c699b07..bb8be2b864 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -432,6 +432,14 @@ This will execute `myprogram` and dump a record of every memory access to `logfi Then it should be possible to parse the output from the program and from Valgrind and check that each location was accessed exactly twice: once by the program's setup and once by the PSA function. +#### Fixed Virtual Platform testing + +It may be possible to measure double accesses by running tests on a Fixed Virtual Platform such as Corstone 310 ecosystem FVP, available [here](https://developer.arm.com/downloads/-/arm-ecosystem-fvps). There exists a pre-packaged example program for the Corstone 310 FVP available as part of the Open IoT SDK [here](https://git.gitlab.arm.com/iot/open-iot-sdk/examples/sdk-examples/-/tree/main/examples/mbedtls/cmsis-rtx/corstone-310) that could provide a starting point for a set of tests. + +Running on an FVP allows two approaches to careful-access testing: +* Convenient scripted use of a debugger with [Iris](https://developer.arm.com/documentation/101196/latest/). This allows memory watchpoints to be set, perhaps more flexibly than with GDB. +* Tracing of all memory accesses with [Tarmac Trace](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace). To validate the single-access properties, the [processor memory access trace source](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace/Processor-memory-access-trace) can be used to output all memory accesses happening on the FVP. This output can then be easily parsed and processed to ensure that the input and output buffers are accessed only once. The addresses of buffers can either be leaked by the program through printing to the serial port or set to fixed values in the FVP's linker script. + #### Discussion The best approach for validating the correctness of memory accesses is an open question that requires further investigation and prototyping. The above sections discuss some possibilities. From c7ccbf51574a24a0830f95d1c19e591d5d1384f1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 24 Oct 2023 15:43:12 +0100 Subject: [PATCH 038/184] Add detailed design section for careful access This consists in outlining the prototyping and evaluation of different possible testing approaches. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index bb8be2b864..14751ea897 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -552,3 +552,22 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `PSA_TEST_COPYING_ASAN` and `PSA_TEST_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling, these options should not be enabled in either the `default` or `full` configurations. Instead, as with the constant flow testing options, they should be enabled in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. ### Validation of protection by careful access + +As concluded in [Validation of careful access for built-in drivers](validation-of-careful-access-for-built-in-drivers), the best approach to validation of careful accesses is an open question. Designing this will involve prototyping each possible approach and choosing the one that seems most fruitful. + +For each of the test strategies discussed: +1. Take 1-2 days to create a basic prototype of a test that uses the approach. +2. Document the prototype - write a short guide that can be followed to arrive at the same prototype. +3. Evaluate the prototype according to its usefulness. The criteria of evaluation should include: + * Ease of implementation - Was the prototype simple to implement? Having implemented it, is it simple to extend it to do all of the required testing? + * Flexibility - Could the prototype be extended to cover other careful-access testing that may be needed in future? + * Performance - Does the test method perform well? Will it cause significant slowdown to CI jobs? + * Ease of reproduction - Does the prototype require a particular platform or tool to be set up? How easy would it be for an external user to run the prototype? + * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily? + +Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing: +* Hash +* MAC +* AEAD (additional data only) +* Key derivation +* Asymmetric signature (input only) From f95767ad565b8d8a7c9fd33859d696a1ceb545ee Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 24 Oct 2023 16:16:36 +0100 Subject: [PATCH 039/184] Clarify use of new tests for careful-access New tests are needed (rather than existing ones) because the complexity of setting up careful-access tests would make it difficult to build atop existing tests. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 14751ea897..452ea7c54d 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -446,6 +446,12 @@ The best approach for validating the correctness of memory accesses is an open q However, there is one additional consideration that may make this easier. The careful-access approach to memory protection is only planned for hash and MAC algorithms. These lend themselves to a linear access pattern on input data; it may be simpler to test that a linear pattern is followed, rather than a random-access single-access-per-location pattern. +##### New vs existing tests + +Most of the test methods discussed above need extra setup. Some require leaking of buffer bounds, predictable memory access patterns or allocation of special buffers. FVP testing even requires the tests to be run on a non-host target. + +With this complexity in mind it does not seem feasible to run careful-access tests using existing testsuites. Instead, new tests should be written that exercise the drivers in the required way. Fortunately, the only interfaces that need testing are hash, MAC, AEAD (testing over AD only), Key derivation and Asymmetric signature, which limits the number of new tests that must be written. + ## Analysis of argument protection in built-in drivers TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? From 2711d23976d6b4c562f51efdc9e44fbf6e151ca2 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 15:07:58 +0100 Subject: [PATCH 040/184] Fix broken links Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 452ea7c54d..c812387eec 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -228,7 +228,7 @@ This section explains how Mbed TLS implements the shared memory protection strat * The built-in implementations of cryptographic mechanisms with arguments whose access needs to be protected shall protect those arguments. -Justification: see “[Susceptibility of different mechanisms](susceptibility-of-different-mechanisms)”. +Justification: see “[Susceptibility of different mechanisms](#susceptibility-of-different-mechanisms)”. ### Implementation of copying @@ -559,7 +559,7 @@ Since the memory poisoning tests will require the use of interfaces specific to ### Validation of protection by careful access -As concluded in [Validation of careful access for built-in drivers](validation-of-careful-access-for-built-in-drivers), the best approach to validation of careful accesses is an open question. Designing this will involve prototyping each possible approach and choosing the one that seems most fruitful. +As concluded in [Validation of careful access for built-in drivers](#validation-of-careful-access-for-built-in-drivers), the best approach to validation of careful accesses is an open question. Designing this will involve prototyping each possible approach and choosing the one that seems most fruitful. For each of the test strategies discussed: 1. Take 1-2 days to create a basic prototype of a test that uses the approach. From 8e58ccb4f6690aa2f045c3897aee4433ef3ff1ce Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 15:13:29 +0100 Subject: [PATCH 041/184] Add blank lines before lists This widens compatibility with different dialects of Markdown. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index c812387eec..c1e6ba1809 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -318,6 +318,7 @@ The reason to poison the memory before calling the library, rather than after th ##### Options for implementing poisoning There are several different ways that poisoning could be implemented: + 1. Using Valgrind's memcheck tool. Valgrind provides a macro `VALGRIND_MAKE_MEM_NO_ACCESS` that allows manual memory poisoning. Valgrind memory poisoning is already used for constant-flow testing in Mbed TLS. 2. Using Memory Sanitizer (MSan), which allows us to mark memory as uninitialized. This is also used for constant-flow testing. It is suitable for input buffers only, since it allows us to detect when a poisoned buffer is read but not when it is written. 3. Using Address Sanitizer (ASan). This provides `ASAN_POISON_MEMORY_REGION` which marks memory as inaccessible. @@ -348,10 +349,12 @@ Additionally, we can ensure that all functions are exercised by automatically ge ##### Validation with existing tests An alternative approach would be to integrate memory poisoning validation with existing tests. This has two main advantages: + * All of the tests are written already, potentially saving development time. * The code coverage of these tests is greater than would be achievable writing new tests from scratch. In practice this advantage is small as buffer copying will take place in the dispatch layer. The tests are therefore independent of the values of parameters passed to the driver, so extra coverage in these parameters does not gain anything. It may be possible to transparently implement memory poisoning so that existing tests can work without modification. This would be achieved by replacing the implementation of `malloc()` with one that allocates poisoned buffers. However, there are some difficulties with this: + * Not all buffers allocated by tests are used as inputs and outputs to PSA functions being tested. * Those buffers that are inputs to a PSA function need to be unpoisoned right up until the function is called, so that they can be filled with input data. * Those buffers that are outputs from a PSA function need to be unpoisoned straight after the function returns, so that they can be read to check the output is correct. @@ -363,6 +366,7 @@ These issues may be solved by creating some kind of test wrapper around every PS #### Discussion Of all discussed approaches, validation by memory poisoning appears as the best. This is because it: + * Does not require complex linking against different versions of `malloc()` (as is the case with the memory pool approach). * Allows automated testing (unlike the review approach). @@ -381,6 +385,7 @@ Note: We are focusing on read-read inconsistencies for now, as most of the cases #### Review As with validation of copying, the simplest method of validation we can implement is careful code review. This is the least desirable method of validation for several reasons: + 1. It is tedious for the reviewers. 2. Reviewers are prone to make mistakes (especially when performing tedious tasks). 3. It requires engineering time linear in the number of PSA functions to be tested. @@ -424,6 +429,7 @@ Valgrind has no tool specifically that checks the property that we are looking f valgrind --tool=lackey --trace-mem=yes --log-file=logfile ./myprogram ``` This will execute `myprogram` and dump a record of every memory access to `logfile`, with its address and data width. If `myprogram` is a test that does the following: + 1. Set up input and output buffers for a PSA function call. 2. Leak the start and end address of each buffer via `print()`. 3. Write data into the input buffer exactly once. @@ -437,6 +443,7 @@ Then it should be possible to parse the output from the program and from Valgrin It may be possible to measure double accesses by running tests on a Fixed Virtual Platform such as Corstone 310 ecosystem FVP, available [here](https://developer.arm.com/downloads/-/arm-ecosystem-fvps). There exists a pre-packaged example program for the Corstone 310 FVP available as part of the Open IoT SDK [here](https://git.gitlab.arm.com/iot/open-iot-sdk/examples/sdk-examples/-/tree/main/examples/mbedtls/cmsis-rtx/corstone-310) that could provide a starting point for a set of tests. Running on an FVP allows two approaches to careful-access testing: + * Convenient scripted use of a debugger with [Iris](https://developer.arm.com/documentation/101196/latest/). This allows memory watchpoints to be set, perhaps more flexibly than with GDB. * Tracing of all memory accesses with [Tarmac Trace](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace). To validate the single-access properties, the [processor memory access trace source](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace/Processor-memory-access-trace) can be used to output all memory accesses happening on the FVP. This output can then be easily parsed and processed to ensure that the input and output buffers are accessed only once. The addresses of buffers can either be leaked by the program through printing to the serial port or set to fixed values in the FVP's linker script. @@ -512,12 +519,14 @@ To perform memory poisoning, we must implement the function alluded to in [Valid mbedtls_psa_core_poison_memory(uint8_t *buffer, size_t length, int should_poison); ``` This should either poison or unpoison the given buffer based on the value of `should_poison`: + * When `should_poison == 1`, this is equivalent to calling `VALGRIND_MAKE_MEM_NOACCESS(buffer, length)` or `ASAN_POISON_MEMORY_REGION(buffer, length)`. * When `should_poison == 0`, this is equivalent to calling `VALGRIND_MAKE_MEM_DEFINED(buffer, length)` or `ASAN_UNPOISON_MEMORY_REGION(buffer, length)`. The PSA copying function must then have test hooks implemented as outlined in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning). For test implementation, we may choose one of two approaches: + * Use transparent allocation-based memory poisoning. * Use memory poisoning functions and a new testsuite. @@ -562,6 +571,7 @@ Since the memory poisoning tests will require the use of interfaces specific to As concluded in [Validation of careful access for built-in drivers](#validation-of-careful-access-for-built-in-drivers), the best approach to validation of careful accesses is an open question. Designing this will involve prototyping each possible approach and choosing the one that seems most fruitful. For each of the test strategies discussed: + 1. Take 1-2 days to create a basic prototype of a test that uses the approach. 2. Document the prototype - write a short guide that can be followed to arrive at the same prototype. 3. Evaluate the prototype according to its usefulness. The criteria of evaluation should include: @@ -572,6 +582,7 @@ For each of the test strategies discussed: * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily? Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing: + * Hash * MAC * AEAD (additional data only) From 2b86df87da757322703a19ba9d6ebad6f3664aa0 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 15:26:27 +0100 Subject: [PATCH 042/184] De-duplicate section titles Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index c1e6ba1809..cdcccd9287 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -363,7 +363,7 @@ These issues may be solved by creating some kind of test wrapper around every PS **Design decision: Attempt to add memory poisoning transparently to existing tests. If this proves difficult, write new tests instead.** -#### Discussion +#### Discussion of copying validation Of all discussed approaches, validation by memory poisoning appears as the best. This is because it: @@ -447,7 +447,7 @@ Running on an FVP allows two approaches to careful-access testing: * Convenient scripted use of a debugger with [Iris](https://developer.arm.com/documentation/101196/latest/). This allows memory watchpoints to be set, perhaps more flexibly than with GDB. * Tracing of all memory accesses with [Tarmac Trace](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace). To validate the single-access properties, the [processor memory access trace source](https://developer.arm.com/documentation/100964/1123/Plug-ins-for-Fast-Models/TarmacTrace/Processor-memory-access-trace) can be used to output all memory accesses happening on the FVP. This output can then be easily parsed and processed to ensure that the input and output buffers are accessed only once. The addresses of buffers can either be leaked by the program through printing to the serial port or set to fixed values in the FVP's linker script. -#### Discussion +#### Discussion of careful-access validation The best approach for validating the correctness of memory accesses is an open question that requires further investigation and prototyping. The above sections discuss some possibilities. @@ -510,7 +510,7 @@ This function would first copy the `buffers->out` buffer to the user-supplied ou Some PSA functions may not use these convenience functions as they may have local optimizations that reduce memory usage. For example, ciphers may be able to use a single intermediate buffer for both input and output. -### Validation of copying +### Implementation of copying validation As discussed in the [design exploration of copying validation](#validation-of-copying), the best strategy for validation of copies appears to be validation by memory poisoning, implemented using Valgrind and ASan. From c59913822e6817afced3978f1591bcd4306af683 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 15:33:50 +0100 Subject: [PATCH 043/184] Remove references to new-test approach in design This is already covered in the design exploration and since the other approach was chose, we do not need to discuss it in the detailed design section. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 11 +---------- 1 file changed, 1 insertion(+), 10 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index cdcccd9287..82f4bd0610 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -525,14 +525,7 @@ This should either poison or unpoison the given buffer based on the value of `sh The PSA copying function must then have test hooks implemented as outlined in [Validation of copying by memory poisoning](#validation-of-copying-by-memory-poisoning). -For test implementation, we may choose one of two approaches: - -* Use transparent allocation-based memory poisoning. -* Use memory poisoning functions and a new testsuite. - -As discussed in [the design exploration](#validation-with-existing-tests), the transparent approach is preferred. - -We will specify the particularities of each approach's implementation below. +As discussed in [the design exploration](#validation-with-existing-tests), the preferred approach for implementing copy-testing is to implement it transparently using existing tests. This is specified in more detail below. #### Transparent allocation-based memory poisoning @@ -560,8 +553,6 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #define psa_aead_update(...) mem_poison_psa_aead_update(__VA_ARGS__) ``` -#### Memory poisoning functions and a new testsuite - #### Configuration of poisoning tests Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `PSA_TEST_COPYING_ASAN` and `PSA_TEST_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling, these options should not be enabled in either the `default` or `full` configurations. Instead, as with the constant flow testing options, they should be enabled in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. From 78bd77f5749e2d5c9eb89c1f807a54bc15c71279 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 18:04:39 +0100 Subject: [PATCH 044/184] Careful-access prototyping to design exploration Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 42 +++++++++++--------------- 1 file changed, 17 insertions(+), 25 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 82f4bd0610..85e26ad502 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -449,9 +449,24 @@ Running on an FVP allows two approaches to careful-access testing: #### Discussion of careful-access validation -The best approach for validating the correctness of memory accesses is an open question that requires further investigation and prototyping. The above sections discuss some possibilities. +The best approach for validating the correctness of memory accesses is an open question that requires further investigation. To answer this question, each of the test strategies discussed above must be prototyped as follows: -However, there is one additional consideration that may make this easier. The careful-access approach to memory protection is only planned for hash and MAC algorithms. These lend themselves to a linear access pattern on input data; it may be simpler to test that a linear pattern is followed, rather than a random-access single-access-per-location pattern. +1. Take 1-2 days to create a basic prototype of a test that uses the approach. +2. Document the prototype - write a short guide that can be followed to arrive at the same prototype. +3. Evaluate the prototype according to its usefulness. The criteria of evaluation should include: + * Ease of implementation - Was the prototype simple to implement? Having implemented it, is it simple to extend it to do all of the required testing? + * Flexibility - Could the prototype be extended to cover other careful-access testing that may be needed in future? + * Performance - Does the test method perform well? Will it cause significant slowdown to CI jobs? + * Ease of reproduction - Does the prototype require a particular platform or tool to be set up? How easy would it be for an external user to run the prototype? + * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily? + +Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing: + +* Hash +* MAC +* AEAD (additional data only) +* Key derivation +* Asymmetric signature (input only) ##### New vs existing tests @@ -556,26 +571,3 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #### Configuration of poisoning tests Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `PSA_TEST_COPYING_ASAN` and `PSA_TEST_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling, these options should not be enabled in either the `default` or `full` configurations. Instead, as with the constant flow testing options, they should be enabled in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. - -### Validation of protection by careful access - -As concluded in [Validation of careful access for built-in drivers](#validation-of-careful-access-for-built-in-drivers), the best approach to validation of careful accesses is an open question. Designing this will involve prototyping each possible approach and choosing the one that seems most fruitful. - -For each of the test strategies discussed: - -1. Take 1-2 days to create a basic prototype of a test that uses the approach. -2. Document the prototype - write a short guide that can be followed to arrive at the same prototype. -3. Evaluate the prototype according to its usefulness. The criteria of evaluation should include: - * Ease of implementation - Was the prototype simple to implement? Having implemented it, is it simple to extend it to do all of the required testing? - * Flexibility - Could the prototype be extended to cover other careful-access testing that may be needed in future? - * Performance - Does the test method perform well? Will it cause significant slowdown to CI jobs? - * Ease of reproduction - Does the prototype require a particular platform or tool to be set up? How easy would it be for an external user to run the prototype? - * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily? - -Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing: - -* Hash -* MAC -* AEAD (additional data only) -* Key derivation -* Asymmetric signature (input only) From 599b0879907d5d815dacefd16403c232e42fa555 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 25 Oct 2023 18:09:17 +0100 Subject: [PATCH 045/184] Rename and specify config options * Rename config options to have MBEDTLS_TEST_ prefix * Clarify that these config options should not exist in mbedtls_config.h Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 85e26ad502..7bcb45c62e 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -570,4 +570,4 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #### Configuration of poisoning tests -Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `PSA_TEST_COPYING_ASAN` and `PSA_TEST_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling, these options should not be enabled in either the `default` or `full` configurations. Instead, as with the constant flow testing options, they should be enabled in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. +Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `MBEDTLS_TEST_PSA_COPYING_ASAN` and `MBEDTLS_TEST_PSA_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling and are for testing only, these options should not be present in `mbedtls_config.h`. Instead, they should be set only in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. From d081e52685bbeb237dab09bd8dff11b9938b4223 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 15:22:07 +0000 Subject: [PATCH 046/184] Discuss plain-overwriting memory poisoning Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 7bcb45c62e..fa07a65252 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -323,9 +323,12 @@ There are several different ways that poisoning could be implemented: 2. Using Memory Sanitizer (MSan), which allows us to mark memory as uninitialized. This is also used for constant-flow testing. It is suitable for input buffers only, since it allows us to detect when a poisoned buffer is read but not when it is written. 3. Using Address Sanitizer (ASan). This provides `ASAN_POISON_MEMORY_REGION` which marks memory as inaccessible. 4. Allocating buffers separate pages and calling `mprotect()` to set pages as inaccessible. This has the disadvantage that we will have to manually ensure that buffers sit in their own pages, which likely means making a copy. +5. Filling buffers with random data, keeping a copy of the original. For input buffers, keep a copy of the original and copy it back once the PSA function returns. For output buffers, fill them with random data and keep a separate copy of it. In the memory poisoning hooks, compare the copy of random data with the original to ensure that the output buffer has not been written directly. Approach (2) is insufficient for the full testing we require as we need to be able to check both input and output buffers. +Approach (5) is simple and requires no extra tooling. It is likely to have good performance as it does not use any sanitizers. However, it requires the memory poisoning test hooks to maintain extra copies of the buffers, which seems difficult to implement in practice. Additionally, it does not precisely test the property we want to validate, so we are relying on the tests to fail if given random data as input. It is possible (if unlikely) that the PSA function will access the poisoned buffer without causing the test to fail. This becomes more likely when we consider test cases that call PSA functions on incorrect inputs to check that the correct error is returned. For these reasons, this memory poisoning approach seems unsuitable. + All three remaining approaches are suitable for our purposes. However, approach (4) is more complex than the other two. To implement it, we would need to allocate poisoned buffers in separate memory pages. They would require special handling and test code would likely have to be designed around this special handling. Meanwhile, approaches (1) and (3) are much more convenient. We are simply required to call a special macro on some buffer that was allocated by us and the sanitizer takes care of everything else. Of these two, ASan appears to have a limitation related to buffer alignment. From code comments quoted in [the documentation](https://github.com/google/sanitizers/wiki/AddressSanitizerManualPoisoning): From e88a6f836885b099405d8114b0de567da2f5bdef Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 15:26:21 +0000 Subject: [PATCH 047/184] Add portability consideration to careful-access It's important that we be able to test for target-specific bugs. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index fa07a65252..b5314ef05d 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -462,6 +462,7 @@ The best approach for validating the correctness of memory accesses is an open q * Performance - Does the test method perform well? Will it cause significant slowdown to CI jobs? * Ease of reproduction - Does the prototype require a particular platform or tool to be set up? How easy would it be for an external user to run the prototype? * Comprehensibility - Accounting for the lower code quality of a prototype, would developers unfamiliar with the tests based on the prototype be able to understand them easily? + * Portability - How well can this approach be ported to multiple platforms? This would allow us to ensure that there are no double-accesses due to a bug that only affects a specific target. Once each prototype is complete, choose the best approach to implement the careful-access testing. Implement tests using this approach for each of the PSA interfaces that require careful-access testing: From e045b55c652d0ba486f321dbbdb495517a460bd9 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 17:00:16 +0000 Subject: [PATCH 048/184] Add sections on validation of validation These cover the fact that we need to test our test framework to make sure it really detects incorrect accesses. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index b5314ef05d..fc179fa2ac 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -478,6 +478,15 @@ Most of the test methods discussed above need extra setup. Some require leaking With this complexity in mind it does not seem feasible to run careful-access tests using existing testsuites. Instead, new tests should be written that exercise the drivers in the required way. Fortunately, the only interfaces that need testing are hash, MAC, AEAD (testing over AD only), Key derivation and Asymmetric signature, which limits the number of new tests that must be written. +#### Validation of validation for careful-access + +In order to ensure that the careful-access validation works, it is necessary to write tests to check that we can correctly detect careful-access violations when they occur. To do this, write a test function that: + +* Reads its input multiple times at the same location. +* Writes to its output multiple times at the same location. + +Then, write a careful-access test for this function and ensure that it fails. + ## Analysis of argument protection in built-in drivers TODO: analyze the built-in implementations of mechanisms for which there is a requirement on drivers. By code inspection, how satisfied are we that they meet the requirement? @@ -575,3 +584,12 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #### Configuration of poisoning tests Since the memory poisoning tests will require the use of interfaces specific to the sanitizers used to poison memory, they must be guarded by new config options, for example `MBEDTLS_TEST_PSA_COPYING_ASAN` and `MBEDTLS_TEST_PSA_COPYING_VALGRIND`, as well as `MBEDTLS_TEST_HOOKS`. These would be analogous to the existing `MBEDTLS_TEST_CONSTANT_FLOW_MEMSAN` and `MBEDTLS_TEST_CONSTANT_FLOW_VALGRIND`. Since they require special tooling and are for testing only, these options should not be present in `mbedtls_config.h`. Instead, they should be set only in a new component in `all.sh` that performs the copy testing with Valgrind or ASan. + +#### Validation of validation for copying + +To make sure that we can correctly detect functions that access their input/output buffers rather than the copies, it is necessary to write a test function that misbehaves and test it with memory poisoning. Specifically, the function should: + +* Read its input buffer and after calling the input-buffer-copying function to create a local copy of its input. +* Write to its output buffer before and after calling the output-buffer-copying function to copy-back its output. + +Then, write a test that uses this function with memory poisoning and ensure that it fails. Since we are expecting a failure due to memory-poisoning, run this test separately from the rest of the memory-poisoning testing. From 15b5beea0c568857afbd235450e2f440db15878e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 17:13:54 +0000 Subject: [PATCH 049/184] Add note on platform-specific barriers Describe the approach of platform-specific code and draw a comparison with the constant-time module. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index fc179fa2ac..3968af142f 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -245,7 +245,7 @@ Once the copying code is implemented, it should be evaluated to see whether comp If copying behaviour is preserved by all major compilers then assume that compiler optimization is not a problem. -If copying behaviour is optimized away by the compiler, further investigation is needed. Experiment with using the `volatile` keyword to force the compiler not to optimize accesses to the copied buffers. +If copying behaviour is optimized away by the compiler, further investigation is needed. Experiment with using the `volatile` keyword to force the compiler not to optimize accesses to the copied buffers. If the `volatile` keyword is not sufficient, we may be able to use compiler or target-specific techniques to prevent optimization, for example memory barriers or empty `asm` blocks. These may be implemented and verified for important platforms while retaining a C implementation that is likely to be correct on most platforms as a fallback - the same approach taken by the constant-time module. **Open questions: Will the compiler optimize away copies? If so, can it be prevented from doing so in a portable way?** From 2531dab296b65ff4c53ed852c3dcef8cc9823caf Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 18:27:10 +0000 Subject: [PATCH 050/184] Add auto-generation of test wrappers to design Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 3968af142f..385f48fe17 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -580,6 +580,18 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #define psa_aead_update(...) mem_poison_psa_aead_update(__VA_ARGS__) ``` +A header containing these wrappers should be auto-generated using a template and annotations to the input and output parameters to the PSA API. This will look something like the following: +```c +MBEDTLS_PSA_SHARED_BUFFER(input, input_length) +MBEDTLS_PSA_SHARED_BUFFER(output, output_size) +psa_status_t psa_aead_update(psa_aead_operation_t *operation, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length); +``` +The `MBEDTLS_PSA_SHARED_BUFFER()` annotation expands to an empty expression but can be parsed by the wrapper generation script, which generates a new header file with the wrappers. #### Configuration of poisoning tests From af45b8333a51d34a4b34c4b5b083cd8a22bf368b Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 19:48:13 +0000 Subject: [PATCH 051/184] Add function prototypes for copying functions Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 29b3b94bfe..4c38a7566f 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -854,4 +854,34 @@ psa_status_t mbedtls_psa_verify_hash_complete( psa_status_t mbedtls_psa_verify_hash_abort( mbedtls_psa_verify_hash_interruptible_operation_t *operation); +/** Copy from an input buffer to a local copy. + * + * \param[in] input Pointer to input buffer. + * \param[in] input_len Length of the input buffer. + * \param[out] input_copy Pointer to a local copy in which to store the input data. + * \param[out] input_copy_len Length of the local copy buffer. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the local copy + * is too small to hold contents of the + * input buffer. + */ +psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, + uint8_t *input_copy, size_t input_copy_len); + +/** Copy from a local output buffer into a user-supplied one. + * + * \param[in] output_copy Pointer to a local buffer containing the output. + * \param[in] output_copy_len Length of the local buffer. + * \param[out] output Pointer to user-supplied output buffer. + * \param[out] output_len Length of the user-supplied output buffer. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the + * user-supplied output buffer is too small to + * hold the contents of the local buffer. + */ +psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, + uint8_t *output, size_t output_len); + #endif /* PSA_CRYPTO_CORE_H */ From 413dd07a4947f86cfefc16c7331d2bf3347c79cf Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 12:16:53 +0000 Subject: [PATCH 052/184] Downgrade auto testing testing to a nice-to-have Automatic testing of our testing is not essential, as our testing framework may be manually tested. Having automated tests to test our tests may be left to future work. Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 385f48fe17..01b42b0cb2 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -599,9 +599,11 @@ Since the memory poisoning tests will require the use of interfaces specific to #### Validation of validation for copying -To make sure that we can correctly detect functions that access their input/output buffers rather than the copies, it is necessary to write a test function that misbehaves and test it with memory poisoning. Specifically, the function should: +To make sure that we can correctly detect functions that access their input/output buffers rather than the copies, it would be best to write a test function that misbehaves and test it with memory poisoning. Specifically, the function should: * Read its input buffer and after calling the input-buffer-copying function to create a local copy of its input. * Write to its output buffer before and after calling the output-buffer-copying function to copy-back its output. -Then, write a test that uses this function with memory poisoning and ensure that it fails. Since we are expecting a failure due to memory-poisoning, run this test separately from the rest of the memory-poisoning testing. +Then, we could write a test that uses this function with memory poisoning and ensure that it fails. Since we are expecting a failure due to memory-poisoning, we would run this test separately from the rest of the memory-poisoning testing. + +However, performing this testing automatically is not urgent. It will suffice to manually verify that the test framework works, automatic tests are a 'nice to have' feature that may be left to future work. From f63a52ed6317371c89cbbc2c168fab1cd2dff270 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 14:25:30 +0000 Subject: [PATCH 053/184] Remove auto-generation of test wrappers Signed-off-by: David Horstmann --- docs/architecture/psa-shared-memory.md | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/docs/architecture/psa-shared-memory.md b/docs/architecture/psa-shared-memory.md index 01b42b0cb2..0b98f25fcc 100644 --- a/docs/architecture/psa-shared-memory.md +++ b/docs/architecture/psa-shared-memory.md @@ -580,18 +580,6 @@ psa_status_t mem_poison_psa_aead_update(psa_aead_operation_t *operation, #define psa_aead_update(...) mem_poison_psa_aead_update(__VA_ARGS__) ``` -A header containing these wrappers should be auto-generated using a template and annotations to the input and output parameters to the PSA API. This will look something like the following: -```c -MBEDTLS_PSA_SHARED_BUFFER(input, input_length) -MBEDTLS_PSA_SHARED_BUFFER(output, output_size) -psa_status_t psa_aead_update(psa_aead_operation_t *operation, - const uint8_t *input, - size_t input_length, - uint8_t *output, - size_t output_size, - size_t *output_length); -``` -The `MBEDTLS_PSA_SHARED_BUFFER()` annotation expands to an empty expression but can be parsed by the wrapper generation script, which generates a new header file with the wrappers. #### Configuration of poisoning tests From 0b241ee584d74d1782163786fac300273ee4e430 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 20:16:41 +0000 Subject: [PATCH 054/184] Add testcases for psa_crypto_copy_input() Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 9 ++++++ tests/suites/test_suite_psa_crypto.function | 31 ++++++++++++++++++++- 2 files changed, 39 insertions(+), 1 deletion(-) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 7b1974865a..757b614bde 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7406,3 +7406,12 @@ persistent_key_load_key_from_storage:"":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY PSA derive persistent key: HKDF SHA-256, exportable persistent_key_load_key_from_storage:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_KEY_TYPE_RAW_DATA:1024:PSA_KEY_USAGE_EXPORT:0:DERIVE_KEY + +PSA input buffer copy: straightforward copy +psa_crypto_copy_input:20:20:PSA_SUCCESS + +PSA input buffer copy: copy buffer larger than required +psa_crypto_copy_input:10:20:PSA_SUCCESS + +PSA input buffer copy: copy buffer too small +psa_crypto_copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 2dfc7a4bfc..21aa3fc610 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -13,7 +13,7 @@ #include "psa/crypto.h" #include "psa_crypto_slot_management.h" -/* For psa_can_do_hash() */ +/* For psa_can_do_hash() and buffer copying functions */ #include "psa_crypto_core.h" #include "test/asn1_helpers.h" @@ -10305,3 +10305,32 @@ void ecjpake_size_macros() PSA_PAKE_INPUT_MAX_SIZE); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_copy_input(int src_len, int dst_len, int exp_ret) +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + uint8_t *src_buffer = NULL; + uint8_t *dst_buffer = NULL; + psa_status_t ret; + + TEST_CALLOC(src_buffer, src_len); + TEST_CALLOC(dst_buffer, dst_len); + + for (int i = 0; i < src_len; i++) { + src_buffer[i] = data[i % sizeof(data)]; + } + + ret = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == (int) PSA_SUCCESS) { + /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ + TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); + } + +exit: + mbedtls_free(src_buffer); + mbedtls_free(dst_buffer); +} +/* END_CASE */ From fde97394a07721b277aa895a51175268848f4bfc Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 20:29:43 +0000 Subject: [PATCH 055/184] Add implementation of psa_crypto_copy_input() Signed-off-by: David Horstmann --- library/psa_crypto.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 1faf1dd6ca..33040faf69 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8441,4 +8441,17 @@ psa_status_t psa_pake_abort( } #endif /* PSA_WANT_ALG_SOME_PAKE */ + +psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, + uint8_t *input_copy, size_t input_copy_len) +{ + if (input_len > input_copy_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + + memcpy(input_copy, input, input_len); + + return PSA_SUCCESS; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 2f964231471b11d47f33821783b48a7bb280e1c3 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 20:34:41 +0000 Subject: [PATCH 056/184] Add testcases for psa_crypto_copy_output() Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 9 +++++++ tests/suites/test_suite_psa_crypto.function | 29 +++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 757b614bde..ced02afe8a 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7415,3 +7415,12 @@ psa_crypto_copy_input:10:20:PSA_SUCCESS PSA input buffer copy: copy buffer too small psa_crypto_copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL + +PSA output buffer copy: straightforward copy +psa_crypto_copy_output:20:20:PSA_SUCCESS + +PSA output buffer copy: output buffer larger than required +psa_crypto_copy_output:10:20:PSA_SUCCESS + +PSA output buffer copy: output buffer too small +psa_crypto_copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 21aa3fc610..c8bbcdbfe4 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10334,3 +10334,32 @@ exit: mbedtls_free(dst_buffer); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_copy_output(int src_len, int dst_len, int exp_ret) +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + uint8_t *src_buffer = NULL; + uint8_t *dst_buffer = NULL; + psa_status_t ret; + + TEST_CALLOC(src_buffer, src_len); + TEST_CALLOC(dst_buffer, dst_len); + + for (int i = 0; i < src_len; i++) { + src_buffer[i] = data[i % sizeof(data)]; + } + + ret = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == (int) PSA_SUCCESS) { + /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ + TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); + } + +exit: + mbedtls_free(src_buffer); + mbedtls_free(dst_buffer); +} +/* END_CASE */ From 8978f5c32a2f09fff67746a6f08d2f31a9034ee8 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 30 Oct 2023 20:37:12 +0000 Subject: [PATCH 057/184] Add implementation of psa_crypto_copy_output() Signed-off-by: David Horstmann --- library/psa_crypto.c | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 33040faf69..56f02a9b41 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8454,4 +8454,14 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, return PSA_SUCCESS; } +psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, + uint8_t *output, size_t output_len) +{ + if (output_len < output_copy_len) { + return PSA_ERROR_BUFFER_TOO_SMALL; + } + memcpy(output, output_copy, output_copy_len); + return PSA_SUCCESS; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 1d838b27b1b770534a79c58c5db819e38beee5a9 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 15:41:45 +0000 Subject: [PATCH 058/184] Add buffers struct and prototypes for alloc API Add function prototypes for psa_crypto_alloc_and_copy() and psa_crypto_alloc_and_free(), along with the necessary state struct. Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 53 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 4c38a7566f..78550f8ee5 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -884,4 +884,57 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, uint8_t *output, size_t output_len); +/** + * \brief Structure to store a pair of copied buffers (input, output) with a + * reference to the original output to be used during copy-back. + */ +struct psa_crypto_buffer_copy_s { + uint8_t *input; + size_t input_len; + + uint8_t *output_original; + uint8_t *output; + size_t output_len; +}; +typedef struct psa_crypto_buffer_copy_s psa_crypto_buffer_copy_t; + +/** + * \brief Allocate copies of provided input and output + * buffers and store references to them along with + * the original output buffer in the provided + * psa_crypto_buffer_copy_t struct. + * Either or both buffers may be NULL, in which case + * they are not copied. + * + * \note The input and output buffers may overlap. + * + * \param[in] input Pointer to the input buffer. + * \param[in] input_len Length of the input buffer. + * \param[in] output Pointer to the output buffer. + * \param[in] output_len Length of the output buffer. + * \param[out] buffers Struct containing pointers to the allocated + * copies and the original output buffer. + * \retval #PSA_SUCCESS + * The buffers were successfully allocated and copied. + * \retval #PSA_ERROR_INSUFFICIENT_MEMORY + * Failed to allocate buffers. + */ +psa_status_t psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_len, + uint8_t *output, size_t output_len, + psa_crypto_buffer_copy_t *buffers); + +/** + * \brief Free an allocated pair of buffers after first + * copying the output buffer back to its original. + * + * \param[in] buffers psa_crypto_buffer_copy_t created by a previous + * call to psa_crypto_alloc_and_copy(). + * \retval #PSA_SUCCESS + * The buffers were successfully copied-back and the + * copies freed. + * \retval #PSA_ERROR_INVALID_ARGUMENT + * Could not copy-back as \p buffers is invalid. + */ +psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers); + #endif /* PSA_CRYPTO_CORE_H */ From 24f11f9cc7c3224ab06ea52bf238369961965586 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 17:40:59 +0000 Subject: [PATCH 059/184] Add testcases for psa_crypto_alloc_and_copy() Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 12 ++++++ tests/suites/test_suite_psa_crypto.function | 41 +++++++++++++++++++++ 2 files changed, 53 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index ced02afe8a..ca13b51e84 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7424,3 +7424,15 @@ psa_crypto_copy_output:10:20:PSA_SUCCESS PSA output buffer copy: output buffer too small psa_crypto_copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL + +PSA buffers alloc and copy +psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS + +PSA buffers alloc and copy: null input +psa_crypto_alloc_and_copy:1:0:0:20:PSA_SUCCESS + +PSA buffers alloc and copy: null output +psa_crypto_alloc_and_copy:0:20:1:0:PSA_SUCCESS + +PSA buffers alloc and copy: null input and output +psa_crypto_alloc_and_copy:1:0:1:0:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index c8bbcdbfe4..cd46fef13b 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10363,3 +10363,44 @@ exit: mbedtls_free(dst_buffer); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_alloc_and_copy(int input_null, int input_len, + int output_null, int output_len, + int exp_ret) +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + uint8_t *input_buffer = NULL; + uint8_t *output_buffer = NULL; + psa_crypto_buffer_copy_t buffer_copies = { 0 }; + psa_status_t ret; + + if (!input_null) { + TEST_CALLOC(input_buffer, input_len); + for (int i = 0; i < input_len; i++) { + input_buffer[i] = data[i % sizeof(data)]; + } + } + if (!output_null) { + TEST_CALLOC(output_buffer, output_len); + for (int i = 0; i < output_len; i++) { + output_buffer[i] = data[i % sizeof(data)]; + } + } + ret = psa_crypto_alloc_and_copy(input_buffer, input_len, + output_buffer, output_len, + &buffer_copies); + TEST_EQUAL(exp_ret, (int) ret); + + if (exp_ret == PSA_SUCCESS) { + TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); + TEST_EQUAL(output_len, buffer_copies.output_len); + } + +exit: + mbedtls_free(input_buffer); + mbedtls_free(output_buffer); + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ From 83eef383c700148d6fab57f0a57bacf9680c535f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 18:03:06 +0000 Subject: [PATCH 060/184] Add implementation of psa_crypto_alloc_and_copy() Signed-off-by: David Horstmann --- library/psa_crypto.c | 45 ++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 56f02a9b41..fc5e241900 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8464,4 +8464,49 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co return PSA_SUCCESS; } +psa_status_t psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_len, + uint8_t *output, size_t output_len, + psa_crypto_buffer_copy_t *buffers) +{ + psa_status_t ret; + /* Zeroize the buffers struct to ensure we can call free() + * on any pointers safely. */ + memset(buffers, 0, sizeof(*buffers)); + + if (output != NULL) { + buffers->output = mbedtls_calloc(output_len, 1); + if (buffers->output == NULL) { + ret = PSA_ERROR_INSUFFICIENT_MEMORY; + goto error; + } + buffers->output_len = output_len; + + buffers->output_original = output; + } + + if (input != NULL) { + buffers->input = mbedtls_calloc(input_len, 1); + if (buffers->input == NULL) { + ret = PSA_ERROR_INSUFFICIENT_MEMORY; + goto error; + } + buffers->input_len = input_len; + + if (psa_crypto_copy_input(input, input_len, + buffers->input, buffers->input_len) + != PSA_SUCCESS) { + ret = PSA_ERROR_CORRUPTION_DETECTED; + goto error; + } + } + + return PSA_SUCCESS; + +error: + mbedtls_free(buffers->input); + mbedtls_free(buffers->output); + memset(buffers, 0, sizeof(*buffers)); + return ret; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From f06ac88284e7305be6614bf59b882a5af55f9025 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 31 Oct 2023 18:43:09 +0000 Subject: [PATCH 061/184] Add extra testcases for buffer copying Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 12 ++++++++++++ tests/suites/test_suite_psa_crypto.function | 16 ++++++++++++---- 2 files changed, 24 insertions(+), 4 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index ca13b51e84..be32882513 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7416,6 +7416,12 @@ psa_crypto_copy_input:10:20:PSA_SUCCESS PSA input buffer copy: copy buffer too small psa_crypto_copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL +PSA input buffer copy: zero-length source buffer +psa_crypto_copy_input:0:10:PSA_SUCCESS + +PSA input buffer copy: zero-length both buffers +psa_crypto_copy_input:0:0:PSA_SUCCESS + PSA output buffer copy: straightforward copy psa_crypto_copy_output:20:20:PSA_SUCCESS @@ -7425,6 +7431,12 @@ psa_crypto_copy_output:10:20:PSA_SUCCESS PSA output buffer copy: output buffer too small psa_crypto_copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL +PSA output buffer copy: zero-length source buffer +psa_crypto_copy_output:0:10:PSA_SUCCESS + +PSA output buffer copy: zero-length both buffers +psa_crypto_copy_output:0:0:PSA_SUCCESS + PSA buffers alloc and copy psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index cd46fef13b..1e9e8d9565 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10314,8 +10314,12 @@ void psa_crypto_copy_input(int src_len, int dst_len, int exp_ret) uint8_t *dst_buffer = NULL; psa_status_t ret; - TEST_CALLOC(src_buffer, src_len); - TEST_CALLOC(dst_buffer, dst_len); + /* Special case, when src_len or dst_len is 0, we want to test on a real + * buffer. Calling TEST_CALLOC with 0 will return NULL. */ + size_t src_calloc_len = (src_len == 0 ? 1 : src_len); + size_t dst_calloc_len = (dst_len == 0 ? 1 : dst_len); + TEST_CALLOC(src_buffer, src_calloc_len); + TEST_CALLOC(dst_buffer, dst_calloc_len); for (int i = 0; i < src_len; i++) { src_buffer[i] = data[i % sizeof(data)]; @@ -10343,8 +10347,12 @@ void psa_crypto_copy_output(int src_len, int dst_len, int exp_ret) uint8_t *dst_buffer = NULL; psa_status_t ret; - TEST_CALLOC(src_buffer, src_len); - TEST_CALLOC(dst_buffer, dst_len); + /* Special case, when src_len or dst_len is 0, we want to test on a real + * buffer. Calling TEST_CALLOC with 0 will return NULL. */ + size_t src_calloc_len = (src_len == 0 ? 1 : src_len); + size_t dst_calloc_len = (dst_len == 0 ? 1 : dst_len); + TEST_CALLOC(src_buffer, src_calloc_len); + TEST_CALLOC(dst_buffer, dst_calloc_len); for (int i = 0; i < src_len; i++) { src_buffer[i] = data[i % sizeof(data)]; From 03b0472413e8479e01d839e51d05df69312ad882 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 1 Nov 2023 12:30:24 +0000 Subject: [PATCH 062/184] Zero-length test for psa_crypto_alloc_and_copy() Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 9 +++++++ tests/suites/test_suite_psa_crypto.function | 26 +++++++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index be32882513..ac679f9d33 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7448,3 +7448,12 @@ psa_crypto_alloc_and_copy:0:20:1:0:PSA_SUCCESS PSA buffers alloc and copy: null input and output psa_crypto_alloc_and_copy:1:0:1:0:PSA_SUCCESS + +PSA buffers alloc and copy zero-length input +psa_crypto_alloc_and_copy_zero_length:1:0 + +PSA buffers alloc and copy zero-length output +psa_crypto_alloc_and_copy_zero_length:0:1 + +PSA buffers alloc and copy both zero length +psa_crypto_alloc_and_copy_zero_length:1:1 diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 1e9e8d9565..073bb6b4f3 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10412,3 +10412,29 @@ exit: mbedtls_free(buffer_copies.output); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, + int output_zero_length) +{ + uint8_t input_buffer[] = { 0x12 }; + uint8_t output_buffer[] = { 0x34 }; + + psa_crypto_buffer_copy_t buffer_copies; + + size_t input_len = input_zero_length ? 0 : 1; + size_t output_len = output_zero_length ? 0 : 1; + + psa_status_t ret = psa_crypto_alloc_and_copy(input_buffer, input_len, + output_buffer, output_len, + &buffer_copies); + TEST_EQUAL(ret, PSA_SUCCESS); + + TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); + TEST_EQUAL(output_len, buffer_copies.output_len); + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ From 0fee689e574a0be51ac4113564210936d6b7434a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 1 Nov 2023 14:46:02 +0000 Subject: [PATCH 063/184] Simplify zero-length buffers to always be NULL Since it is implementation-dependent whether malloc(0) returns NULL or a pointer, explicitly represent zero-length buffers as NULL in the buffer-copy struct, so as to have a uniform behaviour. Signed-off-by: David Horstmann --- library/psa_crypto.c | 10 ++++++++++ tests/suites/test_suite_psa_crypto.function | 12 ++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index fc5e241900..7b4fc6c9d3 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8473,6 +8473,16 @@ psa_status_t psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_len, * on any pointers safely. */ memset(buffers, 0, sizeof(*buffers)); + /* Since calloc() may return NULL if we try to allocate zero-length + * buffers anyway, deal with this corner case explicitly to ensure + * predictable behaviour. Represent zero-length buffers as NULL. */ + if (input_len == 0) { + input = NULL; + } + if (output_len == 0) { + output = NULL; + } + if (output != NULL) { buffers->output = mbedtls_calloc(output_len, 1); if (buffers->output == NULL) { diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 073bb6b4f3..8057763283 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10430,8 +10430,16 @@ void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, &buffer_copies); TEST_EQUAL(ret, PSA_SUCCESS); - TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); - TEST_EQUAL(output_len, buffer_copies.output_len); + if (input_zero_length) { + TEST_ASSERT(buffer_copies.input == NULL); + } else { + TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); + } + if (output_zero_length) { + TEST_ASSERT(buffer_copies.output == NULL); + } else { + TEST_EQUAL(output_len, buffer_copies.output_len); + } exit: mbedtls_free(buffer_copies.input); From 9700876520ef3200bcd29783b65e76cdf3c25421 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 1 Nov 2023 15:41:54 +0000 Subject: [PATCH 064/184] Add testcases for psa_crypto_copy_and_free() Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 9 +++ tests/suites/test_suite_psa_crypto.function | 71 +++++++++++++++++++++ 2 files changed, 80 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index ac679f9d33..d6b2942805 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7457,3 +7457,12 @@ psa_crypto_alloc_and_copy_zero_length:0:1 PSA buffers alloc and copy both zero length psa_crypto_alloc_and_copy_zero_length:1:1 + +PSA buffers copy and free +psa_crypto_copy_and_free:0:20:0:20:0:PSA_SUCCESS + +PSA buffers copy and free, null input +psa_crypto_copy_and_free:1:0:0:20:0:PSA_SUCCESS + +PSA buffers copy and free, null output +psa_crypto_copy_and_free:0:20:1:0:0:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 8057763283..4d945c3ff6 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10446,3 +10446,74 @@ exit: mbedtls_free(buffer_copies.output); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_copy_and_free(int input_null, int input_len, + int output_null, int output_len, + int orig_output_null, + int exp_ret) +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + psa_crypto_buffer_copy_t buffer_copies = { 0 }; + + uint8_t *input = NULL; + uint8_t *output = NULL; + uint8_t *output_for_comparison = NULL; + uint8_t *orig_output = NULL; + size_t calloc_len; + psa_status_t ret; + + if (!input_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = input_len == 0 ? 1 : input_len; + TEST_CALLOC(input, calloc_len); + } + if (!output_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = output_len == 0 ? 1 : output_len; + TEST_CALLOC(output, calloc_len); + TEST_CALLOC(output_for_comparison, calloc_len); + + for (int i = 0; i < output_len; i++) { + output[i] = data[i % sizeof(data)]; + } + /* We expect the output buffer to be freed, so keep a copy + * for comparison. */ + memcpy(output_for_comparison, output, output_len); + } + if (!orig_output_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = output_len == 0 ? 1 : output_len; + TEST_CALLOC(orig_output, calloc_len); + } + + buffer_copies.input = input; + buffer_copies.input_len = input_len; + buffer_copies.output = output; + buffer_copies.output_len = output_len; + buffer_copies.output_original = orig_output; + + ret = psa_crypto_copy_and_free(&buffer_copies); + + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == PSA_SUCCESS) { + TEST_ASSERT(buffer_copies.input == NULL); + TEST_ASSERT(buffer_copies.output == NULL); + + if (!output_null) { + TEST_MEMORY_COMPARE(output_for_comparison, output_len, + buffer_copies.output_original, buffer_copies.output_len); + } + } + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); + mbedtls_free(output_for_comparison); + mbedtls_free(orig_output); +} +/* END_CASE */ From f4bbb632cdf52aca747e7eed306413b836519321 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 1 Nov 2023 17:47:39 +0000 Subject: [PATCH 065/184] Add implementation of psa_crypto_copy_and_free() Signed-off-by: David Horstmann --- library/psa_crypto.c | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 7b4fc6c9d3..25cc23893f 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8519,4 +8519,18 @@ error: return ret; } +psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers) +{ + if (buffers->output != NULL) { + memcpy(buffers->output_original, buffers->output, buffers->output_len); + } + + mbedtls_free(buffers->input); + buffers->input = NULL; + mbedtls_free(buffers->output); + buffers->output = NULL; + + return PSA_SUCCESS; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 2b79cbaa17ce2e190888b13c95b961668cc22d8e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 1 Nov 2023 17:55:43 +0000 Subject: [PATCH 066/184] Reject NULL original_output with non-NULL output If we have a copy buffer but no original to copy back to, there is not much sensible we can do. The psa_crypto_buffer_copy_t state is invalid. Signed-off-by: David Horstmann --- library/psa_crypto.c | 6 ++++++ tests/suites/test_suite_psa_crypto.data | 6 ++++++ 2 files changed, 12 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 25cc23893f..02aa0eb279 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8522,6 +8522,12 @@ error: psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers) { if (buffers->output != NULL) { + if (buffers->output_original == NULL) { + /* Output is non-NULL but original output is NULL. The argument + * buffers is invalid. Return an error as we have no original to + * copy back to. */ + return PSA_ERROR_INVALID_ARGUMENT; + } memcpy(buffers->output_original, buffers->output, buffers->output_len); } diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index d6b2942805..81ad333b67 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7466,3 +7466,9 @@ psa_crypto_copy_and_free:1:0:0:20:0:PSA_SUCCESS PSA buffers copy and free, null output psa_crypto_copy_and_free:0:20:1:0:0:PSA_SUCCESS + +PSA buffers copy and free, null output_original +psa_crypto_copy_and_free:0:20:0:20:1:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers copy and free, null output_original and null output +psa_crypto_copy_and_free:0:20:1:0:1:PSA_SUCCESS From 72ab8ad44a386e1aa0cb20ae60379afccffb7d09 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 2 Nov 2023 12:00:02 +0000 Subject: [PATCH 067/184] Reject zero-lengths in psa_crypto_copy_and_free() Zero-length buffers should be represented in the psa_crypto_buffer_copy_t struct as NULL if it was created in psa_crypto_alloc_and_copy(), so reject non-NULL zero-length buffers. Signed-off-by: David Horstmann --- library/psa_crypto.c | 10 ++++++++++ tests/suites/test_suite_psa_crypto.data | 6 ++++++ 2 files changed, 16 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 02aa0eb279..33068af385 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8521,7 +8521,17 @@ error: psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers) { + if ((buffers->input != NULL) && (buffers->input_len == 0)) { + /* Reject zero-length buffers, these should have been represented by + * NULL in psa_crypto_alloc_and_copy() */ + return PSA_ERROR_INVALID_ARGUMENT; + } if (buffers->output != NULL) { + if (buffers->output_len == 0) { + /* Reject zero-length buffers, these should have been represented + * by NULL in psa_crypto_alloc_and_copy() */ + return PSA_ERROR_INVALID_ARGUMENT; + } if (buffers->output_original == NULL) { /* Output is non-NULL but original output is NULL. The argument * buffers is invalid. Return an error as we have no original to diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 81ad333b67..f27a9beab4 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7472,3 +7472,9 @@ psa_crypto_copy_and_free:0:20:0:20:1:PSA_ERROR_INVALID_ARGUMENT PSA buffers copy and free, null output_original and null output psa_crypto_copy_and_free:0:20:1:0:1:PSA_SUCCESS + +PSA buffers copy and free, zero-length input +psa_crypto_copy_and_free:0:0:0:20:0:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers copy and free, zero-length output +psa_crypto_copy_and_free:20:0:0:0:0:PSA_ERROR_INVALID_ARGUMENT From 5b9c21756a96360a716ef7735f1149dc291039f2 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 2 Nov 2023 14:39:00 +0000 Subject: [PATCH 068/184] Add test case for overlapping buffers Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 15 +++ tests/suites/test_suite_psa_crypto.function | 103 ++++++++++++++++++++ 2 files changed, 118 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index f27a9beab4..948196e052 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7458,6 +7458,21 @@ psa_crypto_alloc_and_copy_zero_length:0:1 PSA buffers alloc and copy both zero length psa_crypto_alloc_and_copy_zero_length:1:1 +PSA buffers alloc and copy overlapping input first +psa_crypto_alloc_and_copy_overlapping:20:20:5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping output first +psa_crypto_alloc_and_copy_overlapping:20:20:-5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping output within input +psa_crypto_alloc_and_copy_overlapping:20:10:5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping input within output +psa_crypto_alloc_and_copy_overlapping:10:20:-5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping input equals output +psa_crypto_alloc_and_copy_overlapping:20:20:0:PSA_SUCCESS + PSA buffers copy and free psa_crypto_copy_and_free:0:20:0:20:0:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 4d945c3ff6..600c8ea22e 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -1253,6 +1253,63 @@ static void interruptible_signverify_get_minmax_completes(uint32_t max_ops, } #endif /* MBEDTLS_ECP_RESTARTABLE */ +/* Helper to get 2 buffers that overlap by the specified amount. + * The parameter ptr_diff may be negative, in which case the start of + * buf2 is allocated before the start of buf1. */ +static int setup_overlapping_buffers(size_t buf1_len, size_t buf2_len, + int ptr_diff, + uint8_t **full_buffer, + uint8_t **buf1, uint8_t **buf2) +{ + size_t total_len; + int buf1_offset; + int buf2_offset; + + *full_buffer = NULL; + *buf1 = NULL; + *buf2 = NULL; + + if (ptr_diff >= 0) { + /* + * |---------- buf1 ----------| + * <-- ptr_diff -->|---------- buf2 ----------| + */ + total_len = ptr_diff + buf2_len; + if (buf1_len > total_len) { + total_len = buf1_len; + } + buf1_offset = 0; + buf2_offset = ptr_diff; + } else { + /* + * <-- (-ptr_diff) -->|---------- buf1 ----------| + * |---------- buf2 ----------| + */ + total_len = (-ptr_diff) + buf1_len; + if (buf2_len > total_len) { + total_len = buf2_len; + } + buf1_offset = -ptr_diff; + buf2_offset = 0; + } + + /* Edge case: if the length is zero, allocate a 1-byte buffer to avoid + * calloc returning NULL. */ + if (total_len == 0) { + total_len = 1; + } + + TEST_CALLOC(*full_buffer, total_len); + + *buf1 = *full_buffer + buf1_offset; + *buf2 = *full_buffer + buf2_offset; + + return 0; + +exit: + return -1; +} + /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -10447,6 +10504,52 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +/* Ensure that overlapping buffers can be copied correctly. */ +void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, + int ptr_diff, int exp_ret) +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + psa_crypto_buffer_copy_t buffer_copies = { 0 }; + + uint8_t *full_buffer = NULL; + uint8_t *input = NULL; + uint8_t *output = NULL; + + psa_status_t ret; + + TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, + &full_buffer, &input, &output), 0); + + for (int i = 0; i < input_len; i++) { + input[i] = data[i % sizeof(data)]; + } + + ret = psa_crypto_alloc_and_copy(input, input_len, output, output_len, + &buffer_copies); + + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == PSA_SUCCESS) { + if (input_len == 0) { + TEST_ASSERT(buffer_copies.input == NULL); + } else { + TEST_MEMORY_COMPARE(input, input_len, buffer_copies.input, buffer_copies.input_len); + } + if (output_len == 0) { + TEST_ASSERT(buffer_copies.output == NULL); + } else { + TEST_EQUAL(output_len, buffer_copies.output_len); + } + } + +exit: + mbedtls_free(full_buffer); + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ + /* BEGIN_CASE */ void psa_crypto_copy_and_free(int input_null, int input_len, int output_null, int output_len, From 70fda48670eeb63e91f8f71d8e27bc4ad595bd1d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 2 Nov 2023 17:07:16 +0000 Subject: [PATCH 069/184] Add full round-trip tests for buffer copying Test that a buffer pair can be created with psa_crypto_alloc_and_copy() and destroyed with psa_crypto_copy_and_free() correctly. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 3 ++ tests/suites/test_suite_psa_crypto.function | 44 +++++++++++++++++++++ 2 files changed, 47 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 948196e052..d197023394 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7493,3 +7493,6 @@ psa_crypto_copy_and_free:0:0:0:20:0:PSA_ERROR_INVALID_ARGUMENT PSA buffers copy and free, zero-length output psa_crypto_copy_and_free:20:0:0:0:0:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers round-trip +psa_crypto_buffer_copy_round_trip diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 600c8ea22e..23d25eddb4 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10620,3 +10620,47 @@ exit: mbedtls_free(orig_output); } /* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_buffer_copy_round_trip() +{ + uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; + uint8_t input[100]; + uint8_t output[100]; + uint8_t output_for_comparison[100]; + psa_crypto_buffer_copy_t buffer_copies = { 0 }; + psa_status_t ret; + + for (size_t i = 0; i < sizeof(input); i++) { + input[i] = data[i % sizeof(data)]; + } + + ret = psa_crypto_alloc_and_copy(input, sizeof(input), + output, sizeof(output), + &buffer_copies); + + TEST_ASSERT(ret == PSA_SUCCESS); + TEST_MEMORY_COMPARE(input, sizeof(input), + buffer_copies.input, buffer_copies.input_len); + TEST_EQUAL(sizeof(output), buffer_copies.output_len); + + /* Simulate the PSA function filling the (internal) output buffer. */ + for (size_t i = 0; i < buffer_copies.output_len; i++) { + buffer_copies.output[i] = data[i % sizeof(data)]; + } + /* Make a copy of output to compare the copy-back */ + memcpy(output_for_comparison, buffer_copies.output, + sizeof(output_for_comparison)); + + ret = psa_crypto_copy_and_free(&buffer_copies); + + TEST_EQUAL(ret, PSA_SUCCESS); + /* Check that the output was copied back correctly. */ + TEST_MEMORY_COMPARE(output_for_comparison, sizeof(output_for_comparison), + output, sizeof(output)); + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ From 8f77dc7f681951b3f1690c58ff6c17d29d4ca0b8 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 2 Nov 2023 17:48:07 +0000 Subject: [PATCH 070/184] Refactor: move buffer pattern fills into helper Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 51 +++++++++------------ 1 file changed, 21 insertions(+), 30 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 23d25eddb4..d58864f593 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -1253,6 +1253,18 @@ static void interruptible_signverify_get_minmax_completes(uint32_t max_ops, } #endif /* MBEDTLS_ECP_RESTARTABLE */ +/* Helper to fill a buffer with a data pattern. The pattern is not + * important, it just allows a basic check that the correct thing has + * been written, in a way that will detect an error in offset. */ +static void fill_buffer_pattern(uint8_t *buffer, size_t len) +{ + uint8_t data[] = { 0x12, 0x34, 0x56, 0x78 }; + + for (size_t i = 0; i < len; i++) { + buffer[i] = data[i % sizeof(data)]; + } +} + /* Helper to get 2 buffers that overlap by the specified amount. * The parameter ptr_diff may be negative, in which case the start of * buf2 is allocated before the start of buf1. */ @@ -10366,7 +10378,6 @@ void ecjpake_size_macros() /* BEGIN_CASE */ void psa_crypto_copy_input(int src_len, int dst_len, int exp_ret) { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; psa_status_t ret; @@ -10378,9 +10389,7 @@ void psa_crypto_copy_input(int src_len, int dst_len, int exp_ret) TEST_CALLOC(src_buffer, src_calloc_len); TEST_CALLOC(dst_buffer, dst_calloc_len); - for (int i = 0; i < src_len; i++) { - src_buffer[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(src_buffer, src_len); ret = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); TEST_EQUAL((int) ret, exp_ret); @@ -10399,7 +10408,6 @@ exit: /* BEGIN_CASE */ void psa_crypto_copy_output(int src_len, int dst_len, int exp_ret) { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; psa_status_t ret; @@ -10411,9 +10419,7 @@ void psa_crypto_copy_output(int src_len, int dst_len, int exp_ret) TEST_CALLOC(src_buffer, src_calloc_len); TEST_CALLOC(dst_buffer, dst_calloc_len); - for (int i = 0; i < src_len; i++) { - src_buffer[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(src_buffer, src_len); ret = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); TEST_EQUAL((int) ret, exp_ret); @@ -10434,7 +10440,6 @@ void psa_crypto_alloc_and_copy(int input_null, int input_len, int output_null, int output_len, int exp_ret) { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; uint8_t *input_buffer = NULL; uint8_t *output_buffer = NULL; psa_crypto_buffer_copy_t buffer_copies = { 0 }; @@ -10442,15 +10447,11 @@ void psa_crypto_alloc_and_copy(int input_null, int input_len, if (!input_null) { TEST_CALLOC(input_buffer, input_len); - for (int i = 0; i < input_len; i++) { - input_buffer[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(input_buffer, input_len); } if (!output_null) { TEST_CALLOC(output_buffer, output_len); - for (int i = 0; i < output_len; i++) { - output_buffer[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(output_buffer, output_len); } ret = psa_crypto_alloc_and_copy(input_buffer, input_len, output_buffer, output_len, @@ -10509,7 +10510,6 @@ exit: void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, int ptr_diff, int exp_ret) { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; psa_crypto_buffer_copy_t buffer_copies = { 0 }; uint8_t *full_buffer = NULL; @@ -10521,9 +10521,7 @@ void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, &full_buffer, &input, &output), 0); - for (int i = 0; i < input_len; i++) { - input[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(input, input_len); ret = psa_crypto_alloc_and_copy(input, input_len, output, output_len, &buffer_copies); @@ -10556,7 +10554,6 @@ void psa_crypto_copy_and_free(int input_null, int input_len, int orig_output_null, int exp_ret) { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; psa_crypto_buffer_copy_t buffer_copies = { 0 }; uint8_t *input = NULL; @@ -10579,9 +10576,7 @@ void psa_crypto_copy_and_free(int input_null, int input_len, TEST_CALLOC(output, calloc_len); TEST_CALLOC(output_for_comparison, calloc_len); - for (int i = 0; i < output_len; i++) { - output[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(output, output_len); /* We expect the output buffer to be freed, so keep a copy * for comparison. */ memcpy(output_for_comparison, output, output_len); @@ -10624,16 +10619,13 @@ exit: /* BEGIN_CASE */ void psa_crypto_buffer_copy_round_trip() { - uint8_t data[] = {0x12, 0x34, 0x56, 0x78}; uint8_t input[100]; uint8_t output[100]; uint8_t output_for_comparison[100]; psa_crypto_buffer_copy_t buffer_copies = { 0 }; psa_status_t ret; - for (size_t i = 0; i < sizeof(input); i++) { - input[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(input, sizeof(input)); ret = psa_crypto_alloc_and_copy(input, sizeof(input), output, sizeof(output), @@ -10645,9 +10637,8 @@ void psa_crypto_buffer_copy_round_trip() TEST_EQUAL(sizeof(output), buffer_copies.output_len); /* Simulate the PSA function filling the (internal) output buffer. */ - for (size_t i = 0; i < buffer_copies.output_len; i++) { - buffer_copies.output[i] = data[i % sizeof(data)]; - } + fill_buffer_pattern(buffer_copies.output, buffer_copies.output_len); + /* Make a copy of output to compare the copy-back */ memcpy(output_for_comparison, buffer_copies.output, sizeof(output_for_comparison)); From 676cfdd0eab65abb30dc24f30d63985d3da76ef2 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 2 Nov 2023 20:47:04 +0000 Subject: [PATCH 071/184] Replace compound-initializers with memset This should eliminate some pedantic compiler warnings. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 23 ++++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index d58864f593..1dab4e055f 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10442,9 +10442,11 @@ void psa_crypto_alloc_and_copy(int input_null, int input_len, { uint8_t *input_buffer = NULL; uint8_t *output_buffer = NULL; - psa_crypto_buffer_copy_t buffer_copies = { 0 }; psa_status_t ret; + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + if (!input_null) { TEST_CALLOC(input_buffer, input_len); fill_buffer_pattern(input_buffer, input_len); @@ -10478,11 +10480,12 @@ void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, uint8_t input_buffer[] = { 0x12 }; uint8_t output_buffer[] = { 0x34 }; - psa_crypto_buffer_copy_t buffer_copies; - size_t input_len = input_zero_length ? 0 : 1; size_t output_len = output_zero_length ? 0 : 1; + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + psa_status_t ret = psa_crypto_alloc_and_copy(input_buffer, input_len, output_buffer, output_len, &buffer_copies); @@ -10510,14 +10513,15 @@ exit: void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, int ptr_diff, int exp_ret) { - psa_crypto_buffer_copy_t buffer_copies = { 0 }; - uint8_t *full_buffer = NULL; uint8_t *input = NULL; uint8_t *output = NULL; psa_status_t ret; + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, &full_buffer, &input, &output), 0); @@ -10554,8 +10558,6 @@ void psa_crypto_copy_and_free(int input_null, int input_len, int orig_output_null, int exp_ret) { - psa_crypto_buffer_copy_t buffer_copies = { 0 }; - uint8_t *input = NULL; uint8_t *output = NULL; uint8_t *output_for_comparison = NULL; @@ -10563,6 +10565,9 @@ void psa_crypto_copy_and_free(int input_null, int input_len, size_t calloc_len; psa_status_t ret; + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + if (!input_null) { /* If zero-length, ensure we actually allocate something * rather than getting NULL. */ @@ -10622,9 +10627,11 @@ void psa_crypto_buffer_copy_round_trip() uint8_t input[100]; uint8_t output[100]; uint8_t output_for_comparison[100]; - psa_crypto_buffer_copy_t buffer_copies = { 0 }; psa_status_t ret; + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + fill_buffer_pattern(input, sizeof(input)); ret = psa_crypto_alloc_and_copy(input, sizeof(input), From 8995b50cf4aabf76227d3feacc192fcee95b5145 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:20:33 +0000 Subject: [PATCH 072/184] Remove superfluous comment Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 1dab4e055f..890a0448a8 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -13,7 +13,6 @@ #include "psa/crypto.h" #include "psa_crypto_slot_management.h" -/* For psa_can_do_hash() and buffer copying functions */ #include "psa_crypto_core.h" #include "test/asn1_helpers.h" From ac12d2dc69451dda421586cc9f4fead37b2ef582 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:23:49 +0000 Subject: [PATCH 073/184] Remove psa_crypto_ prefix from test functions This ensures they have a different name to the functions they test. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 20 ++++++++++---------- tests/suites/test_suite_psa_crypto.function | 4 ++-- 2 files changed, 12 insertions(+), 12 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index d197023394..6b112462b4 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7408,34 +7408,34 @@ PSA derive persistent key: HKDF SHA-256, exportable persistent_key_load_key_from_storage:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_KEY_TYPE_RAW_DATA:1024:PSA_KEY_USAGE_EXPORT:0:DERIVE_KEY PSA input buffer copy: straightforward copy -psa_crypto_copy_input:20:20:PSA_SUCCESS +copy_input:20:20:PSA_SUCCESS PSA input buffer copy: copy buffer larger than required -psa_crypto_copy_input:10:20:PSA_SUCCESS +copy_input:10:20:PSA_SUCCESS PSA input buffer copy: copy buffer too small -psa_crypto_copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL +copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL PSA input buffer copy: zero-length source buffer -psa_crypto_copy_input:0:10:PSA_SUCCESS +copy_input:0:10:PSA_SUCCESS PSA input buffer copy: zero-length both buffers -psa_crypto_copy_input:0:0:PSA_SUCCESS +copy_input:0:0:PSA_SUCCESS PSA output buffer copy: straightforward copy -psa_crypto_copy_output:20:20:PSA_SUCCESS +copy_output:20:20:PSA_SUCCESS PSA output buffer copy: output buffer larger than required -psa_crypto_copy_output:10:20:PSA_SUCCESS +copy_output:10:20:PSA_SUCCESS PSA output buffer copy: output buffer too small -psa_crypto_copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL +copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL PSA output buffer copy: zero-length source buffer -psa_crypto_copy_output:0:10:PSA_SUCCESS +copy_output:0:10:PSA_SUCCESS PSA output buffer copy: zero-length both buffers -psa_crypto_copy_output:0:0:PSA_SUCCESS +copy_output:0:0:PSA_SUCCESS PSA buffers alloc and copy psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 890a0448a8..4f57e05229 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10375,7 +10375,7 @@ void ecjpake_size_macros() /* END_CASE */ /* BEGIN_CASE */ -void psa_crypto_copy_input(int src_len, int dst_len, int exp_ret) +void copy_input(int src_len, int dst_len, int exp_ret) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; @@ -10405,7 +10405,7 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void psa_crypto_copy_output(int src_len, int dst_len, int exp_ret) +void copy_output(int src_len, int dst_len, int exp_ret) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; From 8075c7faf7a9cf828756530fd46332e4298df435 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:28:08 +0000 Subject: [PATCH 074/184] Switch from int to psa_status_t for test args Remove unnecessary casts as well. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 4f57e05229..6f757fd3fb 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10375,7 +10375,7 @@ void ecjpake_size_macros() /* END_CASE */ /* BEGIN_CASE */ -void copy_input(int src_len, int dst_len, int exp_ret) +void copy_input(int src_len, int dst_len, psa_status_t exp_ret) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; @@ -10391,9 +10391,9 @@ void copy_input(int src_len, int dst_len, int exp_ret) fill_buffer_pattern(src_buffer, src_len); ret = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL((int) ret, exp_ret); + TEST_EQUAL(ret, exp_ret); - if (exp_ret == (int) PSA_SUCCESS) { + if (exp_ret == PSA_SUCCESS) { /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } @@ -10405,7 +10405,7 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void copy_output(int src_len, int dst_len, int exp_ret) +void copy_output(int src_len, int dst_len, psa_status_t exp_ret) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; @@ -10421,9 +10421,9 @@ void copy_output(int src_len, int dst_len, int exp_ret) fill_buffer_pattern(src_buffer, src_len); ret = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL((int) ret, exp_ret); + TEST_EQUAL(ret, exp_ret); - if (exp_ret == (int) PSA_SUCCESS) { + if (exp_ret == PSA_SUCCESS) { /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } From b8381513c19a440c2e28c4c14ecc10e6a96cce2d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:31:35 +0000 Subject: [PATCH 075/184] Switch from ret to status as naming convention Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 6f757fd3fb..8747660391 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10375,11 +10375,11 @@ void ecjpake_size_macros() /* END_CASE */ /* BEGIN_CASE */ -void copy_input(int src_len, int dst_len, psa_status_t exp_ret) +void copy_input(int src_len, int dst_len, psa_status_t exp_status) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; - psa_status_t ret; + psa_status_t status; /* Special case, when src_len or dst_len is 0, we want to test on a real * buffer. Calling TEST_CALLOC with 0 will return NULL. */ @@ -10390,10 +10390,10 @@ void copy_input(int src_len, int dst_len, psa_status_t exp_ret) fill_buffer_pattern(src_buffer, src_len); - ret = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL(ret, exp_ret); + status = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL(status, exp_status); - if (exp_ret == PSA_SUCCESS) { + if (exp_status == PSA_SUCCESS) { /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } @@ -10405,11 +10405,11 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void copy_output(int src_len, int dst_len, psa_status_t exp_ret) +void copy_output(int src_len, int dst_len, psa_status_t exp_status) { uint8_t *src_buffer = NULL; uint8_t *dst_buffer = NULL; - psa_status_t ret; + psa_status_t status; /* Special case, when src_len or dst_len is 0, we want to test on a real * buffer. Calling TEST_CALLOC with 0 will return NULL. */ @@ -10420,10 +10420,10 @@ void copy_output(int src_len, int dst_len, psa_status_t exp_ret) fill_buffer_pattern(src_buffer, src_len); - ret = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL(ret, exp_ret); + status = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL(status, exp_status); - if (exp_ret == PSA_SUCCESS) { + if (exp_status == PSA_SUCCESS) { /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } From 86cdc7646de5ace463d8727c0e36827bc796f198 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:45:39 +0000 Subject: [PATCH 076/184] Switch to TEST_CALLOC_NONNULL() This removes some gubbins related to making sure the buffer is not NULL that was previously cluttering the test case. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 8747660391..41d7009296 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10381,12 +10381,8 @@ void copy_input(int src_len, int dst_len, psa_status_t exp_status) uint8_t *dst_buffer = NULL; psa_status_t status; - /* Special case, when src_len or dst_len is 0, we want to test on a real - * buffer. Calling TEST_CALLOC with 0 will return NULL. */ - size_t src_calloc_len = (src_len == 0 ? 1 : src_len); - size_t dst_calloc_len = (dst_len == 0 ? 1 : dst_len); - TEST_CALLOC(src_buffer, src_calloc_len); - TEST_CALLOC(dst_buffer, dst_calloc_len); + TEST_CALLOC_NONNULL(src_buffer, src_len); + TEST_CALLOC_NONNULL(dst_buffer, dst_len); fill_buffer_pattern(src_buffer, src_len); @@ -10411,12 +10407,8 @@ void copy_output(int src_len, int dst_len, psa_status_t exp_status) uint8_t *dst_buffer = NULL; psa_status_t status; - /* Special case, when src_len or dst_len is 0, we want to test on a real - * buffer. Calling TEST_CALLOC with 0 will return NULL. */ - size_t src_calloc_len = (src_len == 0 ? 1 : src_len); - size_t dst_calloc_len = (dst_len == 0 ? 1 : dst_len); - TEST_CALLOC(src_buffer, src_calloc_len); - TEST_CALLOC(dst_buffer, dst_calloc_len); + TEST_CALLOC_NONNULL(src_buffer, src_len); + TEST_CALLOC_NONNULL(dst_buffer, dst_len); fill_buffer_pattern(src_buffer, src_len); From 49a7276c497b466d4eea7dd283ba940dbb297c2a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 19:51:40 +0000 Subject: [PATCH 077/184] Switch error code to more appropriate value Since we are internal rather than user-facing, PSA_ERROR_CORRUPTION_DETECTED makes more sense than PSA_ERROR_BUFFER_TOO_SMALL. Whilst it really is a buffer that is too small, this error code is intended to indicate that a user-supplied buffer is too small, not an internal one. Signed-off-by: David Horstmann --- library/psa_crypto.c | 4 ++-- library/psa_crypto_core.h | 6 +++--- tests/suites/test_suite_psa_crypto.data | 4 ++-- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 33068af385..09180b3c3a 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8446,7 +8446,7 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, uint8_t *input_copy, size_t input_copy_len) { if (input_len > input_copy_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return PSA_ERROR_CORRUPTION_DETECTED; } memcpy(input_copy, input, input_len); @@ -8458,7 +8458,7 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co uint8_t *output, size_t output_len) { if (output_len < output_copy_len) { - return PSA_ERROR_BUFFER_TOO_SMALL; + return PSA_ERROR_CORRUPTION_DETECTED; } memcpy(output, output_copy, output_copy_len); return PSA_SUCCESS; diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 78550f8ee5..00d9e9eedd 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -862,8 +862,8 @@ psa_status_t mbedtls_psa_verify_hash_abort( * \param[out] input_copy_len Length of the local copy buffer. * \return #PSA_SUCCESS, if the buffer was successfully * copied. - * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the local copy - * is too small to hold contents of the + * \return #PSA_ERROR_CORRUPTION_DETECTED, if the local + * copy is too small to hold contents of the * input buffer. */ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, @@ -877,7 +877,7 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, * \param[out] output_len Length of the user-supplied output buffer. * \return #PSA_SUCCESS, if the buffer was successfully * copied. - * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the + * \return #PSA_ERROR_CORRUPTION_DETECTED, if the * user-supplied output buffer is too small to * hold the contents of the local buffer. */ diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 6b112462b4..594c609e3c 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7414,7 +7414,7 @@ PSA input buffer copy: copy buffer larger than required copy_input:10:20:PSA_SUCCESS PSA input buffer copy: copy buffer too small -copy_input:20:10:PSA_ERROR_BUFFER_TOO_SMALL +copy_input:20:10:PSA_ERROR_CORRUPTION_DETECTED PSA input buffer copy: zero-length source buffer copy_input:0:10:PSA_SUCCESS @@ -7429,7 +7429,7 @@ PSA output buffer copy: output buffer larger than required copy_output:10:20:PSA_SUCCESS PSA output buffer copy: output buffer too small -copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL +copy_output:20:10:PSA_ERROR_CORRUPTION_DETECTED PSA output buffer copy: zero-length source buffer copy_output:0:10:PSA_SUCCESS From ad33ab376b619fffeb6d2b4a8f10f22bf8994c33 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 3 Nov 2023 20:01:37 +0000 Subject: [PATCH 078/184] Move buffer copy tests into new testsuite Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.data | 90 ----- tests/suites/test_suite_psa_crypto.function | 348 ----------------- .../suites/test_suite_psa_crypto_memory.data | 89 +++++ .../test_suite_psa_crypto_memory.function | 365 ++++++++++++++++++ 4 files changed, 454 insertions(+), 438 deletions(-) create mode 100644 tests/suites/test_suite_psa_crypto_memory.data create mode 100644 tests/suites/test_suite_psa_crypto_memory.function diff --git a/tests/suites/test_suite_psa_crypto.data b/tests/suites/test_suite_psa_crypto.data index 594c609e3c..7b1974865a 100644 --- a/tests/suites/test_suite_psa_crypto.data +++ b/tests/suites/test_suite_psa_crypto.data @@ -7406,93 +7406,3 @@ persistent_key_load_key_from_storage:"":PSA_KEY_TYPE_ECC_KEY_PAIR(PSA_ECC_FAMILY PSA derive persistent key: HKDF SHA-256, exportable persistent_key_load_key_from_storage:"0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b0b":PSA_KEY_TYPE_RAW_DATA:1024:PSA_KEY_USAGE_EXPORT:0:DERIVE_KEY - -PSA input buffer copy: straightforward copy -copy_input:20:20:PSA_SUCCESS - -PSA input buffer copy: copy buffer larger than required -copy_input:10:20:PSA_SUCCESS - -PSA input buffer copy: copy buffer too small -copy_input:20:10:PSA_ERROR_CORRUPTION_DETECTED - -PSA input buffer copy: zero-length source buffer -copy_input:0:10:PSA_SUCCESS - -PSA input buffer copy: zero-length both buffers -copy_input:0:0:PSA_SUCCESS - -PSA output buffer copy: straightforward copy -copy_output:20:20:PSA_SUCCESS - -PSA output buffer copy: output buffer larger than required -copy_output:10:20:PSA_SUCCESS - -PSA output buffer copy: output buffer too small -copy_output:20:10:PSA_ERROR_CORRUPTION_DETECTED - -PSA output buffer copy: zero-length source buffer -copy_output:0:10:PSA_SUCCESS - -PSA output buffer copy: zero-length both buffers -copy_output:0:0:PSA_SUCCESS - -PSA buffers alloc and copy -psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS - -PSA buffers alloc and copy: null input -psa_crypto_alloc_and_copy:1:0:0:20:PSA_SUCCESS - -PSA buffers alloc and copy: null output -psa_crypto_alloc_and_copy:0:20:1:0:PSA_SUCCESS - -PSA buffers alloc and copy: null input and output -psa_crypto_alloc_and_copy:1:0:1:0:PSA_SUCCESS - -PSA buffers alloc and copy zero-length input -psa_crypto_alloc_and_copy_zero_length:1:0 - -PSA buffers alloc and copy zero-length output -psa_crypto_alloc_and_copy_zero_length:0:1 - -PSA buffers alloc and copy both zero length -psa_crypto_alloc_and_copy_zero_length:1:1 - -PSA buffers alloc and copy overlapping input first -psa_crypto_alloc_and_copy_overlapping:20:20:5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping output first -psa_crypto_alloc_and_copy_overlapping:20:20:-5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping output within input -psa_crypto_alloc_and_copy_overlapping:20:10:5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping input within output -psa_crypto_alloc_and_copy_overlapping:10:20:-5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping input equals output -psa_crypto_alloc_and_copy_overlapping:20:20:0:PSA_SUCCESS - -PSA buffers copy and free -psa_crypto_copy_and_free:0:20:0:20:0:PSA_SUCCESS - -PSA buffers copy and free, null input -psa_crypto_copy_and_free:1:0:0:20:0:PSA_SUCCESS - -PSA buffers copy and free, null output -psa_crypto_copy_and_free:0:20:1:0:0:PSA_SUCCESS - -PSA buffers copy and free, null output_original -psa_crypto_copy_and_free:0:20:0:20:1:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers copy and free, null output_original and null output -psa_crypto_copy_and_free:0:20:1:0:1:PSA_SUCCESS - -PSA buffers copy and free, zero-length input -psa_crypto_copy_and_free:0:0:0:20:0:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers copy and free, zero-length output -psa_crypto_copy_and_free:20:0:0:0:0:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers round-trip -psa_crypto_buffer_copy_round_trip diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 41d7009296..be773da8fa 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -1252,75 +1252,6 @@ static void interruptible_signverify_get_minmax_completes(uint32_t max_ops, } #endif /* MBEDTLS_ECP_RESTARTABLE */ -/* Helper to fill a buffer with a data pattern. The pattern is not - * important, it just allows a basic check that the correct thing has - * been written, in a way that will detect an error in offset. */ -static void fill_buffer_pattern(uint8_t *buffer, size_t len) -{ - uint8_t data[] = { 0x12, 0x34, 0x56, 0x78 }; - - for (size_t i = 0; i < len; i++) { - buffer[i] = data[i % sizeof(data)]; - } -} - -/* Helper to get 2 buffers that overlap by the specified amount. - * The parameter ptr_diff may be negative, in which case the start of - * buf2 is allocated before the start of buf1. */ -static int setup_overlapping_buffers(size_t buf1_len, size_t buf2_len, - int ptr_diff, - uint8_t **full_buffer, - uint8_t **buf1, uint8_t **buf2) -{ - size_t total_len; - int buf1_offset; - int buf2_offset; - - *full_buffer = NULL; - *buf1 = NULL; - *buf2 = NULL; - - if (ptr_diff >= 0) { - /* - * |---------- buf1 ----------| - * <-- ptr_diff -->|---------- buf2 ----------| - */ - total_len = ptr_diff + buf2_len; - if (buf1_len > total_len) { - total_len = buf1_len; - } - buf1_offset = 0; - buf2_offset = ptr_diff; - } else { - /* - * <-- (-ptr_diff) -->|---------- buf1 ----------| - * |---------- buf2 ----------| - */ - total_len = (-ptr_diff) + buf1_len; - if (buf2_len > total_len) { - total_len = buf2_len; - } - buf1_offset = -ptr_diff; - buf2_offset = 0; - } - - /* Edge case: if the length is zero, allocate a 1-byte buffer to avoid - * calloc returning NULL. */ - if (total_len == 0) { - total_len = 1; - } - - TEST_CALLOC(*full_buffer, total_len); - - *buf1 = *full_buffer + buf1_offset; - *buf2 = *full_buffer + buf2_offset; - - return 0; - -exit: - return -1; -} - /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -10374,282 +10305,3 @@ void ecjpake_size_macros() } /* END_CASE */ -/* BEGIN_CASE */ -void copy_input(int src_len, int dst_len, psa_status_t exp_status) -{ - uint8_t *src_buffer = NULL; - uint8_t *dst_buffer = NULL; - psa_status_t status; - - TEST_CALLOC_NONNULL(src_buffer, src_len); - TEST_CALLOC_NONNULL(dst_buffer, dst_len); - - fill_buffer_pattern(src_buffer, src_len); - - status = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL(status, exp_status); - - if (exp_status == PSA_SUCCESS) { - /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ - TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); - } - -exit: - mbedtls_free(src_buffer); - mbedtls_free(dst_buffer); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void copy_output(int src_len, int dst_len, psa_status_t exp_status) -{ - uint8_t *src_buffer = NULL; - uint8_t *dst_buffer = NULL; - psa_status_t status; - - TEST_CALLOC_NONNULL(src_buffer, src_len); - TEST_CALLOC_NONNULL(dst_buffer, dst_len); - - fill_buffer_pattern(src_buffer, src_len); - - status = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); - TEST_EQUAL(status, exp_status); - - if (exp_status == PSA_SUCCESS) { - /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ - TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); - } - -exit: - mbedtls_free(src_buffer); - mbedtls_free(dst_buffer); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_alloc_and_copy(int input_null, int input_len, - int output_null, int output_len, - int exp_ret) -{ - uint8_t *input_buffer = NULL; - uint8_t *output_buffer = NULL; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - if (!input_null) { - TEST_CALLOC(input_buffer, input_len); - fill_buffer_pattern(input_buffer, input_len); - } - if (!output_null) { - TEST_CALLOC(output_buffer, output_len); - fill_buffer_pattern(output_buffer, output_len); - } - ret = psa_crypto_alloc_and_copy(input_buffer, input_len, - output_buffer, output_len, - &buffer_copies); - TEST_EQUAL(exp_ret, (int) ret); - - if (exp_ret == PSA_SUCCESS) { - TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); - TEST_EQUAL(output_len, buffer_copies.output_len); - } - -exit: - mbedtls_free(input_buffer); - mbedtls_free(output_buffer); - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, - int output_zero_length) -{ - uint8_t input_buffer[] = { 0x12 }; - uint8_t output_buffer[] = { 0x34 }; - - size_t input_len = input_zero_length ? 0 : 1; - size_t output_len = output_zero_length ? 0 : 1; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - psa_status_t ret = psa_crypto_alloc_and_copy(input_buffer, input_len, - output_buffer, output_len, - &buffer_copies); - TEST_EQUAL(ret, PSA_SUCCESS); - - if (input_zero_length) { - TEST_ASSERT(buffer_copies.input == NULL); - } else { - TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); - } - if (output_zero_length) { - TEST_ASSERT(buffer_copies.output == NULL); - } else { - TEST_EQUAL(output_len, buffer_copies.output_len); - } - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -/* Ensure that overlapping buffers can be copied correctly. */ -void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, - int ptr_diff, int exp_ret) -{ - uint8_t *full_buffer = NULL; - uint8_t *input = NULL; - uint8_t *output = NULL; - - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, - &full_buffer, &input, &output), 0); - - fill_buffer_pattern(input, input_len); - - ret = psa_crypto_alloc_and_copy(input, input_len, output, output_len, - &buffer_copies); - - TEST_EQUAL((int) ret, exp_ret); - - if (exp_ret == PSA_SUCCESS) { - if (input_len == 0) { - TEST_ASSERT(buffer_copies.input == NULL); - } else { - TEST_MEMORY_COMPARE(input, input_len, buffer_copies.input, buffer_copies.input_len); - } - if (output_len == 0) { - TEST_ASSERT(buffer_copies.output == NULL); - } else { - TEST_EQUAL(output_len, buffer_copies.output_len); - } - } - -exit: - mbedtls_free(full_buffer); - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_copy_and_free(int input_null, int input_len, - int output_null, int output_len, - int orig_output_null, - int exp_ret) -{ - uint8_t *input = NULL; - uint8_t *output = NULL; - uint8_t *output_for_comparison = NULL; - uint8_t *orig_output = NULL; - size_t calloc_len; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - if (!input_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = input_len == 0 ? 1 : input_len; - TEST_CALLOC(input, calloc_len); - } - if (!output_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = output_len == 0 ? 1 : output_len; - TEST_CALLOC(output, calloc_len); - TEST_CALLOC(output_for_comparison, calloc_len); - - fill_buffer_pattern(output, output_len); - /* We expect the output buffer to be freed, so keep a copy - * for comparison. */ - memcpy(output_for_comparison, output, output_len); - } - if (!orig_output_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = output_len == 0 ? 1 : output_len; - TEST_CALLOC(orig_output, calloc_len); - } - - buffer_copies.input = input; - buffer_copies.input_len = input_len; - buffer_copies.output = output; - buffer_copies.output_len = output_len; - buffer_copies.output_original = orig_output; - - ret = psa_crypto_copy_and_free(&buffer_copies); - - TEST_EQUAL((int) ret, exp_ret); - - if (exp_ret == PSA_SUCCESS) { - TEST_ASSERT(buffer_copies.input == NULL); - TEST_ASSERT(buffer_copies.output == NULL); - - if (!output_null) { - TEST_MEMORY_COMPARE(output_for_comparison, output_len, - buffer_copies.output_original, buffer_copies.output_len); - } - } - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); - mbedtls_free(output_for_comparison); - mbedtls_free(orig_output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_buffer_copy_round_trip() -{ - uint8_t input[100]; - uint8_t output[100]; - uint8_t output_for_comparison[100]; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - fill_buffer_pattern(input, sizeof(input)); - - ret = psa_crypto_alloc_and_copy(input, sizeof(input), - output, sizeof(output), - &buffer_copies); - - TEST_ASSERT(ret == PSA_SUCCESS); - TEST_MEMORY_COMPARE(input, sizeof(input), - buffer_copies.input, buffer_copies.input_len); - TEST_EQUAL(sizeof(output), buffer_copies.output_len); - - /* Simulate the PSA function filling the (internal) output buffer. */ - fill_buffer_pattern(buffer_copies.output, buffer_copies.output_len); - - /* Make a copy of output to compare the copy-back */ - memcpy(output_for_comparison, buffer_copies.output, - sizeof(output_for_comparison)); - - ret = psa_crypto_copy_and_free(&buffer_copies); - - TEST_EQUAL(ret, PSA_SUCCESS); - /* Check that the output was copied back correctly. */ - TEST_MEMORY_COMPARE(output_for_comparison, sizeof(output_for_comparison), - output, sizeof(output)); - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data new file mode 100644 index 0000000000..33623afdc1 --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -0,0 +1,89 @@ +PSA input buffer copy: straightforward copy +copy_input:20:20:PSA_SUCCESS + +PSA input buffer copy: copy buffer larger than required +copy_input:10:20:PSA_SUCCESS + +PSA input buffer copy: copy buffer too small +copy_input:20:10:PSA_ERROR_CORRUPTION_DETECTED + +PSA input buffer copy: zero-length source buffer +copy_input:0:10:PSA_SUCCESS + +PSA input buffer copy: zero-length both buffers +copy_input:0:0:PSA_SUCCESS + +PSA output buffer copy: straightforward copy +copy_output:20:20:PSA_SUCCESS + +PSA output buffer copy: output buffer larger than required +copy_output:10:20:PSA_SUCCESS + +PSA output buffer copy: output buffer too small +copy_output:20:10:PSA_ERROR_CORRUPTION_DETECTED + +PSA output buffer copy: zero-length source buffer +copy_output:0:10:PSA_SUCCESS + +PSA output buffer copy: zero-length both buffers +copy_output:0:0:PSA_SUCCESS + +PSA buffers alloc and copy +psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS + +PSA buffers alloc and copy: null input +psa_crypto_alloc_and_copy:1:0:0:20:PSA_SUCCESS + +PSA buffers alloc and copy: null output +psa_crypto_alloc_and_copy:0:20:1:0:PSA_SUCCESS + +PSA buffers alloc and copy: null input and output +psa_crypto_alloc_and_copy:1:0:1:0:PSA_SUCCESS + +PSA buffers alloc and copy zero-length input +psa_crypto_alloc_and_copy_zero_length:1:0 + +PSA buffers alloc and copy zero-length output +psa_crypto_alloc_and_copy_zero_length:0:1 + +PSA buffers alloc and copy both zero length +psa_crypto_alloc_and_copy_zero_length:1:1 + +PSA buffers alloc and copy overlapping input first +psa_crypto_alloc_and_copy_overlapping:20:20:5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping output first +psa_crypto_alloc_and_copy_overlapping:20:20:-5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping output within input +psa_crypto_alloc_and_copy_overlapping:20:10:5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping input within output +psa_crypto_alloc_and_copy_overlapping:10:20:-5:PSA_SUCCESS + +PSA buffers alloc and copy overlapping input equals output +psa_crypto_alloc_and_copy_overlapping:20:20:0:PSA_SUCCESS + +PSA buffers copy and free +psa_crypto_copy_and_free:0:20:0:20:0:PSA_SUCCESS + +PSA buffers copy and free, null input +psa_crypto_copy_and_free:1:0:0:20:0:PSA_SUCCESS + +PSA buffers copy and free, null output +psa_crypto_copy_and_free:0:20:1:0:0:PSA_SUCCESS + +PSA buffers copy and free, null output_original +psa_crypto_copy_and_free:0:20:0:20:1:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers copy and free, null output_original and null output +psa_crypto_copy_and_free:0:20:1:0:1:PSA_SUCCESS + +PSA buffers copy and free, zero-length input +psa_crypto_copy_and_free:0:0:0:20:0:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers copy and free, zero-length output +psa_crypto_copy_and_free:20:0:0:0:0:PSA_ERROR_INVALID_ARGUMENT + +PSA buffers round-trip +psa_crypto_buffer_copy_round_trip diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function new file mode 100644 index 0000000000..a27f76b88a --- /dev/null +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -0,0 +1,365 @@ +/* BEGIN_HEADER */ +#include + +#include "common.h" + +#include "psa/crypto.h" + +#include "psa_crypto_core.h" + +#include "test/psa_crypto_helpers.h" + +/* Helper to fill a buffer with a data pattern. The pattern is not + * important, it just allows a basic check that the correct thing has + * been written, in a way that will detect an error in offset. */ +static void fill_buffer_pattern(uint8_t *buffer, size_t len) +{ + uint8_t data[] = { 0x12, 0x34, 0x56, 0x78 }; + + for (size_t i = 0; i < len; i++) { + buffer[i] = data[i % sizeof(data)]; + } +} + +/* Helper to get 2 buffers that overlap by the specified amount. + * The parameter ptr_diff may be negative, in which case the start of + * buf2 is allocated before the start of buf1. */ +static int setup_overlapping_buffers(size_t buf1_len, size_t buf2_len, + int ptr_diff, + uint8_t **full_buffer, + uint8_t **buf1, uint8_t **buf2) +{ + size_t total_len; + int buf1_offset; + int buf2_offset; + + *full_buffer = NULL; + *buf1 = NULL; + *buf2 = NULL; + + if (ptr_diff >= 0) { + /* + * |---------- buf1 ----------| + * <-- ptr_diff -->|---------- buf2 ----------| + */ + total_len = ptr_diff + buf2_len; + if (buf1_len > total_len) { + total_len = buf1_len; + } + buf1_offset = 0; + buf2_offset = ptr_diff; + } else { + /* + * <-- (-ptr_diff) -->|---------- buf1 ----------| + * |---------- buf2 ----------| + */ + total_len = (-ptr_diff) + buf1_len; + if (buf2_len > total_len) { + total_len = buf2_len; + } + buf1_offset = -ptr_diff; + buf2_offset = 0; + } + + /* Edge case: if the length is zero, allocate a 1-byte buffer to avoid + * calloc returning NULL. */ + if (total_len == 0) { + total_len = 1; + } + + TEST_CALLOC(*full_buffer, total_len); + + *buf1 = *full_buffer + buf1_offset; + *buf2 = *full_buffer + buf2_offset; + + return 0; + +exit: + return -1; +} +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES + * depends_on:MBEDTLS_PSA_CRYPTO_C + * END_DEPENDENCIES + */ + +/* BEGIN_CASE */ +void copy_input(int src_len, int dst_len, psa_status_t exp_status) +{ + uint8_t *src_buffer = NULL; + uint8_t *dst_buffer = NULL; + psa_status_t status; + + TEST_CALLOC_NONNULL(src_buffer, src_len); + TEST_CALLOC_NONNULL(dst_buffer, dst_len); + + fill_buffer_pattern(src_buffer, src_len); + + status = psa_crypto_copy_input(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL(status, exp_status); + + if (exp_status == PSA_SUCCESS) { + /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ + TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); + } + +exit: + mbedtls_free(src_buffer); + mbedtls_free(dst_buffer); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void copy_output(int src_len, int dst_len, psa_status_t exp_status) +{ + uint8_t *src_buffer = NULL; + uint8_t *dst_buffer = NULL; + psa_status_t status; + + TEST_CALLOC_NONNULL(src_buffer, src_len); + TEST_CALLOC_NONNULL(dst_buffer, dst_len); + + fill_buffer_pattern(src_buffer, src_len); + + status = psa_crypto_copy_output(src_buffer, src_len, dst_buffer, dst_len); + TEST_EQUAL(status, exp_status); + + if (exp_status == PSA_SUCCESS) { + /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ + TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); + } + +exit: + mbedtls_free(src_buffer); + mbedtls_free(dst_buffer); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_alloc_and_copy(int input_null, int input_len, + int output_null, int output_len, + int exp_ret) +{ + uint8_t *input_buffer = NULL; + uint8_t *output_buffer = NULL; + psa_status_t ret; + + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + + if (!input_null) { + TEST_CALLOC(input_buffer, input_len); + fill_buffer_pattern(input_buffer, input_len); + } + if (!output_null) { + TEST_CALLOC(output_buffer, output_len); + fill_buffer_pattern(output_buffer, output_len); + } + ret = psa_crypto_alloc_and_copy(input_buffer, input_len, + output_buffer, output_len, + &buffer_copies); + TEST_EQUAL(exp_ret, (int) ret); + + if (exp_ret == PSA_SUCCESS) { + TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); + TEST_EQUAL(output_len, buffer_copies.output_len); + } + +exit: + mbedtls_free(input_buffer); + mbedtls_free(output_buffer); + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, + int output_zero_length) +{ + uint8_t input_buffer[] = { 0x12 }; + uint8_t output_buffer[] = { 0x34 }; + + size_t input_len = input_zero_length ? 0 : 1; + size_t output_len = output_zero_length ? 0 : 1; + + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + + psa_status_t ret = psa_crypto_alloc_and_copy(input_buffer, input_len, + output_buffer, output_len, + &buffer_copies); + TEST_EQUAL(ret, PSA_SUCCESS); + + if (input_zero_length) { + TEST_ASSERT(buffer_copies.input == NULL); + } else { + TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); + } + if (output_zero_length) { + TEST_ASSERT(buffer_copies.output == NULL); + } else { + TEST_EQUAL(output_len, buffer_copies.output_len); + } + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ + +/* BEGIN_CASE */ +/* Ensure that overlapping buffers can be copied correctly. */ +void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, + int ptr_diff, int exp_ret) +{ + uint8_t *full_buffer = NULL; + uint8_t *input = NULL; + uint8_t *output = NULL; + + psa_status_t ret; + + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + + TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, + &full_buffer, &input, &output), 0); + + fill_buffer_pattern(input, input_len); + + ret = psa_crypto_alloc_and_copy(input, input_len, output, output_len, + &buffer_copies); + + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == PSA_SUCCESS) { + if (input_len == 0) { + TEST_ASSERT(buffer_copies.input == NULL); + } else { + TEST_MEMORY_COMPARE(input, input_len, buffer_copies.input, buffer_copies.input_len); + } + if (output_len == 0) { + TEST_ASSERT(buffer_copies.output == NULL); + } else { + TEST_EQUAL(output_len, buffer_copies.output_len); + } + } + +exit: + mbedtls_free(full_buffer); + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_copy_and_free(int input_null, int input_len, + int output_null, int output_len, + int orig_output_null, + int exp_ret) +{ + uint8_t *input = NULL; + uint8_t *output = NULL; + uint8_t *output_for_comparison = NULL; + uint8_t *orig_output = NULL; + size_t calloc_len; + psa_status_t ret; + + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + + if (!input_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = input_len == 0 ? 1 : input_len; + TEST_CALLOC(input, calloc_len); + } + if (!output_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = output_len == 0 ? 1 : output_len; + TEST_CALLOC(output, calloc_len); + TEST_CALLOC(output_for_comparison, calloc_len); + + fill_buffer_pattern(output, output_len); + /* We expect the output buffer to be freed, so keep a copy + * for comparison. */ + memcpy(output_for_comparison, output, output_len); + } + if (!orig_output_null) { + /* If zero-length, ensure we actually allocate something + * rather than getting NULL. */ + calloc_len = output_len == 0 ? 1 : output_len; + TEST_CALLOC(orig_output, calloc_len); + } + + buffer_copies.input = input; + buffer_copies.input_len = input_len; + buffer_copies.output = output; + buffer_copies.output_len = output_len; + buffer_copies.output_original = orig_output; + + ret = psa_crypto_copy_and_free(&buffer_copies); + + TEST_EQUAL((int) ret, exp_ret); + + if (exp_ret == PSA_SUCCESS) { + TEST_ASSERT(buffer_copies.input == NULL); + TEST_ASSERT(buffer_copies.output == NULL); + + if (!output_null) { + TEST_MEMORY_COMPARE(output_for_comparison, output_len, + buffer_copies.output_original, buffer_copies.output_len); + } + } + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); + mbedtls_free(output_for_comparison); + mbedtls_free(orig_output); +} +/* END_CASE */ + +/* BEGIN_CASE */ +void psa_crypto_buffer_copy_round_trip() +{ + uint8_t input[100]; + uint8_t output[100]; + uint8_t output_for_comparison[100]; + psa_status_t ret; + + psa_crypto_buffer_copy_t buffer_copies; + memset(&buffer_copies, 0, sizeof(buffer_copies)); + + fill_buffer_pattern(input, sizeof(input)); + + ret = psa_crypto_alloc_and_copy(input, sizeof(input), + output, sizeof(output), + &buffer_copies); + + TEST_ASSERT(ret == PSA_SUCCESS); + TEST_MEMORY_COMPARE(input, sizeof(input), + buffer_copies.input, buffer_copies.input_len); + TEST_EQUAL(sizeof(output), buffer_copies.output_len); + + /* Simulate the PSA function filling the (internal) output buffer. */ + fill_buffer_pattern(buffer_copies.output, buffer_copies.output_len); + + /* Make a copy of output to compare the copy-back */ + memcpy(output_for_comparison, buffer_copies.output, + sizeof(output_for_comparison)); + + ret = psa_crypto_copy_and_free(&buffer_copies); + + TEST_EQUAL(ret, PSA_SUCCESS); + /* Check that the output was copied back correctly. */ + TEST_MEMORY_COMPARE(output_for_comparison, sizeof(output_for_comparison), + output, sizeof(output)); + +exit: + mbedtls_free(buffer_copies.input); + mbedtls_free(buffer_copies.output); +} +/* END_CASE */ From 7dd8205423c443d149798f7cc95b8c6910a6b185 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 6 Nov 2023 17:43:40 +0000 Subject: [PATCH 079/184] Remove extra blank line at end of file (This causes code style checks to fail) Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto.function | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index be773da8fa..4e8853d8e4 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -10304,4 +10304,3 @@ void ecjpake_size_macros() PSA_PAKE_INPUT_MAX_SIZE); } /* END_CASE */ - From b3de69493c8770eff88a7c65e3a38bcd3d219a48 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 6 Nov 2023 14:22:28 +0000 Subject: [PATCH 080/184] Remove psa_crypto_alloc_and_copy() API This tied input and output buffers together in awkward pairs, which made the API more difficult to use. Signed-off-by: David Horstmann --- library/psa_crypto.c | 85 ------ library/psa_crypto_core.h | 53 ---- .../suites/test_suite_psa_crypto_memory.data | 60 ---- .../test_suite_psa_crypto_memory.function | 285 ------------------ 4 files changed, 483 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 09180b3c3a..b6533496c6 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8464,89 +8464,4 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co return PSA_SUCCESS; } -psa_status_t psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_len, - uint8_t *output, size_t output_len, - psa_crypto_buffer_copy_t *buffers) -{ - psa_status_t ret; - /* Zeroize the buffers struct to ensure we can call free() - * on any pointers safely. */ - memset(buffers, 0, sizeof(*buffers)); - - /* Since calloc() may return NULL if we try to allocate zero-length - * buffers anyway, deal with this corner case explicitly to ensure - * predictable behaviour. Represent zero-length buffers as NULL. */ - if (input_len == 0) { - input = NULL; - } - if (output_len == 0) { - output = NULL; - } - - if (output != NULL) { - buffers->output = mbedtls_calloc(output_len, 1); - if (buffers->output == NULL) { - ret = PSA_ERROR_INSUFFICIENT_MEMORY; - goto error; - } - buffers->output_len = output_len; - - buffers->output_original = output; - } - - if (input != NULL) { - buffers->input = mbedtls_calloc(input_len, 1); - if (buffers->input == NULL) { - ret = PSA_ERROR_INSUFFICIENT_MEMORY; - goto error; - } - buffers->input_len = input_len; - - if (psa_crypto_copy_input(input, input_len, - buffers->input, buffers->input_len) - != PSA_SUCCESS) { - ret = PSA_ERROR_CORRUPTION_DETECTED; - goto error; - } - } - - return PSA_SUCCESS; - -error: - mbedtls_free(buffers->input); - mbedtls_free(buffers->output); - memset(buffers, 0, sizeof(*buffers)); - return ret; -} - -psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers) -{ - if ((buffers->input != NULL) && (buffers->input_len == 0)) { - /* Reject zero-length buffers, these should have been represented by - * NULL in psa_crypto_alloc_and_copy() */ - return PSA_ERROR_INVALID_ARGUMENT; - } - if (buffers->output != NULL) { - if (buffers->output_len == 0) { - /* Reject zero-length buffers, these should have been represented - * by NULL in psa_crypto_alloc_and_copy() */ - return PSA_ERROR_INVALID_ARGUMENT; - } - if (buffers->output_original == NULL) { - /* Output is non-NULL but original output is NULL. The argument - * buffers is invalid. Return an error as we have no original to - * copy back to. */ - return PSA_ERROR_INVALID_ARGUMENT; - } - memcpy(buffers->output_original, buffers->output, buffers->output_len); - } - - mbedtls_free(buffers->input); - buffers->input = NULL; - mbedtls_free(buffers->output); - buffers->output = NULL; - - return PSA_SUCCESS; -} - #endif /* MBEDTLS_PSA_CRYPTO_C */ diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 00d9e9eedd..c9e277c2d3 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -884,57 +884,4 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, uint8_t *output, size_t output_len); -/** - * \brief Structure to store a pair of copied buffers (input, output) with a - * reference to the original output to be used during copy-back. - */ -struct psa_crypto_buffer_copy_s { - uint8_t *input; - size_t input_len; - - uint8_t *output_original; - uint8_t *output; - size_t output_len; -}; -typedef struct psa_crypto_buffer_copy_s psa_crypto_buffer_copy_t; - -/** - * \brief Allocate copies of provided input and output - * buffers and store references to them along with - * the original output buffer in the provided - * psa_crypto_buffer_copy_t struct. - * Either or both buffers may be NULL, in which case - * they are not copied. - * - * \note The input and output buffers may overlap. - * - * \param[in] input Pointer to the input buffer. - * \param[in] input_len Length of the input buffer. - * \param[in] output Pointer to the output buffer. - * \param[in] output_len Length of the output buffer. - * \param[out] buffers Struct containing pointers to the allocated - * copies and the original output buffer. - * \retval #PSA_SUCCESS - * The buffers were successfully allocated and copied. - * \retval #PSA_ERROR_INSUFFICIENT_MEMORY - * Failed to allocate buffers. - */ -psa_status_t psa_crypto_alloc_and_copy(const uint8_t *input, size_t input_len, - uint8_t *output, size_t output_len, - psa_crypto_buffer_copy_t *buffers); - -/** - * \brief Free an allocated pair of buffers after first - * copying the output buffer back to its original. - * - * \param[in] buffers psa_crypto_buffer_copy_t created by a previous - * call to psa_crypto_alloc_and_copy(). - * \retval #PSA_SUCCESS - * The buffers were successfully copied-back and the - * copies freed. - * \retval #PSA_ERROR_INVALID_ARGUMENT - * Could not copy-back as \p buffers is invalid. - */ -psa_status_t psa_crypto_copy_and_free(psa_crypto_buffer_copy_t *buffers); - #endif /* PSA_CRYPTO_CORE_H */ diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 33623afdc1..0d44fb35d5 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -27,63 +27,3 @@ copy_output:0:10:PSA_SUCCESS PSA output buffer copy: zero-length both buffers copy_output:0:0:PSA_SUCCESS - -PSA buffers alloc and copy -psa_crypto_alloc_and_copy:0:20:0:20:PSA_SUCCESS - -PSA buffers alloc and copy: null input -psa_crypto_alloc_and_copy:1:0:0:20:PSA_SUCCESS - -PSA buffers alloc and copy: null output -psa_crypto_alloc_and_copy:0:20:1:0:PSA_SUCCESS - -PSA buffers alloc and copy: null input and output -psa_crypto_alloc_and_copy:1:0:1:0:PSA_SUCCESS - -PSA buffers alloc and copy zero-length input -psa_crypto_alloc_and_copy_zero_length:1:0 - -PSA buffers alloc and copy zero-length output -psa_crypto_alloc_and_copy_zero_length:0:1 - -PSA buffers alloc and copy both zero length -psa_crypto_alloc_and_copy_zero_length:1:1 - -PSA buffers alloc and copy overlapping input first -psa_crypto_alloc_and_copy_overlapping:20:20:5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping output first -psa_crypto_alloc_and_copy_overlapping:20:20:-5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping output within input -psa_crypto_alloc_and_copy_overlapping:20:10:5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping input within output -psa_crypto_alloc_and_copy_overlapping:10:20:-5:PSA_SUCCESS - -PSA buffers alloc and copy overlapping input equals output -psa_crypto_alloc_and_copy_overlapping:20:20:0:PSA_SUCCESS - -PSA buffers copy and free -psa_crypto_copy_and_free:0:20:0:20:0:PSA_SUCCESS - -PSA buffers copy and free, null input -psa_crypto_copy_and_free:1:0:0:20:0:PSA_SUCCESS - -PSA buffers copy and free, null output -psa_crypto_copy_and_free:0:20:1:0:0:PSA_SUCCESS - -PSA buffers copy and free, null output_original -psa_crypto_copy_and_free:0:20:0:20:1:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers copy and free, null output_original and null output -psa_crypto_copy_and_free:0:20:1:0:1:PSA_SUCCESS - -PSA buffers copy and free, zero-length input -psa_crypto_copy_and_free:0:0:0:20:0:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers copy and free, zero-length output -psa_crypto_copy_and_free:20:0:0:0:0:PSA_ERROR_INVALID_ARGUMENT - -PSA buffers round-trip -psa_crypto_buffer_copy_round_trip diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index a27f76b88a..7e59bb96f9 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -20,63 +20,6 @@ static void fill_buffer_pattern(uint8_t *buffer, size_t len) buffer[i] = data[i % sizeof(data)]; } } - -/* Helper to get 2 buffers that overlap by the specified amount. - * The parameter ptr_diff may be negative, in which case the start of - * buf2 is allocated before the start of buf1. */ -static int setup_overlapping_buffers(size_t buf1_len, size_t buf2_len, - int ptr_diff, - uint8_t **full_buffer, - uint8_t **buf1, uint8_t **buf2) -{ - size_t total_len; - int buf1_offset; - int buf2_offset; - - *full_buffer = NULL; - *buf1 = NULL; - *buf2 = NULL; - - if (ptr_diff >= 0) { - /* - * |---------- buf1 ----------| - * <-- ptr_diff -->|---------- buf2 ----------| - */ - total_len = ptr_diff + buf2_len; - if (buf1_len > total_len) { - total_len = buf1_len; - } - buf1_offset = 0; - buf2_offset = ptr_diff; - } else { - /* - * <-- (-ptr_diff) -->|---------- buf1 ----------| - * |---------- buf2 ----------| - */ - total_len = (-ptr_diff) + buf1_len; - if (buf2_len > total_len) { - total_len = buf2_len; - } - buf1_offset = -ptr_diff; - buf2_offset = 0; - } - - /* Edge case: if the length is zero, allocate a 1-byte buffer to avoid - * calloc returning NULL. */ - if (total_len == 0) { - total_len = 1; - } - - TEST_CALLOC(*full_buffer, total_len); - - *buf1 = *full_buffer + buf1_offset; - *buf2 = *full_buffer + buf2_offset; - - return 0; - -exit: - return -1; -} /* END_HEADER */ /* BEGIN_DEPENDENCIES @@ -135,231 +78,3 @@ exit: mbedtls_free(dst_buffer); } /* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_alloc_and_copy(int input_null, int input_len, - int output_null, int output_len, - int exp_ret) -{ - uint8_t *input_buffer = NULL; - uint8_t *output_buffer = NULL; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - if (!input_null) { - TEST_CALLOC(input_buffer, input_len); - fill_buffer_pattern(input_buffer, input_len); - } - if (!output_null) { - TEST_CALLOC(output_buffer, output_len); - fill_buffer_pattern(output_buffer, output_len); - } - ret = psa_crypto_alloc_and_copy(input_buffer, input_len, - output_buffer, output_len, - &buffer_copies); - TEST_EQUAL(exp_ret, (int) ret); - - if (exp_ret == PSA_SUCCESS) { - TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); - TEST_EQUAL(output_len, buffer_copies.output_len); - } - -exit: - mbedtls_free(input_buffer); - mbedtls_free(output_buffer); - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_alloc_and_copy_zero_length(int input_zero_length, - int output_zero_length) -{ - uint8_t input_buffer[] = { 0x12 }; - uint8_t output_buffer[] = { 0x34 }; - - size_t input_len = input_zero_length ? 0 : 1; - size_t output_len = output_zero_length ? 0 : 1; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - psa_status_t ret = psa_crypto_alloc_and_copy(input_buffer, input_len, - output_buffer, output_len, - &buffer_copies); - TEST_EQUAL(ret, PSA_SUCCESS); - - if (input_zero_length) { - TEST_ASSERT(buffer_copies.input == NULL); - } else { - TEST_MEMORY_COMPARE(input_buffer, input_len, buffer_copies.input, buffer_copies.input_len); - } - if (output_zero_length) { - TEST_ASSERT(buffer_copies.output == NULL); - } else { - TEST_EQUAL(output_len, buffer_copies.output_len); - } - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -/* Ensure that overlapping buffers can be copied correctly. */ -void psa_crypto_alloc_and_copy_overlapping(int input_len, int output_len, - int ptr_diff, int exp_ret) -{ - uint8_t *full_buffer = NULL; - uint8_t *input = NULL; - uint8_t *output = NULL; - - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - TEST_EQUAL(setup_overlapping_buffers(input_len, output_len, ptr_diff, - &full_buffer, &input, &output), 0); - - fill_buffer_pattern(input, input_len); - - ret = psa_crypto_alloc_and_copy(input, input_len, output, output_len, - &buffer_copies); - - TEST_EQUAL((int) ret, exp_ret); - - if (exp_ret == PSA_SUCCESS) { - if (input_len == 0) { - TEST_ASSERT(buffer_copies.input == NULL); - } else { - TEST_MEMORY_COMPARE(input, input_len, buffer_copies.input, buffer_copies.input_len); - } - if (output_len == 0) { - TEST_ASSERT(buffer_copies.output == NULL); - } else { - TEST_EQUAL(output_len, buffer_copies.output_len); - } - } - -exit: - mbedtls_free(full_buffer); - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_copy_and_free(int input_null, int input_len, - int output_null, int output_len, - int orig_output_null, - int exp_ret) -{ - uint8_t *input = NULL; - uint8_t *output = NULL; - uint8_t *output_for_comparison = NULL; - uint8_t *orig_output = NULL; - size_t calloc_len; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - if (!input_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = input_len == 0 ? 1 : input_len; - TEST_CALLOC(input, calloc_len); - } - if (!output_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = output_len == 0 ? 1 : output_len; - TEST_CALLOC(output, calloc_len); - TEST_CALLOC(output_for_comparison, calloc_len); - - fill_buffer_pattern(output, output_len); - /* We expect the output buffer to be freed, so keep a copy - * for comparison. */ - memcpy(output_for_comparison, output, output_len); - } - if (!orig_output_null) { - /* If zero-length, ensure we actually allocate something - * rather than getting NULL. */ - calloc_len = output_len == 0 ? 1 : output_len; - TEST_CALLOC(orig_output, calloc_len); - } - - buffer_copies.input = input; - buffer_copies.input_len = input_len; - buffer_copies.output = output; - buffer_copies.output_len = output_len; - buffer_copies.output_original = orig_output; - - ret = psa_crypto_copy_and_free(&buffer_copies); - - TEST_EQUAL((int) ret, exp_ret); - - if (exp_ret == PSA_SUCCESS) { - TEST_ASSERT(buffer_copies.input == NULL); - TEST_ASSERT(buffer_copies.output == NULL); - - if (!output_null) { - TEST_MEMORY_COMPARE(output_for_comparison, output_len, - buffer_copies.output_original, buffer_copies.output_len); - } - } - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); - mbedtls_free(output_for_comparison); - mbedtls_free(orig_output); -} -/* END_CASE */ - -/* BEGIN_CASE */ -void psa_crypto_buffer_copy_round_trip() -{ - uint8_t input[100]; - uint8_t output[100]; - uint8_t output_for_comparison[100]; - psa_status_t ret; - - psa_crypto_buffer_copy_t buffer_copies; - memset(&buffer_copies, 0, sizeof(buffer_copies)); - - fill_buffer_pattern(input, sizeof(input)); - - ret = psa_crypto_alloc_and_copy(input, sizeof(input), - output, sizeof(output), - &buffer_copies); - - TEST_ASSERT(ret == PSA_SUCCESS); - TEST_MEMORY_COMPARE(input, sizeof(input), - buffer_copies.input, buffer_copies.input_len); - TEST_EQUAL(sizeof(output), buffer_copies.output_len); - - /* Simulate the PSA function filling the (internal) output buffer. */ - fill_buffer_pattern(buffer_copies.output, buffer_copies.output_len); - - /* Make a copy of output to compare the copy-back */ - memcpy(output_for_comparison, buffer_copies.output, - sizeof(output_for_comparison)); - - ret = psa_crypto_copy_and_free(&buffer_copies); - - TEST_EQUAL(ret, PSA_SUCCESS); - /* Check that the output was copied back correctly. */ - TEST_MEMORY_COMPARE(output_for_comparison, sizeof(output_for_comparison), - output, sizeof(output)); - -exit: - mbedtls_free(buffer_copies.input); - mbedtls_free(buffer_copies.output); -} -/* END_CASE */ From 6fd4c7cff2b37584e176dce71b950ecb08e61fc0 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 6 Nov 2023 17:27:16 +0000 Subject: [PATCH 081/184] Add prototypes for psa_crypto_input_copy API This includes: * The psa_crypto_input_copy_t struct * psa_crypto_input_copy_alloc() * psa_crypto_input_copy_free() Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index c9e277c2d3..a53824ec3e 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -884,4 +884,31 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, uint8_t *output, size_t output_len); +typedef struct psa_crypto_input_copy_s { + uint8_t *buffer; + size_t len; +} psa_crypto_input_copy_t; + +/** Allocate a local copy of an input buffer. + * + * \param[in] input Pointer to input buffer. + * \param[in] input_len Length of the input buffer. + * \param[out] input_copy Pointer to a psa_crypto_input_copy_t struct to + * populate with the input copy. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of + * the buffer cannot be allocated. + */ +psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, + psa_crypto_input_copy_t *input_copy); + +/** Free a local copy of an input buffer. + * + * \param[in] input_copy Pointer to a psa_crypto_input_copy_t struct + * populated by a previous call to + * psa_crypto_input_copy_alloc(). + */ +void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy); + #endif /* PSA_CRYPTO_CORE_H */ From 1ac7e24fb72a54ebdab56f5e492663115bd81858 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 6 Nov 2023 18:18:59 +0000 Subject: [PATCH 082/184] Add testcase for psa_crypto_input_copy_alloc() Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 6 ++++ .../test_suite_psa_crypto_memory.function | 31 +++++++++++++++++++ 2 files changed, 37 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 0d44fb35d5..85fed8f0e1 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -27,3 +27,9 @@ copy_output:0:10:PSA_SUCCESS PSA output buffer copy: zero-length both buffers copy_output:0:0:PSA_SUCCESS + +PSA crypto input copy alloc +input_copy_alloc:200:PSA_SUCCESS + +PSA crypto input copy alloc, NULL buffer +input_copy_alloc:0:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 7e59bb96f9..837ff06e9e 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -78,3 +78,34 @@ exit: mbedtls_free(dst_buffer); } /* END_CASE */ + +/* BEGIN_CASE */ +void input_copy_alloc(int input_len, psa_status_t exp_status) +{ + uint8_t *input = NULL; + psa_crypto_input_copy_t input_copy; + psa_status_t status; + + input_copy.buffer = NULL; + + TEST_CALLOC(input, input_len); + fill_buffer_pattern(input, input_len); + + status = psa_crypto_input_copy_alloc(input, input_len, &input_copy); + TEST_EQUAL(status, exp_status); + + if (exp_status == PSA_SUCCESS) { + if (input == NULL) { + TEST_ASSERT(input_copy.buffer == NULL); + } else { + TEST_ASSERT(input_copy.buffer != input); + TEST_MEMORY_COMPARE(input, input_len, + input_copy.buffer, input_copy.len); + } + } + +exit: + mbedtls_free(input_copy.buffer); + mbedtls_free(input); +} +/* END_CASE */ From 4ac788573b620387e7afe74148cf1974451e9dc7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 7 Nov 2023 16:36:48 +0000 Subject: [PATCH 083/184] Add psa_crypto_input_copy_alloc() implementation Signed-off-by: David Horstmann --- library/psa_crypto.c | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index b6533496c6..2c37aebf79 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8464,4 +8464,43 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co return PSA_SUCCESS; } +psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, + psa_crypto_input_copy_t *input_copy) +{ + psa_status_t status; + + input_copy->buffer = NULL; + input_copy->len = 0; + + /* Treat NULL and zero-length input the same. + * This is simpler than potentially calling calloc(0). */ + if (input == NULL || input_len == 0) { + return PSA_SUCCESS; + } + + input_copy->buffer = mbedtls_calloc(input_len, 1); + if (input_copy->buffer == NULL) { + /* Since we dealt with the zero-length case above, we know that + * a NULL return value means a failure of allocation. */ + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + /* From now on, we must free input_copy->buffer on error. */ + + input_copy->len = input_len; + + status = psa_crypto_copy_input(input, input_len, + input_copy->buffer, input_copy->len); + if (status != PSA_SUCCESS) { + goto error; + } + + return PSA_SUCCESS; + +error: + mbedtls_free(input_copy->buffer); + input_copy->buffer = NULL; + input_copy->len = 0; + return status; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 47001448171368d9fce2be31716423ffebd27a36 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 7 Nov 2023 17:30:25 +0000 Subject: [PATCH 084/184] Add testcase for psa_crypto_input_copy_free() Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 6 ++++++ .../test_suite_psa_crypto_memory.function | 21 +++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 85fed8f0e1..6591ba755c 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -33,3 +33,9 @@ input_copy_alloc:200:PSA_SUCCESS PSA crypto input copy alloc, NULL buffer input_copy_alloc:0:PSA_SUCCESS + +PSA crypto input copy free +input_copy_free:200 + +PSA crypto input copy free, NULL buffer +input_copy_free:0 diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 837ff06e9e..c40ba67f5c 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -109,3 +109,24 @@ exit: mbedtls_free(input); } /* END_CASE */ + +/* BEGIN_CASE */ +void input_copy_free(int input_len) +{ + psa_crypto_input_copy_t input_copy; + + input_copy.buffer = NULL; + input_copy.len = input_len; + TEST_CALLOC(input_copy.buffer, input_copy.len); + + psa_crypto_input_copy_free(&input_copy); + + TEST_ASSERT(input_copy.buffer == NULL); + TEST_EQUAL(input_copy.len, 0); + +exit: + mbedtls_free(input_copy.buffer); + input_copy.buffer = NULL; + input_copy.len = 0; +} +/* END_CASE */ From e6042ffc49e1f96fc6b4e3cdcc49731b7e382f83 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 7 Nov 2023 18:00:41 +0000 Subject: [PATCH 085/184] Add implementation of psa_crypto_input_copy_free() Signed-off-by: David Horstmann --- library/psa_crypto.c | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 2c37aebf79..d4138e0c41 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8503,4 +8503,11 @@ error: return status; } +void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy) +{ + mbedtls_free(input_copy->buffer); + input_copy->buffer = NULL; + input_copy->len = 0; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From dfa14cbbcd00dab45c87f71e86f90864bf252835 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 14:33:05 +0000 Subject: [PATCH 086/184] Add function prototypes for psa_crypto_output fns Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 39 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index a53824ec3e..f3f7dfba01 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -911,4 +911,43 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, */ void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy); +typedef struct psa_crypto_output_copy_s { + uint8_t *original; + uint8_t *buffer; + size_t len; +} psa_crypto_output_copy_t; + +/** Allocate a local copy of an output buffer. + * + * \note This does not copy any data from the original + * output buffer but only allocates a buffer + * whose contents will be copied back to the + * original in a future call to + * psa_crypto_output_copy_free(). + * + * \param[in] output Pointer to output buffer. + * \param[in] output_len Length of the output buffer. + * \param[out] output_copy Pointer to a psa_crypto_output_copy_t struct to + * populate with the output copy. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of + * the buffer cannot be allocated. + */ +psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, + psa_crypto_output_copy_t *output_copy); + +/** Copy from a local copy of an output buffer back to the original, then + * free the local copy. + * + * \param[in] output_copy Pointer to a psa_crypto_output_copy_t struct + * populated by a previous call to + * psa_crypto_output_copy_alloc(). + * \return #PSA_SUCCESS, if the output copy was + * successfully copied back to the original. + * \return #PSA_ERROR_CORRUPTION_DETECTED, if the output + * could not be copied back to the original. + */ +psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy); + #endif /* PSA_CRYPTO_CORE_H */ From 70b82256b51ae0d3d088a3fa48c2051eb057bc6b Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 15:10:41 +0000 Subject: [PATCH 087/184] Add testcase for psa_crypto_output_copy_alloc() Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 6 ++++ .../test_suite_psa_crypto_memory.function | 33 +++++++++++++++++++ 2 files changed, 39 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 6591ba755c..fcd60fb803 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -39,3 +39,9 @@ input_copy_free:200 PSA crypto input copy free, NULL buffer input_copy_free:0 + +PSA crypto output copy alloc +output_copy_alloc:200:PSA_SUCCESS + +PSA crypto output copy alloc, NULL buffer +output_copy_alloc:0:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index c40ba67f5c..6fd58cd0af 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -130,3 +130,36 @@ exit: input_copy.len = 0; } /* END_CASE */ + +/* BEGIN_CASE */ +void output_copy_alloc(int output_len, psa_status_t exp_status) +{ + uint8_t *output = NULL; + psa_crypto_output_copy_t output_copy; + psa_status_t status; + + output_copy.buffer = NULL; + + TEST_CALLOC(output, output_len); + + status = psa_crypto_output_copy_alloc(output, output_len, &output_copy); + TEST_EQUAL(status, exp_status); + + if (exp_status == PSA_SUCCESS) { + TEST_ASSERT(output_copy.original == output); + if (output == NULL) { + TEST_ASSERT(output_copy.buffer == NULL); + } else { + TEST_EQUAL(output_copy.len, output_len); + } + } + +exit: + mbedtls_free(output_copy.buffer); + output_copy.original = NULL; + output_copy.buffer = NULL; + output_copy.len = 0; + mbedtls_free(output); + output = NULL; +} +/* END_CASE */ From ba3c7d649c0d916d7af8f1b32c3bf17b5e5db2f7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 15:19:43 +0000 Subject: [PATCH 088/184] Add implementation of psa_crypto_output_alloc() Signed-off-by: David Horstmann --- library/psa_crypto.c | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index d4138e0c41..c733b28ff8 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8510,4 +8510,28 @@ void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy) input_copy->len = 0; } +psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, + psa_crypto_output_copy_t *output_copy) +{ + output_copy->original = NULL; + output_copy->buffer = NULL; + output_copy->len = 0; + + /* Treat NULL and zero-length input the same. + * This is simpler than potentially calling calloc(0). */ + if (output == NULL || output_len == 0) { + return PSA_SUCCESS; + } + output_copy->buffer = mbedtls_calloc(output_len, 1); + if (output_copy->buffer == NULL) { + /* Since we dealt with the zero-length case above, we know that + * a NULL return value means a failure of allocation. */ + return PSA_ERROR_INSUFFICIENT_MEMORY; + } + output_copy->len = output_len; + output_copy->original = output; + + return PSA_SUCCESS; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 63a73588cf03624a8099b960a88d45f85ef6a63f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 17:25:45 +0000 Subject: [PATCH 089/184] Add testcase for psa_crypto_output_copy_free() Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 9 ++++ .../test_suite_psa_crypto_memory.function | 45 +++++++++++++++++++ 2 files changed, 54 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index fcd60fb803..2ca7e15abf 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -45,3 +45,12 @@ output_copy_alloc:200:PSA_SUCCESS PSA crypto output copy alloc, NULL buffer output_copy_alloc:0:PSA_SUCCESS + +PSA crypto output copy free +output_copy_free:200:0:PSA_SUCCESS + +PSA crypto output copy free, NULL buffer +output_copy_free:0:0:PSA_SUCCESS + +PSA crypto output copy free, NULL original buffer +output_copy_free:200:1:PSA_ERROR_CORRUPTION_DETECTED diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 6fd58cd0af..57a10ae791 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -163,3 +163,48 @@ exit: output = NULL; } /* END_CASE */ + +/* BEGIN_CASE */ +void output_copy_free(int output_len, int original_is_null, + psa_status_t exp_status) +{ + uint8_t *output = NULL; + uint8_t *buffer_copy_for_comparison = NULL; + psa_crypto_output_copy_t output_copy; + psa_status_t status; + + output_copy.buffer = NULL; + output_copy.len = 0; + + if (!original_is_null) { + TEST_CALLOC(output, output_len); + } + TEST_CALLOC(buffer_copy_for_comparison, output_len); + TEST_CALLOC(output_copy.buffer, output_len); + output_copy.len = output_len; + output_copy.original = output; + + if (output_copy.buffer != NULL) { + fill_buffer_pattern(output_copy.buffer, output_copy.len); + memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.len); + } + + status = psa_crypto_output_copy_free(&output_copy); + TEST_EQUAL(status, exp_status); + + if (exp_status == PSA_SUCCESS) { + TEST_ASSERT(output_copy.buffer == NULL); + TEST_EQUAL(output_copy.len, 0); + if (output != NULL) { + TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, + output, output_len); + } + } + +exit: + mbedtls_free(output); + mbedtls_free(buffer_copy_for_comparison); + mbedtls_free(output_copy.buffer); + output_copy.len = 0; +} +/* END_CASE */ From 9467ea343b182e38449f787c24852a805e5c67d6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 17:44:18 +0000 Subject: [PATCH 090/184] Add psa_crypto_output_copy_free() implementation Signed-off-by: David Horstmann --- library/psa_crypto.c | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index c733b28ff8..0e2fb3f946 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8534,4 +8534,24 @@ psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, return PSA_SUCCESS; } +psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) +{ + if (output_copy->buffer == NULL) { + output_copy->len = 0; + return PSA_SUCCESS; + } + if (output_copy->original == NULL) { + /* We have an internal copy but nothing to copy back to. */ + return PSA_ERROR_CORRUPTION_DETECTED; + } + + memcpy(output_copy->original, output_copy->buffer, output_copy->len); + + mbedtls_free(output_copy->buffer); + output_copy->buffer = NULL; + output_copy->len = 0; + + return PSA_SUCCESS; +} + #endif /* MBEDTLS_PSA_CRYPTO_C */ From 35dd103688acdb4e01da7c2dff8c3dde319add51 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 18:01:40 +0000 Subject: [PATCH 091/184] Add input round-trip testcase Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 3 +++ .../test_suite_psa_crypto_memory.function | 21 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 2ca7e15abf..3d14ba35dc 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -40,6 +40,9 @@ input_copy_free:200 PSA crypto input copy free, NULL buffer input_copy_free:0 +PSA crypto input copy round-trip +input_copy_round_trip + PSA crypto output copy alloc output_copy_alloc:200:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 57a10ae791..3c7b8425eb 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -131,6 +131,27 @@ exit: } /* END_CASE */ +/* BEGIN_CASE */ +void input_copy_round_trip() +{ + psa_crypto_input_copy_t input_copy; + uint8_t input[200]; + psa_status_t status; + + fill_buffer_pattern(input, sizeof(input)); + + status = psa_crypto_input_copy_alloc(input, sizeof(input), &input_copy); + TEST_EQUAL(status, PSA_SUCCESS); + TEST_MEMORY_COMPARE(input_copy.buffer, input_copy.len, + input, sizeof(input)); + TEST_ASSERT(input_copy.buffer != input); + + psa_crypto_input_copy_free(&input_copy); + TEST_ASSERT(input_copy.buffer == NULL); + TEST_EQUAL(input_copy.len, 0); +} +/* END_CASE */ + /* BEGIN_CASE */ void output_copy_alloc(int output_len, psa_status_t exp_status) { From 0a57ed25c4344653544ce92a32fa1315b8b03e6a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 8 Nov 2023 18:11:29 +0000 Subject: [PATCH 092/184] Add output round-trip testcase Signed-off-by: David Horstmann --- .../suites/test_suite_psa_crypto_memory.data | 3 ++ .../test_suite_psa_crypto_memory.function | 31 +++++++++++++++++++ 2 files changed, 34 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 3d14ba35dc..357d3423d3 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -57,3 +57,6 @@ output_copy_free:0:0:PSA_SUCCESS PSA crypto output copy free, NULL original buffer output_copy_free:200:1:PSA_ERROR_CORRUPTION_DETECTED + +PSA crypto output copy round-trip +output_copy_round_trip diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 3c7b8425eb..df08bf45f3 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -229,3 +229,34 @@ exit: output_copy.len = 0; } /* END_CASE */ + +/* BEGIN_CASE */ +void output_copy_round_trip() +{ + psa_crypto_output_copy_t output_copy; + uint8_t output[200]; + uint8_t *buffer_copy_for_comparison = NULL; + psa_status_t status; + + status = psa_crypto_output_copy_alloc(output, sizeof(output), &output_copy); + TEST_EQUAL(status, PSA_SUCCESS); + TEST_ASSERT(output_copy.buffer != output); + + /* Simulate the function generating output */ + fill_buffer_pattern(output_copy.buffer, output_copy.len); + + TEST_CALLOC(buffer_copy_for_comparison, output_copy.len); + memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.len); + + psa_crypto_output_copy_free(&output_copy); + TEST_ASSERT(output_copy.buffer == NULL); + TEST_EQUAL(output_copy.len, 0); + + /* Check that the buffer was correctly copied back */ + TEST_MEMORY_COMPARE(output, sizeof(output), + buffer_copy_for_comparison, sizeof(output)); + +exit: + mbedtls_free(buffer_copy_for_comparison); +} +/* END_CASE */ From c335a4e1865d31f9183870ccde0d3d2f1f256cdb Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 15 Nov 2023 15:57:32 +0000 Subject: [PATCH 093/184] Fix unintentional direct use of memcpy() Change psa_crypto_output_copy_free() to use psa_crypto_copy_output() rather than calling memcpy directly as was erroneously done previously. Signed-off-by: David Horstmann --- library/psa_crypto.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 0e2fb3f946..6787b03e2f 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8536,6 +8536,8 @@ psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) { + psa_status_t status; + if (output_copy->buffer == NULL) { output_copy->len = 0; return PSA_SUCCESS; @@ -8545,7 +8547,11 @@ psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) return PSA_ERROR_CORRUPTION_DETECTED; } - memcpy(output_copy->original, output_copy->buffer, output_copy->len); + status = psa_crypto_copy_output(output_copy->buffer, output_copy->len, + output_copy->original, output_copy->len); + if (status != PSA_SUCCESS) { + return status; + } mbedtls_free(output_copy->buffer); output_copy->buffer = NULL; From 1b7279a849783babc2fac2c726ed014db7fbc76a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 15 Nov 2023 15:18:30 +0000 Subject: [PATCH 094/184] Make copy functions static-testable This allows greater compiler optimisation. Signed-off-by: David Horstmann --- library/psa_crypto.c | 26 ++++++++++++++++ library/psa_crypto_core.h | 30 ------------------- library/psa_crypto_invasive.h | 7 +++++ .../test_suite_psa_crypto_memory.function | 3 +- 4 files changed, 35 insertions(+), 31 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 6787b03e2f..e03e2ae122 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8442,6 +8442,19 @@ psa_status_t psa_pake_abort( #endif /* PSA_WANT_ALG_SOME_PAKE */ +/** Copy from an input buffer to a local copy. + * + * \param[in] input Pointer to input buffer. + * \param[in] input_len Length of the input buffer. + * \param[out] input_copy Pointer to a local copy in which to store the input data. + * \param[out] input_copy_len Length of the local copy buffer. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_CORRUPTION_DETECTED, if the local + * copy is too small to hold contents of the + * input buffer. + */ +MBEDTLS_STATIC_TESTABLE psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, uint8_t *input_copy, size_t input_copy_len) { @@ -8454,6 +8467,19 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, return PSA_SUCCESS; } +/** Copy from a local output buffer into a user-supplied one. + * + * \param[in] output_copy Pointer to a local buffer containing the output. + * \param[in] output_copy_len Length of the local buffer. + * \param[out] output Pointer to user-supplied output buffer. + * \param[out] output_len Length of the user-supplied output buffer. + * \return #PSA_SUCCESS, if the buffer was successfully + * copied. + * \return #PSA_ERROR_CORRUPTION_DETECTED, if the + * user-supplied output buffer is too small to + * hold the contents of the local buffer. + */ +MBEDTLS_STATIC_TESTABLE psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, uint8_t *output, size_t output_len) { diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index f3f7dfba01..125f7ec4c9 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -854,36 +854,6 @@ psa_status_t mbedtls_psa_verify_hash_complete( psa_status_t mbedtls_psa_verify_hash_abort( mbedtls_psa_verify_hash_interruptible_operation_t *operation); -/** Copy from an input buffer to a local copy. - * - * \param[in] input Pointer to input buffer. - * \param[in] input_len Length of the input buffer. - * \param[out] input_copy Pointer to a local copy in which to store the input data. - * \param[out] input_copy_len Length of the local copy buffer. - * \return #PSA_SUCCESS, if the buffer was successfully - * copied. - * \return #PSA_ERROR_CORRUPTION_DETECTED, if the local - * copy is too small to hold contents of the - * input buffer. - */ -psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, - uint8_t *input_copy, size_t input_copy_len); - -/** Copy from a local output buffer into a user-supplied one. - * - * \param[in] output_copy Pointer to a local buffer containing the output. - * \param[in] output_copy_len Length of the local buffer. - * \param[out] output Pointer to user-supplied output buffer. - * \param[out] output_len Length of the user-supplied output buffer. - * \return #PSA_SUCCESS, if the buffer was successfully - * copied. - * \return #PSA_ERROR_CORRUPTION_DETECTED, if the - * user-supplied output buffer is too small to - * hold the contents of the local buffer. - */ -psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, - uint8_t *output, size_t output_len); - typedef struct psa_crypto_input_copy_s { uint8_t *buffer; size_t len; diff --git a/library/psa_crypto_invasive.h b/library/psa_crypto_invasive.h index 408c39bfec..29d4e3dd13 100644 --- a/library/psa_crypto_invasive.h +++ b/library/psa_crypto_invasive.h @@ -84,6 +84,13 @@ psa_status_t mbedtls_psa_crypto_configure_entropy_sources( psa_status_t psa_mac_key_can_do( psa_algorithm_t algorithm, psa_key_type_t key_type); + +psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, + uint8_t *input_copy, size_t input_copy_len); + +psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, + uint8_t *output, size_t output_len); + #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C */ #endif /* PSA_CRYPTO_INVASIVE_H */ diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index df08bf45f3..29cb3eca13 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -6,6 +6,7 @@ #include "psa/crypto.h" #include "psa_crypto_core.h" +#include "psa_crypto_invasive.h" #include "test/psa_crypto_helpers.h" @@ -23,7 +24,7 @@ static void fill_buffer_pattern(uint8_t *buffer, size_t len) /* END_HEADER */ /* BEGIN_DEPENDENCIES - * depends_on:MBEDTLS_PSA_CRYPTO_C + * depends_on:MBEDTLS_PSA_CRYPTO_C:MBEDTLS_TEST_HOOKS * END_DEPENDENCIES */ From 777e74130f409ab69e47d7110c1b81ae0e5d0520 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 15 Nov 2023 17:33:47 +0000 Subject: [PATCH 095/184] Skip call to memcpy if buffer length is zero This allows the copy functions to work when passed a (NULL, 0) buffer. Signed-off-by: David Horstmann --- library/psa_crypto.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index e03e2ae122..43495e247e 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8462,7 +8462,9 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, return PSA_ERROR_CORRUPTION_DETECTED; } - memcpy(input_copy, input, input_len); + if (input_len > 0) { + memcpy(input_copy, input, input_len); + } return PSA_SUCCESS; } @@ -8486,7 +8488,11 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co if (output_len < output_copy_len) { return PSA_ERROR_CORRUPTION_DETECTED; } - memcpy(output, output_copy, output_copy_len); + + if (output_copy_len > 0) { + memcpy(output, output_copy, output_copy_len); + } + return PSA_SUCCESS; } From c5cc1c3a92b1a80bb93e484a9c954066afd3a123 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 15 Nov 2023 18:11:26 +0000 Subject: [PATCH 096/184] Remove redundant NULL check A NULL buffer with a non-zero length is an internal error, so just check the length. Signed-off-by: David Horstmann --- library/psa_crypto.c | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 43495e247e..167ce2d173 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8504,9 +8504,7 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, input_copy->buffer = NULL; input_copy->len = 0; - /* Treat NULL and zero-length input the same. - * This is simpler than potentially calling calloc(0). */ - if (input == NULL || input_len == 0) { + if (input_len == 0) { return PSA_SUCCESS; } @@ -8549,9 +8547,7 @@ psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, output_copy->buffer = NULL; output_copy->len = 0; - /* Treat NULL and zero-length input the same. - * This is simpler than potentially calling calloc(0). */ - if (output == NULL || output_len == 0) { + if (output_len == 0) { return PSA_SUCCESS; } output_copy->buffer = mbedtls_calloc(output_len, 1); From b4e3f36918e8022b7f6cb2100c3bd7e316daad5f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 19:57:25 +0000 Subject: [PATCH 097/184] Change data pattern to simpler one Just use the index modulo 256, as this has a greater stride and is simpler to use. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 29cb3eca13..81c52a3f7a 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -15,10 +15,8 @@ * been written, in a way that will detect an error in offset. */ static void fill_buffer_pattern(uint8_t *buffer, size_t len) { - uint8_t data[] = { 0x12, 0x34, 0x56, 0x78 }; - for (size_t i = 0; i < len; i++) { - buffer[i] = data[i % sizeof(data)]; + buffer[i] = (uint8_t) (i % 256); } } /* END_HEADER */ From 23f1122838c001beb980a64d68d173e8e2d8686c Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 20:01:32 +0000 Subject: [PATCH 098/184] Use TEST_CALLOC_NONNULL Check that input/output copying works for zero-length NULL input buffers. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 81c52a3f7a..e92cbb157f 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -33,8 +33,8 @@ void copy_input(int src_len, int dst_len, psa_status_t exp_status) uint8_t *dst_buffer = NULL; psa_status_t status; - TEST_CALLOC_NONNULL(src_buffer, src_len); - TEST_CALLOC_NONNULL(dst_buffer, dst_len); + TEST_CALLOC(src_buffer, src_len); + TEST_CALLOC(dst_buffer, dst_len); fill_buffer_pattern(src_buffer, src_len); @@ -59,8 +59,8 @@ void copy_output(int src_len, int dst_len, psa_status_t exp_status) uint8_t *dst_buffer = NULL; psa_status_t status; - TEST_CALLOC_NONNULL(src_buffer, src_len); - TEST_CALLOC_NONNULL(dst_buffer, dst_len); + TEST_CALLOC(src_buffer, src_len); + TEST_CALLOC(dst_buffer, dst_len); fill_buffer_pattern(src_buffer, src_len); From 0fca150b8146f69d624d5e94a057a21e50c20ed3 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 20:12:17 +0000 Subject: [PATCH 099/184] Compare buffers even for zero-length cases This enables us to test that lengths are correctly zero when the buffer pointer is NULL. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index e92cbb157f..593825823c 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -98,9 +98,9 @@ void input_copy_alloc(int input_len, psa_status_t exp_status) TEST_ASSERT(input_copy.buffer == NULL); } else { TEST_ASSERT(input_copy.buffer != input); - TEST_MEMORY_COMPARE(input, input_len, - input_copy.buffer, input_copy.len); } + TEST_MEMORY_COMPARE(input, input_len, + input_copy.buffer, input_copy.len); } exit: @@ -169,9 +169,8 @@ void output_copy_alloc(int output_len, psa_status_t exp_status) TEST_ASSERT(output_copy.original == output); if (output == NULL) { TEST_ASSERT(output_copy.buffer == NULL); - } else { - TEST_EQUAL(output_copy.len, output_len); } + TEST_EQUAL(output_copy.len, output_len); } exit: @@ -215,10 +214,8 @@ void output_copy_free(int output_len, int original_is_null, if (exp_status == PSA_SUCCESS) { TEST_ASSERT(output_copy.buffer == NULL); TEST_EQUAL(output_copy.len, 0); - if (output != NULL) { - TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, - output, output_len); - } + TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, + output, output_len); } exit: From bab3e76da5888268ace5a18417d3f5480f0c77bc Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 20:21:19 +0000 Subject: [PATCH 100/184] Fix code style in psa_crypto_core.h Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 125f7ec4c9..5cdd9141ff 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -882,9 +882,9 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy); typedef struct psa_crypto_output_copy_s { - uint8_t *original; - uint8_t *buffer; - size_t len; + uint8_t *original; + uint8_t *buffer; + size_t len; } psa_crypto_output_copy_t; /** Allocate a local copy of an output buffer. From 58909704e33679517075c947e6b097ec51ba3cca Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 20:26:16 +0000 Subject: [PATCH 101/184] Check for len == 0 rather than buffer == NULL This makes the intention clearer Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 593825823c..83fa6cbd86 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -94,7 +94,7 @@ void input_copy_alloc(int input_len, psa_status_t exp_status) TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { - if (input == NULL) { + if (input_len == 0) { TEST_ASSERT(input_copy.buffer == NULL); } else { TEST_ASSERT(input_copy.buffer != input); @@ -167,7 +167,7 @@ void output_copy_alloc(int output_len, psa_status_t exp_status) if (exp_status == PSA_SUCCESS) { TEST_ASSERT(output_copy.original == output); - if (output == NULL) { + if (output_len == 0) { TEST_ASSERT(output_copy.buffer == NULL); } TEST_EQUAL(output_copy.len, output_len); @@ -203,7 +203,7 @@ void output_copy_free(int output_len, int original_is_null, output_copy.len = output_len; output_copy.original = output; - if (output_copy.buffer != NULL) { + if (output_copy.len != 0) { fill_buffer_pattern(output_copy.buffer, output_copy.len); memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.len); } From 365df3f16c2d91fcdeb0680fc168284aa39b4b06 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 16 Nov 2023 20:30:36 +0000 Subject: [PATCH 102/184] Remove unnecessary checks for NULL-ness of copies Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 83fa6cbd86..7b6b7f189c 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -94,9 +94,7 @@ void input_copy_alloc(int input_len, psa_status_t exp_status) TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { - if (input_len == 0) { - TEST_ASSERT(input_copy.buffer == NULL); - } else { + if (input_len != 0) { TEST_ASSERT(input_copy.buffer != input); } TEST_MEMORY_COMPARE(input, input_len, @@ -167,9 +165,6 @@ void output_copy_alloc(int output_len, psa_status_t exp_status) if (exp_status == PSA_SUCCESS) { TEST_ASSERT(output_copy.original == output); - if (output_len == 0) { - TEST_ASSERT(output_copy.buffer == NULL); - } TEST_EQUAL(output_copy.len, output_len); } From 9abf5350784cb72713667fda4835c01c284b833e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 20 Nov 2023 12:16:08 +0000 Subject: [PATCH 103/184] Add initializers for input / output copies Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 4 ++++ tests/suites/test_suite_psa_crypto_memory.function | 5 +---- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 5cdd9141ff..940748edd9 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -859,6 +859,8 @@ typedef struct psa_crypto_input_copy_s { size_t len; } psa_crypto_input_copy_t; +#define PSA_CRYPTO_INPUT_COPY_INIT { NULL, 0 } + /** Allocate a local copy of an input buffer. * * \param[in] input Pointer to input buffer. @@ -887,6 +889,8 @@ typedef struct psa_crypto_output_copy_s { size_t len; } psa_crypto_output_copy_t; +#define PSA_CRYPTO_OUTPUT_COPY_INIT { NULL, NULL, 0 } + /** Allocate a local copy of an output buffer. * * \note This does not copy any data from the original diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 7b6b7f189c..e9e529989e 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -184,12 +184,9 @@ void output_copy_free(int output_len, int original_is_null, { uint8_t *output = NULL; uint8_t *buffer_copy_for_comparison = NULL; - psa_crypto_output_copy_t output_copy; + psa_crypto_output_copy_t output_copy = PSA_CRYPTO_OUTPUT_COPY_INIT; psa_status_t status; - output_copy.buffer = NULL; - output_copy.len = 0; - if (!original_is_null) { TEST_CALLOC(output, output_len); } From 671f5f539ea30b6506c0b74c9c156344b24ccc38 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 20 Nov 2023 12:39:38 +0000 Subject: [PATCH 104/184] Change psa_crypto_copy_output error code When we are copying output, it makes sense to return PSA_ERROR_BUFFER_TOO_SMALL since the buffer we are copying to is a user output buffer. Signed-off-by: David Horstmann --- library/psa_crypto.c | 4 ++-- tests/suites/test_suite_psa_crypto_memory.data | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 167ce2d173..3327c30c43 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8477,7 +8477,7 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, * \param[out] output_len Length of the user-supplied output buffer. * \return #PSA_SUCCESS, if the buffer was successfully * copied. - * \return #PSA_ERROR_CORRUPTION_DETECTED, if the + * \return #PSA_ERROR_BUFFER_TOO_SMALL, if the * user-supplied output buffer is too small to * hold the contents of the local buffer. */ @@ -8486,7 +8486,7 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co uint8_t *output, size_t output_len) { if (output_len < output_copy_len) { - return PSA_ERROR_CORRUPTION_DETECTED; + return PSA_ERROR_BUFFER_TOO_SMALL; } if (output_copy_len > 0) { diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 357d3423d3..94fb407f9b 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -20,7 +20,7 @@ PSA output buffer copy: output buffer larger than required copy_output:10:20:PSA_SUCCESS PSA output buffer copy: output buffer too small -copy_output:20:10:PSA_ERROR_CORRUPTION_DETECTED +copy_output:20:10:PSA_ERROR_BUFFER_TOO_SMALL PSA output buffer copy: zero-length source buffer copy_output:0:10:PSA_SUCCESS From 2f307b4216105a603ab9c19b3598e9b8818d85f6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 20 Nov 2023 12:54:09 +0000 Subject: [PATCH 105/184] De-abbreviate "len" -> "length" Signed-off-by: David Horstmann --- library/psa_crypto.c | 22 +++++------ library/psa_crypto_core.h | 4 +- .../test_suite_psa_crypto_memory.function | 38 +++++++++---------- 3 files changed, 32 insertions(+), 32 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 3327c30c43..bad55b1c65 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8502,7 +8502,7 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, psa_status_t status; input_copy->buffer = NULL; - input_copy->len = 0; + input_copy->length = 0; if (input_len == 0) { return PSA_SUCCESS; @@ -8516,10 +8516,10 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, } /* From now on, we must free input_copy->buffer on error. */ - input_copy->len = input_len; + input_copy->length = input_len; status = psa_crypto_copy_input(input, input_len, - input_copy->buffer, input_copy->len); + input_copy->buffer, input_copy->length); if (status != PSA_SUCCESS) { goto error; } @@ -8529,7 +8529,7 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, error: mbedtls_free(input_copy->buffer); input_copy->buffer = NULL; - input_copy->len = 0; + input_copy->length = 0; return status; } @@ -8537,7 +8537,7 @@ void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy) { mbedtls_free(input_copy->buffer); input_copy->buffer = NULL; - input_copy->len = 0; + input_copy->length = 0; } psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, @@ -8545,7 +8545,7 @@ psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, { output_copy->original = NULL; output_copy->buffer = NULL; - output_copy->len = 0; + output_copy->length = 0; if (output_len == 0) { return PSA_SUCCESS; @@ -8556,7 +8556,7 @@ psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, * a NULL return value means a failure of allocation. */ return PSA_ERROR_INSUFFICIENT_MEMORY; } - output_copy->len = output_len; + output_copy->length = output_len; output_copy->original = output; return PSA_SUCCESS; @@ -8567,7 +8567,7 @@ psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) psa_status_t status; if (output_copy->buffer == NULL) { - output_copy->len = 0; + output_copy->length = 0; return PSA_SUCCESS; } if (output_copy->original == NULL) { @@ -8575,15 +8575,15 @@ psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) return PSA_ERROR_CORRUPTION_DETECTED; } - status = psa_crypto_copy_output(output_copy->buffer, output_copy->len, - output_copy->original, output_copy->len); + status = psa_crypto_copy_output(output_copy->buffer, output_copy->length, + output_copy->original, output_copy->length); if (status != PSA_SUCCESS) { return status; } mbedtls_free(output_copy->buffer); output_copy->buffer = NULL; - output_copy->len = 0; + output_copy->length = 0; return PSA_SUCCESS; } diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 940748edd9..21d4bf3786 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -856,7 +856,7 @@ psa_status_t mbedtls_psa_verify_hash_abort( typedef struct psa_crypto_input_copy_s { uint8_t *buffer; - size_t len; + size_t length; } psa_crypto_input_copy_t; #define PSA_CRYPTO_INPUT_COPY_INIT { NULL, 0 } @@ -886,7 +886,7 @@ void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy); typedef struct psa_crypto_output_copy_s { uint8_t *original; uint8_t *buffer; - size_t len; + size_t length; } psa_crypto_output_copy_t; #define PSA_CRYPTO_OUTPUT_COPY_INIT { NULL, NULL, 0 } diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index e9e529989e..8421604e9d 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -98,7 +98,7 @@ void input_copy_alloc(int input_len, psa_status_t exp_status) TEST_ASSERT(input_copy.buffer != input); } TEST_MEMORY_COMPARE(input, input_len, - input_copy.buffer, input_copy.len); + input_copy.buffer, input_copy.length); } exit: @@ -113,18 +113,18 @@ void input_copy_free(int input_len) psa_crypto_input_copy_t input_copy; input_copy.buffer = NULL; - input_copy.len = input_len; - TEST_CALLOC(input_copy.buffer, input_copy.len); + input_copy.length = input_len; + TEST_CALLOC(input_copy.buffer, input_copy.length); psa_crypto_input_copy_free(&input_copy); TEST_ASSERT(input_copy.buffer == NULL); - TEST_EQUAL(input_copy.len, 0); + TEST_EQUAL(input_copy.length, 0); exit: mbedtls_free(input_copy.buffer); input_copy.buffer = NULL; - input_copy.len = 0; + input_copy.length = 0; } /* END_CASE */ @@ -139,13 +139,13 @@ void input_copy_round_trip() status = psa_crypto_input_copy_alloc(input, sizeof(input), &input_copy); TEST_EQUAL(status, PSA_SUCCESS); - TEST_MEMORY_COMPARE(input_copy.buffer, input_copy.len, + TEST_MEMORY_COMPARE(input_copy.buffer, input_copy.length, input, sizeof(input)); TEST_ASSERT(input_copy.buffer != input); psa_crypto_input_copy_free(&input_copy); TEST_ASSERT(input_copy.buffer == NULL); - TEST_EQUAL(input_copy.len, 0); + TEST_EQUAL(input_copy.length, 0); } /* END_CASE */ @@ -165,14 +165,14 @@ void output_copy_alloc(int output_len, psa_status_t exp_status) if (exp_status == PSA_SUCCESS) { TEST_ASSERT(output_copy.original == output); - TEST_EQUAL(output_copy.len, output_len); + TEST_EQUAL(output_copy.length, output_len); } exit: mbedtls_free(output_copy.buffer); output_copy.original = NULL; output_copy.buffer = NULL; - output_copy.len = 0; + output_copy.length = 0; mbedtls_free(output); output = NULL; } @@ -192,12 +192,12 @@ void output_copy_free(int output_len, int original_is_null, } TEST_CALLOC(buffer_copy_for_comparison, output_len); TEST_CALLOC(output_copy.buffer, output_len); - output_copy.len = output_len; + output_copy.length = output_len; output_copy.original = output; - if (output_copy.len != 0) { - fill_buffer_pattern(output_copy.buffer, output_copy.len); - memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.len); + if (output_copy.length != 0) { + fill_buffer_pattern(output_copy.buffer, output_copy.length); + memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.length); } status = psa_crypto_output_copy_free(&output_copy); @@ -205,7 +205,7 @@ void output_copy_free(int output_len, int original_is_null, if (exp_status == PSA_SUCCESS) { TEST_ASSERT(output_copy.buffer == NULL); - TEST_EQUAL(output_copy.len, 0); + TEST_EQUAL(output_copy.length, 0); TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, output, output_len); } @@ -214,7 +214,7 @@ exit: mbedtls_free(output); mbedtls_free(buffer_copy_for_comparison); mbedtls_free(output_copy.buffer); - output_copy.len = 0; + output_copy.length = 0; } /* END_CASE */ @@ -231,14 +231,14 @@ void output_copy_round_trip() TEST_ASSERT(output_copy.buffer != output); /* Simulate the function generating output */ - fill_buffer_pattern(output_copy.buffer, output_copy.len); + fill_buffer_pattern(output_copy.buffer, output_copy.length); - TEST_CALLOC(buffer_copy_for_comparison, output_copy.len); - memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.len); + TEST_CALLOC(buffer_copy_for_comparison, output_copy.length); + memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.length); psa_crypto_output_copy_free(&output_copy); TEST_ASSERT(output_copy.buffer == NULL); - TEST_EQUAL(output_copy.len, 0); + TEST_EQUAL(output_copy.length, 0); /* Check that the buffer was correctly copied back */ TEST_MEMORY_COMPARE(output, sizeof(output), From f1734054fa3fbc40d16d57d867dbee7a3adbd23a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 20 Nov 2023 17:15:32 +0000 Subject: [PATCH 106/184] Rename "input_copy" -> "local_input" This helps to prevent confusion as it avoids overloading the word "copy" as both an action and an object. Signed-off-by: David Horstmann --- library/psa_crypto.c | 32 ++++++------ library/psa_crypto_core.h | 20 +++---- .../suites/test_suite_psa_crypto_memory.data | 20 +++---- .../test_suite_psa_crypto_memory.function | 52 +++++++++---------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index bad55b1c65..b51b3b9398 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8496,30 +8496,30 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co return PSA_SUCCESS; } -psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, - psa_crypto_input_copy_t *input_copy) +psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len, + psa_crypto_local_input_t *local_input) { psa_status_t status; - input_copy->buffer = NULL; - input_copy->length = 0; + local_input->buffer = NULL; + local_input->length = 0; if (input_len == 0) { return PSA_SUCCESS; } - input_copy->buffer = mbedtls_calloc(input_len, 1); - if (input_copy->buffer == NULL) { + local_input->buffer = mbedtls_calloc(input_len, 1); + if (local_input->buffer == NULL) { /* Since we dealt with the zero-length case above, we know that * a NULL return value means a failure of allocation. */ return PSA_ERROR_INSUFFICIENT_MEMORY; } - /* From now on, we must free input_copy->buffer on error. */ + /* From now on, we must free local_input->buffer on error. */ - input_copy->length = input_len; + local_input->length = input_len; status = psa_crypto_copy_input(input, input_len, - input_copy->buffer, input_copy->length); + local_input->buffer, local_input->length); if (status != PSA_SUCCESS) { goto error; } @@ -8527,17 +8527,17 @@ psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, return PSA_SUCCESS; error: - mbedtls_free(input_copy->buffer); - input_copy->buffer = NULL; - input_copy->length = 0; + mbedtls_free(local_input->buffer); + local_input->buffer = NULL; + local_input->length = 0; return status; } -void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy) +void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input) { - mbedtls_free(input_copy->buffer); - input_copy->buffer = NULL; - input_copy->length = 0; + mbedtls_free(local_input->buffer); + local_input->buffer = NULL; + local_input->length = 0; } psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 21d4bf3786..ea34aab747 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -854,34 +854,34 @@ psa_status_t mbedtls_psa_verify_hash_complete( psa_status_t mbedtls_psa_verify_hash_abort( mbedtls_psa_verify_hash_interruptible_operation_t *operation); -typedef struct psa_crypto_input_copy_s { +typedef struct psa_crypto_local_input_s { uint8_t *buffer; size_t length; -} psa_crypto_input_copy_t; +} psa_crypto_local_input_t; -#define PSA_CRYPTO_INPUT_COPY_INIT { NULL, 0 } +#define PSA_CRYPTO_LOCAL_INPUT_INIT { NULL, 0 } /** Allocate a local copy of an input buffer. * * \param[in] input Pointer to input buffer. * \param[in] input_len Length of the input buffer. - * \param[out] input_copy Pointer to a psa_crypto_input_copy_t struct to - * populate with the input copy. + * \param[out] local_input Pointer to a psa_crypto_local_input_t struct to + * populate with the local input copy. * \return #PSA_SUCCESS, if the buffer was successfully * copied. * \return #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of * the buffer cannot be allocated. */ -psa_status_t psa_crypto_input_copy_alloc(const uint8_t *input, size_t input_len, - psa_crypto_input_copy_t *input_copy); +psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len, + psa_crypto_local_input_t *local_input); /** Free a local copy of an input buffer. * - * \param[in] input_copy Pointer to a psa_crypto_input_copy_t struct + * \param[in] local_input Pointer to a psa_crypto_local_input_t struct * populated by a previous call to - * psa_crypto_input_copy_alloc(). + * psa_crypto_local_input_alloc(). */ -void psa_crypto_input_copy_free(psa_crypto_input_copy_t *input_copy); +void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input); typedef struct psa_crypto_output_copy_s { uint8_t *original; diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index 94fb407f9b..d758bda0d4 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -28,20 +28,20 @@ copy_output:0:10:PSA_SUCCESS PSA output buffer copy: zero-length both buffers copy_output:0:0:PSA_SUCCESS -PSA crypto input copy alloc -input_copy_alloc:200:PSA_SUCCESS +PSA crypto local input alloc +local_input_alloc:200:PSA_SUCCESS -PSA crypto input copy alloc, NULL buffer -input_copy_alloc:0:PSA_SUCCESS +PSA crypto local input alloc, NULL buffer +local_input_alloc:0:PSA_SUCCESS -PSA crypto input copy free -input_copy_free:200 +PSA crypto local input free +local_input_free:200 -PSA crypto input copy free, NULL buffer -input_copy_free:0 +PSA crypto local input free, NULL buffer +local_input_free:0 -PSA crypto input copy round-trip -input_copy_round_trip +PSA crypto local input round-trip +local_input_round_trip PSA crypto output copy alloc output_copy_alloc:200:PSA_SUCCESS diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 8421604e9d..3d606bbb66 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -79,73 +79,73 @@ exit: /* END_CASE */ /* BEGIN_CASE */ -void input_copy_alloc(int input_len, psa_status_t exp_status) +void local_input_alloc(int input_len, psa_status_t exp_status) { uint8_t *input = NULL; - psa_crypto_input_copy_t input_copy; + psa_crypto_local_input_t local_input; psa_status_t status; - input_copy.buffer = NULL; + local_input.buffer = NULL; TEST_CALLOC(input, input_len); fill_buffer_pattern(input, input_len); - status = psa_crypto_input_copy_alloc(input, input_len, &input_copy); + status = psa_crypto_local_input_alloc(input, input_len, &local_input); TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { if (input_len != 0) { - TEST_ASSERT(input_copy.buffer != input); + TEST_ASSERT(local_input.buffer != input); } TEST_MEMORY_COMPARE(input, input_len, - input_copy.buffer, input_copy.length); + local_input.buffer, local_input.length); } exit: - mbedtls_free(input_copy.buffer); + mbedtls_free(local_input.buffer); mbedtls_free(input); } /* END_CASE */ /* BEGIN_CASE */ -void input_copy_free(int input_len) +void local_input_free(int input_len) { - psa_crypto_input_copy_t input_copy; + psa_crypto_local_input_t local_input; - input_copy.buffer = NULL; - input_copy.length = input_len; - TEST_CALLOC(input_copy.buffer, input_copy.length); + local_input.buffer = NULL; + local_input.length = input_len; + TEST_CALLOC(local_input.buffer, local_input.length); - psa_crypto_input_copy_free(&input_copy); + psa_crypto_local_input_free(&local_input); - TEST_ASSERT(input_copy.buffer == NULL); - TEST_EQUAL(input_copy.length, 0); + TEST_ASSERT(local_input.buffer == NULL); + TEST_EQUAL(local_input.length, 0); exit: - mbedtls_free(input_copy.buffer); - input_copy.buffer = NULL; - input_copy.length = 0; + mbedtls_free(local_input.buffer); + local_input.buffer = NULL; + local_input.length = 0; } /* END_CASE */ /* BEGIN_CASE */ -void input_copy_round_trip() +void local_input_round_trip() { - psa_crypto_input_copy_t input_copy; + psa_crypto_local_input_t local_input; uint8_t input[200]; psa_status_t status; fill_buffer_pattern(input, sizeof(input)); - status = psa_crypto_input_copy_alloc(input, sizeof(input), &input_copy); + status = psa_crypto_local_input_alloc(input, sizeof(input), &local_input); TEST_EQUAL(status, PSA_SUCCESS); - TEST_MEMORY_COMPARE(input_copy.buffer, input_copy.length, + TEST_MEMORY_COMPARE(local_input.buffer, local_input.length, input, sizeof(input)); - TEST_ASSERT(input_copy.buffer != input); + TEST_ASSERT(local_input.buffer != input); - psa_crypto_input_copy_free(&input_copy); - TEST_ASSERT(input_copy.buffer == NULL); - TEST_EQUAL(input_copy.length, 0); + psa_crypto_local_input_free(&local_input); + TEST_ASSERT(local_input.buffer == NULL); + TEST_EQUAL(local_input.length, 0); } /* END_CASE */ From 89875a4f20faf17a61d17bd80c287f938b6b4a0e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 20 Nov 2023 12:54:09 +0000 Subject: [PATCH 107/184] Rename "output_copy" -> "local_output" This helps to prevent confusion as it avoids overloading the word "copy" as both an action and an object. Signed-off-by: David Horstmann --- library/psa_crypto.c | 36 +++++----- library/psa_crypto_core.h | 24 +++---- .../suites/test_suite_psa_crypto_memory.data | 24 +++---- .../test_suite_psa_crypto_memory.function | 68 +++++++++---------- 4 files changed, 76 insertions(+), 76 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index b51b3b9398..781034d05f 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8540,50 +8540,50 @@ void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input) local_input->length = 0; } -psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, - psa_crypto_output_copy_t *output_copy) +psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len, + psa_crypto_local_output_t *local_output) { - output_copy->original = NULL; - output_copy->buffer = NULL; - output_copy->length = 0; + local_output->original = NULL; + local_output->buffer = NULL; + local_output->length = 0; if (output_len == 0) { return PSA_SUCCESS; } - output_copy->buffer = mbedtls_calloc(output_len, 1); - if (output_copy->buffer == NULL) { + local_output->buffer = mbedtls_calloc(output_len, 1); + if (local_output->buffer == NULL) { /* Since we dealt with the zero-length case above, we know that * a NULL return value means a failure of allocation. */ return PSA_ERROR_INSUFFICIENT_MEMORY; } - output_copy->length = output_len; - output_copy->original = output; + local_output->length = output_len; + local_output->original = output; return PSA_SUCCESS; } -psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy) +psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output) { psa_status_t status; - if (output_copy->buffer == NULL) { - output_copy->length = 0; + if (local_output->buffer == NULL) { + local_output->length = 0; return PSA_SUCCESS; } - if (output_copy->original == NULL) { + if (local_output->original == NULL) { /* We have an internal copy but nothing to copy back to. */ return PSA_ERROR_CORRUPTION_DETECTED; } - status = psa_crypto_copy_output(output_copy->buffer, output_copy->length, - output_copy->original, output_copy->length); + status = psa_crypto_copy_output(local_output->buffer, local_output->length, + local_output->original, local_output->length); if (status != PSA_SUCCESS) { return status; } - mbedtls_free(output_copy->buffer); - output_copy->buffer = NULL; - output_copy->length = 0; + mbedtls_free(local_output->buffer); + local_output->buffer = NULL; + local_output->length = 0; return PSA_SUCCESS; } diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index ea34aab747..9f76f3329d 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -883,13 +883,13 @@ psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len */ void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input); -typedef struct psa_crypto_output_copy_s { +typedef struct psa_crypto_local_output_s { uint8_t *original; uint8_t *buffer; size_t length; -} psa_crypto_output_copy_t; +} psa_crypto_local_output_t; -#define PSA_CRYPTO_OUTPUT_COPY_INIT { NULL, NULL, 0 } +#define PSA_CRYPTO_LOCAL_OUTPUT_INIT { NULL, NULL, 0 } /** Allocate a local copy of an output buffer. * @@ -897,31 +897,31 @@ typedef struct psa_crypto_output_copy_s { * output buffer but only allocates a buffer * whose contents will be copied back to the * original in a future call to - * psa_crypto_output_copy_free(). + * psa_crypto_local_output_free(). * * \param[in] output Pointer to output buffer. * \param[in] output_len Length of the output buffer. - * \param[out] output_copy Pointer to a psa_crypto_output_copy_t struct to - * populate with the output copy. + * \param[out] local_output Pointer to a psa_crypto_local_output_t struct to + * populate with the local output copy. * \return #PSA_SUCCESS, if the buffer was successfully * copied. * \return #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of * the buffer cannot be allocated. */ -psa_status_t psa_crypto_output_copy_alloc(uint8_t *output, size_t output_len, - psa_crypto_output_copy_t *output_copy); +psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len, + psa_crypto_local_output_t *local_output); /** Copy from a local copy of an output buffer back to the original, then * free the local copy. * - * \param[in] output_copy Pointer to a psa_crypto_output_copy_t struct + * \param[in] local_output Pointer to a psa_crypto_local_output_t struct * populated by a previous call to - * psa_crypto_output_copy_alloc(). - * \return #PSA_SUCCESS, if the output copy was + * psa_crypto_local_output_alloc(). + * \return #PSA_SUCCESS, if the local output was * successfully copied back to the original. * \return #PSA_ERROR_CORRUPTION_DETECTED, if the output * could not be copied back to the original. */ -psa_status_t psa_crypto_output_copy_free(psa_crypto_output_copy_t *output_copy); +psa_status_t psa_crypto_local_output_free(psa_crypto_local_output_t *local_output); #endif /* PSA_CRYPTO_CORE_H */ diff --git a/tests/suites/test_suite_psa_crypto_memory.data b/tests/suites/test_suite_psa_crypto_memory.data index d758bda0d4..2a828f573a 100644 --- a/tests/suites/test_suite_psa_crypto_memory.data +++ b/tests/suites/test_suite_psa_crypto_memory.data @@ -43,20 +43,20 @@ local_input_free:0 PSA crypto local input round-trip local_input_round_trip -PSA crypto output copy alloc -output_copy_alloc:200:PSA_SUCCESS +PSA crypto local output alloc +local_output_alloc:200:PSA_SUCCESS -PSA crypto output copy alloc, NULL buffer -output_copy_alloc:0:PSA_SUCCESS +PSA crypto local output alloc, NULL buffer +local_output_alloc:0:PSA_SUCCESS -PSA crypto output copy free -output_copy_free:200:0:PSA_SUCCESS +PSA crypto local output free +local_output_free:200:0:PSA_SUCCESS -PSA crypto output copy free, NULL buffer -output_copy_free:0:0:PSA_SUCCESS +PSA crypto local output free, NULL buffer +local_output_free:0:0:PSA_SUCCESS -PSA crypto output copy free, NULL original buffer -output_copy_free:200:1:PSA_ERROR_CORRUPTION_DETECTED +PSA crypto local output free, NULL original buffer +local_output_free:200:1:PSA_ERROR_CORRUPTION_DETECTED -PSA crypto output copy round-trip -output_copy_round_trip +PSA crypto local output round-trip +local_output_round_trip diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 3d606bbb66..2bb0f0d7ce 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -150,62 +150,62 @@ void local_input_round_trip() /* END_CASE */ /* BEGIN_CASE */ -void output_copy_alloc(int output_len, psa_status_t exp_status) +void local_output_alloc(int output_len, psa_status_t exp_status) { uint8_t *output = NULL; - psa_crypto_output_copy_t output_copy; + psa_crypto_local_output_t local_output; psa_status_t status; - output_copy.buffer = NULL; + local_output.buffer = NULL; TEST_CALLOC(output, output_len); - status = psa_crypto_output_copy_alloc(output, output_len, &output_copy); + status = psa_crypto_local_output_alloc(output, output_len, &local_output); TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { - TEST_ASSERT(output_copy.original == output); - TEST_EQUAL(output_copy.length, output_len); + TEST_ASSERT(local_output.original == output); + TEST_EQUAL(local_output.length, output_len); } exit: - mbedtls_free(output_copy.buffer); - output_copy.original = NULL; - output_copy.buffer = NULL; - output_copy.length = 0; + mbedtls_free(local_output.buffer); + local_output.original = NULL; + local_output.buffer = NULL; + local_output.length = 0; mbedtls_free(output); output = NULL; } /* END_CASE */ /* BEGIN_CASE */ -void output_copy_free(int output_len, int original_is_null, - psa_status_t exp_status) +void local_output_free(int output_len, int original_is_null, + psa_status_t exp_status) { uint8_t *output = NULL; uint8_t *buffer_copy_for_comparison = NULL; - psa_crypto_output_copy_t output_copy = PSA_CRYPTO_OUTPUT_COPY_INIT; + psa_crypto_local_output_t local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; psa_status_t status; if (!original_is_null) { TEST_CALLOC(output, output_len); } TEST_CALLOC(buffer_copy_for_comparison, output_len); - TEST_CALLOC(output_copy.buffer, output_len); - output_copy.length = output_len; - output_copy.original = output; + TEST_CALLOC(local_output.buffer, output_len); + local_output.length = output_len; + local_output.original = output; - if (output_copy.length != 0) { - fill_buffer_pattern(output_copy.buffer, output_copy.length); - memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.length); + if (local_output.length != 0) { + fill_buffer_pattern(local_output.buffer, local_output.length); + memcpy(buffer_copy_for_comparison, local_output.buffer, local_output.length); } - status = psa_crypto_output_copy_free(&output_copy); + status = psa_crypto_local_output_free(&local_output); TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { - TEST_ASSERT(output_copy.buffer == NULL); - TEST_EQUAL(output_copy.length, 0); + TEST_ASSERT(local_output.buffer == NULL); + TEST_EQUAL(local_output.length, 0); TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, output, output_len); } @@ -213,32 +213,32 @@ void output_copy_free(int output_len, int original_is_null, exit: mbedtls_free(output); mbedtls_free(buffer_copy_for_comparison); - mbedtls_free(output_copy.buffer); - output_copy.length = 0; + mbedtls_free(local_output.buffer); + local_output.length = 0; } /* END_CASE */ /* BEGIN_CASE */ -void output_copy_round_trip() +void local_output_round_trip() { - psa_crypto_output_copy_t output_copy; + psa_crypto_local_output_t local_output; uint8_t output[200]; uint8_t *buffer_copy_for_comparison = NULL; psa_status_t status; - status = psa_crypto_output_copy_alloc(output, sizeof(output), &output_copy); + status = psa_crypto_local_output_alloc(output, sizeof(output), &local_output); TEST_EQUAL(status, PSA_SUCCESS); - TEST_ASSERT(output_copy.buffer != output); + TEST_ASSERT(local_output.buffer != output); /* Simulate the function generating output */ - fill_buffer_pattern(output_copy.buffer, output_copy.length); + fill_buffer_pattern(local_output.buffer, local_output.length); - TEST_CALLOC(buffer_copy_for_comparison, output_copy.length); - memcpy(buffer_copy_for_comparison, output_copy.buffer, output_copy.length); + TEST_CALLOC(buffer_copy_for_comparison, local_output.length); + memcpy(buffer_copy_for_comparison, local_output.buffer, local_output.length); - psa_crypto_output_copy_free(&output_copy); - TEST_ASSERT(output_copy.buffer == NULL); - TEST_EQUAL(output_copy.length, 0); + psa_crypto_local_output_free(&local_output); + TEST_ASSERT(local_output.buffer == NULL); + TEST_EQUAL(local_output.length, 0); /* Check that the buffer was correctly copied back */ TEST_MEMORY_COMPARE(output, sizeof(output), From 10f8366499e7a45b67d55c88160ecda1d652e2f9 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Tue, 21 Nov 2023 09:33:54 +0000 Subject: [PATCH 108/184] Make RSA unblinding constant flow Signed-off-by: Janos Follath --- library/rsa.c | 38 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 36 insertions(+), 2 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 3c538bf439..c559f110ac 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -40,6 +40,7 @@ #if defined(MBEDTLS_RSA_C) #include "mbedtls/rsa.h" +#include "bignum_core.h" #include "rsa_alt_helpers.h" #include "mbedtls/oid.h" #include "mbedtls/platform_util.h" @@ -981,6 +982,40 @@ cleanup: return ret; } +/* + * Unblind + * T = T * Vf mod N + */ +int rsa_unblind(mbedtls_mpi* T, mbedtls_mpi* Vf, mbedtls_mpi* N) +{ + int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; + const mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N->p); + const size_t nlimbs = N->n; + const size_t tlimbs = mbedtls_mpi_core_montmul_working_limbs(nlimbs); + mbedtls_mpi RR, M_T; + + mbedtls_mpi_init(&RR); + mbedtls_mpi_init(&M_T); + + MBEDTLS_MPI_CHK(mbedtls_mpi_core_get_mont_r2_unsafe(&RR, N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(&M_T, tlimbs)); + + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(T, nlimbs)); + MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Vf, nlimbs)); + + // T = T * R mod N + mbedtls_mpi_core_to_mont_rep(T->p, T->p, N->p, nlimbs, mm, RR.p, M_T.p); + // T = T * Vf mod N + mbedtls_mpi_core_montmul(T->p, T->p, Vf->p, nlimbs, N->p, nlimbs, mm, M_T.p); + +cleanup: + + mbedtls_mpi_free(&RR); + mbedtls_mpi_free(&M_T); + + return ret; +} + /* * Exponent blinding supposed to prevent side-channel attacks using multiple * traces of measurements to recover the RSA key. The more collisions are there, @@ -1172,8 +1207,7 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, * Unblind * T = T * Vf mod N */ - MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &T, &ctx->Vf)); - MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &T, &ctx->N)); + MBEDTLS_MPI_CHK(rsa_unblind(&T, &ctx->Vf, &ctx->N)); /* Verify the result to prevent glitching attacks. */ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&C, &T, &ctx->E, From 2d8624dae26734f55d1bd9aaad1abefc084b2e57 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Tue, 21 Nov 2023 09:46:43 +0000 Subject: [PATCH 109/184] Extend blinding to RSA result check Signed-off-by: Janos Follath --- library/rsa.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index c559f110ac..9dc0ec505c 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1125,8 +1125,6 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, goto cleanup; } - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&I, &T)); - /* * Blinding * T = T * Vi mod N @@ -1135,6 +1133,8 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &T, &ctx->Vi)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &T, &ctx->N)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&I, &T)); + /* * Exponent blinding */ @@ -1203,12 +1203,6 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&T, &TQ, &TP)); #endif /* MBEDTLS_RSA_NO_CRT */ - /* - * Unblind - * T = T * Vf mod N - */ - MBEDTLS_MPI_CHK(rsa_unblind(&T, &ctx->Vf, &ctx->N)); - /* Verify the result to prevent glitching attacks. */ MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&C, &T, &ctx->E, &ctx->N, &ctx->RN)); @@ -1217,6 +1211,12 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, goto cleanup; } + /* + * Unblind + * T = T * Vf mod N + */ + MBEDTLS_MPI_CHK(rsa_unblind(&T, &ctx->Vf, &ctx->N)); + olen = ctx->len; MBEDTLS_MPI_CHK(mbedtls_mpi_write_binary(&T, output, olen)); From 31003ffa46ada17c67e6ab544b2195b31e312535 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 23 Nov 2023 15:45:29 +0000 Subject: [PATCH 110/184] Add casts to local input / output initializers Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 9f76f3329d..3d7198ac47 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -859,7 +859,7 @@ typedef struct psa_crypto_local_input_s { size_t length; } psa_crypto_local_input_t; -#define PSA_CRYPTO_LOCAL_INPUT_INIT { NULL, 0 } +#define PSA_CRYPTO_LOCAL_INPUT_INIT ((psa_crypto_local_input_t) { NULL, 0 }) /** Allocate a local copy of an input buffer. * @@ -889,7 +889,7 @@ typedef struct psa_crypto_local_output_s { size_t length; } psa_crypto_local_output_t; -#define PSA_CRYPTO_LOCAL_OUTPUT_INIT { NULL, NULL, 0 } +#define PSA_CRYPTO_LOCAL_OUTPUT_INIT ((psa_crypto_local_output_t) { NULL, NULL, 0 }) /** Allocate a local copy of an output buffer. * From 9db14486daf3545370cb0bef32849947611f6ed0 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 23 Nov 2023 15:50:37 +0000 Subject: [PATCH 111/184] Use initializers in alloc functions Signed-off-by: David Horstmann --- library/psa_crypto.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 781034d05f..e444c0c650 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8501,8 +8501,7 @@ psa_status_t psa_crypto_local_input_alloc(const uint8_t *input, size_t input_len { psa_status_t status; - local_input->buffer = NULL; - local_input->length = 0; + *local_input = PSA_CRYPTO_LOCAL_INPUT_INIT; if (input_len == 0) { return PSA_SUCCESS; @@ -8543,9 +8542,7 @@ void psa_crypto_local_input_free(psa_crypto_local_input_t *local_input) psa_status_t psa_crypto_local_output_alloc(uint8_t *output, size_t output_len, psa_crypto_local_output_t *local_output) { - local_output->original = NULL; - local_output->buffer = NULL; - local_output->length = 0; + *local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; if (output_len == 0) { return PSA_SUCCESS; From a575a5a26ad206b5c35f80ffbcb2fe8ddb54abcc Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 23 Nov 2023 15:59:30 +0000 Subject: [PATCH 112/184] Improve description of psa_crypto_input_copy_alloc Signed-off-by: David Horstmann --- library/psa_crypto_core.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/library/psa_crypto_core.h b/library/psa_crypto_core.h index 3d7198ac47..87b0035521 100644 --- a/library/psa_crypto_core.h +++ b/library/psa_crypto_core.h @@ -861,12 +861,12 @@ typedef struct psa_crypto_local_input_s { #define PSA_CRYPTO_LOCAL_INPUT_INIT ((psa_crypto_local_input_t) { NULL, 0 }) -/** Allocate a local copy of an input buffer. +/** Allocate a local copy of an input buffer and copy the contents into it. * * \param[in] input Pointer to input buffer. * \param[in] input_len Length of the input buffer. - * \param[out] local_input Pointer to a psa_crypto_local_input_t struct to - * populate with the local input copy. + * \param[out] local_input Pointer to a psa_crypto_local_input_t struct + * containing a local input copy. * \return #PSA_SUCCESS, if the buffer was successfully * copied. * \return #PSA_ERROR_INSUFFICIENT_MEMORY, if a copy of From 3fd3d05196b2da6fe4be48b02953a276a78756f1 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 2 Nov 2023 17:14:01 +0100 Subject: [PATCH 113/184] New files for memory-related test functions Signed-off-by: Gilles Peskine --- tests/include/test/memory.h | 18 ++++++++++++++++++ tests/src/test_memory.c | 15 +++++++++++++++ 2 files changed, 33 insertions(+) create mode 100644 tests/include/test/memory.h create mode 100644 tests/src/test_memory.c diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h new file mode 100644 index 0000000000..ef05112f2f --- /dev/null +++ b/tests/include/test/memory.h @@ -0,0 +1,18 @@ +/** + * \file memory.h + * + * \brief Helper macros and functions related to testing memory management. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef TEST_MEMORY_H +#define TEST_MEMORY_H + +#include "mbedtls/build_info.h" +#include "mbedtls/platform.h" + +#endif /* TEST_MEMORY_H */ diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c new file mode 100644 index 0000000000..cda91e5eaf --- /dev/null +++ b/tests/src/test_memory.c @@ -0,0 +1,15 @@ +/** + * \file memory.c + * + * \brief Helper functions related to testing memory management. + */ + +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include +#include +#include + From d29cce91d03aef747b5ee1766f0c3808aaa2e34a Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 2 Nov 2023 20:49:34 +0100 Subject: [PATCH 114/184] Add memory poisoning framework While an area of memory is poisoned, reading or writing from it triggers a sanitizer violation. Implemented for ASan. Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 41 ++++++++++++++++++++ tests/include/test/memory.h | 75 +++++++++++++++++++++++++++++++++++++ tests/src/test_memory.c | 22 +++++++++++ 3 files changed, 138 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index b8dffa9bbd..6ab240c592 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -32,6 +32,7 @@ #include #include "test/helpers.h" #include "test/macros.h" +#include "test/memory.h" #include #include @@ -143,6 +144,45 @@ void memory_leak(const char *name) /* Leak of a heap object */ } +/* name = "test_memory_poison_%(start)_%(offset)_%(count)" + * Poison a region starting at start from an 8-byte aligned origin, + * encompassing count bytes. Access the region at offset from the start. + */ +void test_memory_poison(const char *name) +{ + size_t start = 0, offset = 0, count = 0; + if (sscanf(name, "%*[^0-9]%zu%*[^0-9]%zu%*[^0-9]%zu", + &start, &offset, &count) != 3) { + mbedtls_fprintf(stderr, "%s: Bad name format: %s\n", __func__, name); + return; + } + + union { + long long ll; + unsigned char buf[32]; + } aligned; + memset(aligned.buf, 'a', sizeof(aligned.buf)); + + if (start > sizeof(aligned.buf)) { + mbedtls_fprintf(stderr, "%s: start=%zu > size=%zu", __func__, + start, sizeof(aligned.buf)); + return; + } + if (start + count > sizeof(aligned.buf)) { + mbedtls_fprintf(stderr, "%s: start+count=%zu > size=%zu", __func__, + start + count, sizeof(aligned.buf)); + return; + } + if (offset >= count) { + mbedtls_fprintf(stderr, "%s: offset=%zu >= count=%zu", __func__, + offset, count); + return; + } + + MBEDTLS_TEST_MEMORY_POISON(aligned.buf + start, count); + mbedtls_printf("%u\n", (unsigned) aligned.buf[start + offset]); +} + /****************************************************************/ /* Threading */ @@ -291,6 +331,7 @@ metatest_t metatests[] = { { "double_free", "asan", double_free }, { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, + { "test_memory_poison_0_0_8", "asan", test_memory_poison }, { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index ef05112f2f..27baf7a1dc 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -15,4 +15,79 @@ #include "mbedtls/build_info.h" #include "mbedtls/platform.h" +/** \def MBEDTLS_TEST_MEMORY_CAN_POISON + * + * This macro is defined if the tests are compiled with a method to mark + * memory as poisoned, which can be used to enforce some memory access + * policies. + * + * Currently, only Asan (Address Sanitizer) is supported. + */ +#if defined(__SANITIZE_ADDRESS__) +# define MBEDTLS_TEST_HAVE_ASAN +#endif +#if defined(__has_feature) +# if __has_feature(address_sanitizer) +# define MBEDTLS_TEST_HAVE_ASAN +# endif +#endif +#if defined(MBEDTLS_TEST_HAVE_ASAN) +# define MBEDTLS_TEST_MEMORY_CAN_POISON +#endif + +/** \def MBEDTLS_TEST_MEMORY_POISON(buf, size) + * + * Poison a memory area so that any attempt to read or write from it will + * cause a runtime failure. + * + * The behavior is undefined if any part of the memory area is invalid. + * + * This is a no-op in builds without a poisoning method. + * See #MBEDTLS_TEST_MEMORY_CAN_POISON. + * + * \param buf Pointer to the beginning of the memory area to poison. + * \param size Size of the memory area in bytes. + */ + +/** \def MBEDTLS_TEST_MEMORY_UNPOISON(buf, size) + * + * Undo the effect of #MBEDTLS_TEST_MEMORY_POISON. + * + * The behavior is undefined if any part of the memory area is invalid, + * or if the memory area contains a mixture of poisoned and unpoisoned parts. + * + * This is a no-op in builds without a poisoning method. + * See #MBEDTLS_TEST_MEMORY_CAN_POISON. + * + * \param buf Pointer to the beginning of the memory area to unpoison. + * \param size Size of the memory area in bytes. + */ + +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + +/** Poison a memory area so that any attempt to read or write from it will + * cause a runtime failure. + * + * The behavior is undefined if any part of the memory area is invalid. + */ +void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size); +#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) \ + mbedtls_test_memory_poison(ptr, size) + +/** Undo the effect of mbedtls_test_memory_poison(). + * + * This is a no-op if the given area is entirely valid, unpoisoned memory. + * + * The behavior is undefined if any part of the memory area is invalid, + * or if the memory area contains a mixture of poisoned and unpoisoned parts. + */ +void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); +#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) \ + mbedtls_test_memory_unpoison(ptr, size) + +#else /* MBEDTLS_TEST_MEMORY_CAN_POISON */ +#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) ((void) 0) +#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) ((void) 0) +#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ + #endif /* TEST_MEMORY_H */ diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index cda91e5eaf..6b1404bc3c 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -13,3 +13,25 @@ #include #include +#if defined(MBEDTLS_TEST_HAVE_ASAN) +#include +#include +#endif + +#if defined(MBEDTLS_TEST_HAVE_ASAN) +void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) +{ + if (size == 0) { + return; + } + __asan_poison_memory_region(ptr, size); +} + +void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size) +{ + if (size == 0) { + return; + } + __asan_unpoison_memory_region(ptr, size); +} +#endif /* Asan */ From 0bdb6dc079fd26ef1fe1ed4543c568f1d79159b7 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 2 Nov 2023 22:44:32 +0100 Subject: [PATCH 115/184] Fix memory poisoning with Asan on arbitrary byte boundaries Asan poisons memory with an 8-byte granularity. We want to make sure that the whole specified region is poisoned (our typical use case is a heap-allocated object, and we want to poison the whole object, and we don't care about the bytes after the end of the object and up to the beginning of the next object). So align the start and end of the region to (un)poison to an 8-byte boundary. Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 7 +++++++ tests/include/test/memory.h | 4 ++++ tests/src/test_memory.c | 17 +++++++++++++++++ 3 files changed, 28 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 6ab240c592..a3d9d4095a 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -332,6 +332,13 @@ metatest_t metatests[] = { { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, { "test_memory_poison_0_0_8", "asan", test_memory_poison }, + { "test_memory_poison_0_7_8", "asan", test_memory_poison }, + { "test_memory_poison_0_0_1", "asan", test_memory_poison }, + { "test_memory_poison_0_1_2", "asan", test_memory_poison }, + { "test_memory_poison_7_0_8", "asan", test_memory_poison }, + { "test_memory_poison_7_7_8", "asan", test_memory_poison }, + { "test_memory_poison_7_0_1", "asan", test_memory_poison }, + { "test_memory_poison_7_1_2", "asan", test_memory_poison }, { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index 27baf7a1dc..f610a97a45 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -40,6 +40,10 @@ * Poison a memory area so that any attempt to read or write from it will * cause a runtime failure. * + * Depending on the implementation, this may poison a few bytes beyond the + * indicated region, but will never poison a separate object on the heap + * or a separate object with more than the alignment of a long long. + * * The behavior is undefined if any part of the memory area is invalid. * * This is a no-op in builds without a poisoning method. diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index 6b1404bc3c..c277be85ab 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -19,11 +19,27 @@ #endif #if defined(MBEDTLS_TEST_HAVE_ASAN) +static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) +{ + uintptr_t start = (uintptr_t) *p_ptr; + uintptr_t end = start + (uintptr_t) *p_size; + /* ASan can only poison regions with 8-byte alignment, and only poisons a + * region if it's fully within the requested range. We want to poison the + * whole requested region and don't mind a few extra bytes. Therefore, + * align start down to an 8-byte boundary, and end up to an 8-byte + * boundary. */ + start = start & ~(uintptr_t) 7; + end = (end + 7) & ~(uintptr_t) 7; + *p_ptr = (const unsigned char *) start; + *p_size = end - start; +} + void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) { if (size == 0) { return; } + align_for_asan(&ptr, &size); __asan_poison_memory_region(ptr, size); } @@ -32,6 +48,7 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size) if (size == 0) { return; } + align_for_asan(&ptr, &size); __asan_unpoison_memory_region(ptr, size); } #endif /* Asan */ From 479a1944e886ef755e7bcbfae380a20eac525eff Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 2 Nov 2023 23:07:33 +0100 Subject: [PATCH 116/184] Basic functional test for memory poisoning Signed-off-by: Gilles Peskine --- tests/suites/test_suite_test_helpers.data | 23 +++++++++++ tests/suites/test_suite_test_helpers.function | 40 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 tests/suites/test_suite_test_helpers.data create mode 100644 tests/suites/test_suite_test_helpers.function diff --git a/tests/suites/test_suite_test_helpers.data b/tests/suites/test_suite_test_helpers.data new file mode 100644 index 0000000000..1d221d7bf1 --- /dev/null +++ b/tests/suites/test_suite_test_helpers.data @@ -0,0 +1,23 @@ +Memory poison+unpoison: offset=0 len=42 +memory_poison_unpoison:0:42 + +Memory poison+unpoison: offset=0 len=1 +memory_poison_unpoison:0:1 + +Memory poison+unpoison: offset=0 len=2 +memory_poison_unpoison:0:2 + +Memory poison+unpoison: offset=1 len=1 +memory_poison_unpoison:1:1 + +Memory poison+unpoison: offset=1 len=2 +memory_poison_unpoison:1:2 + +Memory poison+unpoison: offset=7 len=1 +memory_poison_unpoison:7:1 + +Memory poison+unpoison: offset=7 len=2 +memory_poison_unpoison:7:2 + +Memory poison+unpoison: offset=0 len=0 +memory_poison_unpoison:0:0 diff --git a/tests/suites/test_suite_test_helpers.function b/tests/suites/test_suite_test_helpers.function new file mode 100644 index 0000000000..8c5d5adf65 --- /dev/null +++ b/tests/suites/test_suite_test_helpers.function @@ -0,0 +1,40 @@ +/* BEGIN_HEADER */ + +/* Test some parts of the test framework. */ + +#include +#include + +/* END_HEADER */ + +/* BEGIN_DEPENDENCIES */ + +/* END_DEPENDENCIES */ + +/* BEGIN_CASE depends_on:MBEDTLS_TEST_MEMORY_CAN_POISON */ +/* Test that poison+unpoison leaves the memory accessible. */ +/* We can't test that poisoning makes the memory inaccessible: + * there's no sane way to catch an Asan/Valgrind complaint. + * That negative testing is done in programs/test/metatest.c. */ +void memory_poison_unpoison(int align, int size) +{ + unsigned char *buf = NULL; + const size_t buffer_size = align + size; + TEST_CALLOC(buf, buffer_size); + + for (size_t i = 0; i < buffer_size; i++) { + buf[i] = (unsigned char) (i & 0xff); + } + + const unsigned char *start = buf == NULL ? NULL : buf + align; + mbedtls_test_memory_poison(start, (size_t) size); + mbedtls_test_memory_unpoison(start, (size_t) size); + + for (size_t i = 0; i < buffer_size; i++) { + TEST_EQUAL(buf[i], (unsigned char) (i & 0xff)); + } + +exit: + mbedtls_free(buf); +} +/* END_CASE */ From f5dd00288e3410aeaa25c5c7f127bd8c164f0317 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 3 Nov 2023 17:01:32 +0100 Subject: [PATCH 117/184] Fix MSVC build failure Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index a3d9d4095a..6a3e7a911b 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -41,6 +41,11 @@ #include #endif +/* C99 feature missing from older versions of MSVC */ +#if (defined(_MSC_VER) && (_MSC_VER <= 1900)) +#define /*no-check-names*/ __func__ __FUNCTION__ +#endif + /* This is an external variable, so the compiler doesn't know that we're never * changing its value. From e0acf8787dbfdbf1b41cbf81657858ee49806677 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Fri, 3 Nov 2023 19:41:44 +0100 Subject: [PATCH 118/184] Port to platforms where printf doesn't have %zu Reuse the existing abstraction from include/mbedtls/debug.h. Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 6a3e7a911b..6e476b7b32 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -28,6 +28,7 @@ #define MBEDTLS_ALLOW_PRIVATE_ACCESS +#include #include #include #include "test/helpers.h" @@ -156,7 +157,10 @@ void memory_leak(const char *name) void test_memory_poison(const char *name) { size_t start = 0, offset = 0, count = 0; - if (sscanf(name, "%*[^0-9]%zu%*[^0-9]%zu%*[^0-9]%zu", + if (sscanf(name, + "%*[^0-9]%" MBEDTLS_PRINTF_SIZET + "%*[^0-9]%" MBEDTLS_PRINTF_SIZET + "%*[^0-9]%" MBEDTLS_PRINTF_SIZET, &start, &offset, &count) != 3) { mbedtls_fprintf(stderr, "%s: Bad name format: %s\n", __func__, name); return; @@ -169,18 +173,24 @@ void test_memory_poison(const char *name) memset(aligned.buf, 'a', sizeof(aligned.buf)); if (start > sizeof(aligned.buf)) { - mbedtls_fprintf(stderr, "%s: start=%zu > size=%zu", __func__, - start, sizeof(aligned.buf)); + mbedtls_fprintf(stderr, + "%s: start=%" MBEDTLS_PRINTF_SIZET + " > size=%" MBEDTLS_PRINTF_SIZET, + __func__, start, sizeof(aligned.buf)); return; } if (start + count > sizeof(aligned.buf)) { - mbedtls_fprintf(stderr, "%s: start+count=%zu > size=%zu", __func__, - start + count, sizeof(aligned.buf)); + mbedtls_fprintf(stderr, + "%s: start+count=%" MBEDTLS_PRINTF_SIZET + " > size=%" MBEDTLS_PRINTF_SIZET, + __func__, start + count, sizeof(aligned.buf)); return; } if (offset >= count) { - mbedtls_fprintf(stderr, "%s: offset=%zu >= count=%zu", __func__, - offset, count); + mbedtls_fprintf(stderr, + "%s: offset=%" MBEDTLS_PRINTF_SIZET + " >= count=%" MBEDTLS_PRINTF_SIZET, + __func__, offset, count); return; } From abfad786559fea0baeb2ae55eb6a0e123404a6f5 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 22 Nov 2023 18:13:23 +0100 Subject: [PATCH 119/184] Use the existing definition of MBEDTLS_TEST_HAVE_ASAN A definition now exists in tests/helpers.h, which is a better place. Signed-off-by: Gilles Peskine --- tests/include/test/memory.h | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index f610a97a45..fdf7a06c1b 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -14,6 +14,7 @@ #include "mbedtls/build_info.h" #include "mbedtls/platform.h" +#include "test/helpers.h" /** \def MBEDTLS_TEST_MEMORY_CAN_POISON * @@ -23,14 +24,6 @@ * * Currently, only Asan (Address Sanitizer) is supported. */ -#if defined(__SANITIZE_ADDRESS__) -# define MBEDTLS_TEST_HAVE_ASAN -#endif -#if defined(__has_feature) -# if __has_feature(address_sanitizer) -# define MBEDTLS_TEST_HAVE_ASAN -# endif -#endif #if defined(MBEDTLS_TEST_HAVE_ASAN) # define MBEDTLS_TEST_MEMORY_CAN_POISON #endif From 81f8132bd5cbcb0476644b7760e60d152b9b2b68 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 22 Nov 2023 18:13:46 +0100 Subject: [PATCH 120/184] Avoid unused variable warnings in some plausible usage Signed-off-by: Gilles Peskine --- tests/include/test/memory.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index fdf7a06c1b..c22ef53f7c 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -83,8 +83,8 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); mbedtls_test_memory_unpoison(ptr, size) #else /* MBEDTLS_TEST_MEMORY_CAN_POISON */ -#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) ((void) 0) -#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) ((void) 0) +#define MBEDTLS_TEST_MEMORY_POISON(ptr, size) ((void) (ptr), (void) (size)) +#define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) ((void) (ptr), (void) (size)) #endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ #endif /* TEST_MEMORY_H */ From ef0f01fee6d7371fda5bf202b9b0b4f219179313 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 22 Nov 2023 18:22:07 +0100 Subject: [PATCH 121/184] Memory poisoning: meta-test writes as well as reads Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 41 ++++++++++++++++++++++++++++------------ 1 file changed, 29 insertions(+), 12 deletions(-) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 6e476b7b32..0910798e1c 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -150,18 +150,22 @@ void memory_leak(const char *name) /* Leak of a heap object */ } -/* name = "test_memory_poison_%(start)_%(offset)_%(count)" +/* name = "test_memory_poison_%(start)_%(offset)_%(count)_%(direction)" * Poison a region starting at start from an 8-byte aligned origin, * encompassing count bytes. Access the region at offset from the start. + * %(start), %(offset) and %(count) are decimal integers. + * %(direction) is either the character 'r' for read or 'w' for write. */ void test_memory_poison(const char *name) { size_t start = 0, offset = 0, count = 0; + char direction = 'r'; if (sscanf(name, "%*[^0-9]%" MBEDTLS_PRINTF_SIZET "%*[^0-9]%" MBEDTLS_PRINTF_SIZET - "%*[^0-9]%" MBEDTLS_PRINTF_SIZET, - &start, &offset, &count) != 3) { + "%*[^0-9]%" MBEDTLS_PRINTF_SIZET + "_%c", + &start, &offset, &count, &direction) != 4) { mbedtls_fprintf(stderr, "%s: Bad name format: %s\n", __func__, name); return; } @@ -195,7 +199,12 @@ void test_memory_poison(const char *name) } MBEDTLS_TEST_MEMORY_POISON(aligned.buf + start, count); - mbedtls_printf("%u\n", (unsigned) aligned.buf[start + offset]); + + if (direction == 'w') { + aligned.buf[start + offset] = 'b'; + } else { + mbedtls_printf("%u\n", (unsigned) aligned.buf[start + offset]); + } } @@ -346,14 +355,22 @@ metatest_t metatests[] = { { "double_free", "asan", double_free }, { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, - { "test_memory_poison_0_0_8", "asan", test_memory_poison }, - { "test_memory_poison_0_7_8", "asan", test_memory_poison }, - { "test_memory_poison_0_0_1", "asan", test_memory_poison }, - { "test_memory_poison_0_1_2", "asan", test_memory_poison }, - { "test_memory_poison_7_0_8", "asan", test_memory_poison }, - { "test_memory_poison_7_7_8", "asan", test_memory_poison }, - { "test_memory_poison_7_0_1", "asan", test_memory_poison }, - { "test_memory_poison_7_1_2", "asan", test_memory_poison }, + { "test_memory_poison_0_0_8_r", "asan", test_memory_poison }, + { "test_memory_poison_0_0_8_w", "asan", test_memory_poison }, + { "test_memory_poison_0_7_8_r", "asan", test_memory_poison }, + { "test_memory_poison_0_7_8_w", "asan", test_memory_poison }, + { "test_memory_poison_0_0_1_r", "asan", test_memory_poison }, + { "test_memory_poison_0_0_1_w", "asan", test_memory_poison }, + { "test_memory_poison_0_1_2_r", "asan", test_memory_poison }, + { "test_memory_poison_0_1_2_w", "asan", test_memory_poison }, + { "test_memory_poison_7_0_8_r", "asan", test_memory_poison }, + { "test_memory_poison_7_0_8_w", "asan", test_memory_poison }, + { "test_memory_poison_7_7_8_r", "asan", test_memory_poison }, + { "test_memory_poison_7_7_8_w", "asan", test_memory_poison }, + { "test_memory_poison_7_0_1_r", "asan", test_memory_poison }, + { "test_memory_poison_7_0_1_w", "asan", test_memory_poison }, + { "test_memory_poison_7_1_2_r", "asan", test_memory_poison }, + { "test_memory_poison_7_1_2_w", "asan", test_memory_poison }, { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, From 895ebc30f09558ae18319898f029982d8412bb42 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Nov 2023 12:33:39 +0100 Subject: [PATCH 122/184] Protect against compiler optimizations GCC 5.4 optimized the write after poisoning (the surprising thing is that 11.4 doesn't). Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 0910798e1c..1724aed0bd 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -66,6 +66,15 @@ static void set_to_zero_but_the_compiler_does_not_know(volatile void *p, size_t memset((void *) p, false_but_the_compiler_does_not_know, n); } +/* Simulate an access to the given object, to avoid compiler optimizations + * in code that prepares or consumes the object. */ +static void do_nothing_with_object(void *p) +{ + (void) p; +} +void(*volatile do_nothing_with_object_but_the_compiler_does_not_know)(void *) = + do_nothing_with_object; + /****************************************************************/ /* Test framework features */ @@ -202,7 +211,9 @@ void test_memory_poison(const char *name) if (direction == 'w') { aligned.buf[start + offset] = 'b'; + do_nothing_with_object_but_the_compiler_does_not_know(aligned.buf); } else { + do_nothing_with_object_but_the_compiler_does_not_know(aligned.buf); mbedtls_printf("%u\n", (unsigned) aligned.buf[start + offset]); } } From 6f8ca29ce45087533dc08f10c384487a5494b342 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 29 Nov 2023 19:42:43 +0100 Subject: [PATCH 123/184] Use the existing definition of __func__ Now that library is in the include path when compiling metatest.c, don't duplicate the definition of __func__ from library/common.h. Signed-off-by: Gilles Peskine --- programs/test/metatest.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 1724aed0bd..392b638420 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -34,6 +34,7 @@ #include "test/helpers.h" #include "test/macros.h" #include "test/memory.h" +#include "common.h" #include #include @@ -42,11 +43,6 @@ #include #endif -/* C99 feature missing from older versions of MSVC */ -#if (defined(_MSC_VER) && (_MSC_VER <= 1900)) -#define /*no-check-names*/ __func__ __FUNCTION__ -#endif - /* This is an external variable, so the compiler doesn't know that we're never * changing its value. From a7d5662f15916c500fbe6418908f5135f0156061 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 29 Nov 2023 20:49:04 +0100 Subject: [PATCH 124/184] Allow PSA test cases to depend on test conditions In particular, this allows MBEDTLS_TEST_HOOKS. Signed-off-by: Gilles Peskine --- tests/scripts/all.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 93a32aefee..c3d2fb88c1 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -1046,7 +1046,7 @@ component_check_test_dependencies () { grep 'depends_on' \ tests/suites/test_suite_psa*.data tests/suites/test_suite_psa*.function | grep -Eo '!?MBEDTLS_[^: ]*' | - grep -v MBEDTLS_PSA_ | + grep -v -e MBEDTLS_PSA_ -e MBEDTLS_TEST_ | sort -u > $found # Expected ones with justification - keep in sorted order by ASCII table! From aa5cc7b930c96f2580562925ce654aad6befa457 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Tue, 21 Nov 2023 09:57:27 +0000 Subject: [PATCH 125/184] Add Changelog for the Marvin attack fix Signed-off-by: Janos Follath --- ChangeLog.d/fix-Marvin-attack.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ChangeLog.d/fix-Marvin-attack.txt diff --git a/ChangeLog.d/fix-Marvin-attack.txt b/ChangeLog.d/fix-Marvin-attack.txt new file mode 100644 index 0000000000..f729304eef --- /dev/null +++ b/ChangeLog.d/fix-Marvin-attack.txt @@ -0,0 +1,6 @@ +Security + * Fix a timing side channel in RSA private operations. This side channel + could be sufficient for a local attacker to recover the plaintext. It + requires the attecker to send a large number of messages for decryption. + For details, see "Everlasting ROBOT: the Marvin Attack", Hubert Kario. + Reported by Hubert Kario, Red Hat. From 8209ff335ef94fbb949f5bd634bcbc2e0ba86a49 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Tue, 21 Nov 2023 12:48:52 +0000 Subject: [PATCH 126/184] Make local function static Signed-off-by: Janos Follath --- library/rsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/rsa.c b/library/rsa.c index 9dc0ec505c..3095fec883 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -986,7 +986,7 @@ cleanup: * Unblind * T = T * Vf mod N */ -int rsa_unblind(mbedtls_mpi* T, mbedtls_mpi* Vf, mbedtls_mpi* N) +static int rsa_unblind(mbedtls_mpi* T, mbedtls_mpi* Vf, mbedtls_mpi* N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; const mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N->p); From f7f88d6443f369e384731667c47fe28f829bfdda Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Tue, 21 Nov 2023 14:20:08 +0000 Subject: [PATCH 127/184] Fix style Signed-off-by: Janos Follath --- library/rsa.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/library/rsa.c b/library/rsa.c index 3095fec883..8ddef2dac3 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -986,7 +986,7 @@ cleanup: * Unblind * T = T * Vf mod N */ -static int rsa_unblind(mbedtls_mpi* T, mbedtls_mpi* Vf, mbedtls_mpi* N) +static int rsa_unblind(mbedtls_mpi *T, mbedtls_mpi *Vf, mbedtls_mpi *N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; const mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N->p); From 372b8bb6c511d25018ed9390f851915c1c6e8c5a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 24 Nov 2023 16:21:04 +0000 Subject: [PATCH 128/184] Add memory poisoning hooks Signed-off-by: David Horstmann --- library/CMakeLists.txt | 3 ++- library/psa_crypto.c | 20 ++++++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 5c297e0a1f..52178e474a 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -329,7 +329,8 @@ foreach(target IN LISTS target_libraries) $ PRIVATE ${MBEDTLS_DIR}/library/ # Needed to include psa_crypto_driver_wrappers.h - ${CMAKE_CURRENT_BINARY_DIR}) + ${CMAKE_CURRENT_BINARY_DIR} + ${MBEDTLS_DIR}/tests/include/) # Pass-through MBEDTLS_CONFIG_FILE and MBEDTLS_USER_CONFIG_FILE if(MBEDTLS_CONFIG_FILE) target_compile_definitions(${target} diff --git a/library/psa_crypto.c b/library/psa_crypto.c index e3187d8d28..ddc834f95c 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -72,6 +72,10 @@ #include "mbedtls/sha512.h" #include "md_psa.h" +#if defined(MBEDTLS_TEST_HOOKS) +#include "test/memory.h" +#endif + #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) @@ -8451,10 +8455,18 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, return PSA_ERROR_CORRUPTION_DETECTED; } +#if defined(MBEDTLS_TEST_HOOKS) + MBEDTLS_TEST_MEMORY_UNPOISON(input, input_len); +#endif + if (input_len > 0) { memcpy(input_copy, input, input_len); } +#if defined(MBEDTLS_TEST_HOOKS) + MBEDTLS_TEST_MEMORY_POISON(input, input_len); +#endif + return PSA_SUCCESS; } @@ -8478,10 +8490,18 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co return PSA_ERROR_BUFFER_TOO_SMALL; } +#if defined(MBEDTLS_TEST_HOOKS) + MBEDTLS_TEST_MEMORY_UNPOISON(output, output_len); +#endif + if (output_copy_len > 0) { memcpy(output, output_copy, output_copy_len); } +#if defined(MBEDTLS_TEST_HOOKS) + MBEDTLS_TEST_MEMORY_POISON(output, output_len); +#endif + return PSA_SUCCESS; } From 38e4e9c499f05c5fc8ecee56c00d96a86635555a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 24 Nov 2023 18:26:47 +0000 Subject: [PATCH 129/184] Add explicit UNPOISON calls to memory tests These are needed to allow them to operate on buffer copies without triggering ASan use-after-poison detection. Signed-off-by: David Horstmann --- tests/suites/test_suite_psa_crypto_memory.function | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/suites/test_suite_psa_crypto_memory.function b/tests/suites/test_suite_psa_crypto_memory.function index 2bb0f0d7ce..55c00921b2 100644 --- a/tests/suites/test_suite_psa_crypto_memory.function +++ b/tests/suites/test_suite_psa_crypto_memory.function @@ -9,6 +9,7 @@ #include "psa_crypto_invasive.h" #include "test/psa_crypto_helpers.h" +#include "test/memory.h" /* Helper to fill a buffer with a data pattern. The pattern is not * important, it just allows a basic check that the correct thing has @@ -42,6 +43,7 @@ void copy_input(int src_len, int dst_len, psa_status_t exp_status) TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { + MBEDTLS_TEST_MEMORY_UNPOISON(src_buffer, src_len); /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } @@ -68,6 +70,7 @@ void copy_output(int src_len, int dst_len, psa_status_t exp_status) TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { + MBEDTLS_TEST_MEMORY_UNPOISON(dst_buffer, dst_len); /* Note: We compare the first src_len bytes of each buffer, as this is what was copied. */ TEST_MEMORY_COMPARE(src_buffer, src_len, dst_buffer, src_len); } @@ -94,6 +97,7 @@ void local_input_alloc(int input_len, psa_status_t exp_status) TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { + MBEDTLS_TEST_MEMORY_UNPOISON(input, input_len); if (input_len != 0) { TEST_ASSERT(local_input.buffer != input); } @@ -139,6 +143,8 @@ void local_input_round_trip() status = psa_crypto_local_input_alloc(input, sizeof(input), &local_input); TEST_EQUAL(status, PSA_SUCCESS); + + MBEDTLS_TEST_MEMORY_UNPOISON(input, sizeof(input)); TEST_MEMORY_COMPARE(local_input.buffer, local_input.length, input, sizeof(input)); TEST_ASSERT(local_input.buffer != input); @@ -204,6 +210,7 @@ void local_output_free(int output_len, int original_is_null, TEST_EQUAL(status, exp_status); if (exp_status == PSA_SUCCESS) { + MBEDTLS_TEST_MEMORY_UNPOISON(output, output_len); TEST_ASSERT(local_output.buffer == NULL); TEST_EQUAL(local_output.length, 0); TEST_MEMORY_COMPARE(buffer_copy_for_comparison, output_len, @@ -240,6 +247,7 @@ void local_output_round_trip() TEST_ASSERT(local_output.buffer == NULL); TEST_EQUAL(local_output.length, 0); + MBEDTLS_TEST_MEMORY_UNPOISON(output, sizeof(output)); /* Check that the buffer was correctly copied back */ TEST_MEMORY_COMPARE(output, sizeof(output), buffer_copy_for_comparison, sizeof(output)); From 8f35a4f0034a58a747c75dbd44066747c509d9a9 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 27 Nov 2023 17:32:47 +0000 Subject: [PATCH 130/184] Create memory poisoning wrapper for cipher encrypt Use the preprocessor to wrap psa_cipher_encrypt in a wrapper that poisons the input and output buffers. Signed-off-by: David Horstmann --- .../test/psa_memory_poisoning_wrappers.h | 27 +++++++++++++++++++ tests/suites/test_suite_psa_crypto.function | 4 +++ 2 files changed, 31 insertions(+) create mode 100644 tests/include/test/psa_memory_poisoning_wrappers.h diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h new file mode 100644 index 0000000000..08234b4948 --- /dev/null +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -0,0 +1,27 @@ +#include "psa/crypto.h" + +#include "test/memory.h" + +psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + MBEDTLS_TEST_MEMORY_POISON(input, input_length); + MBEDTLS_TEST_MEMORY_POISON(output, output_size); + psa_status_t status = psa_cipher_encrypt(key, + alg, + input, + input_length, + output, + output_size, + output_length); + MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); + MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); + return status; +} + +#define psa_cipher_encrypt(...) wrap_psa_cipher_encrypt(__VA_ARGS__) diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index 1962f7b33e..a024c1ee48 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -25,6 +25,10 @@ #define TEST_DRIVER_LOCATION 0x7fffff #endif +#if defined(MBEDTLS_TEST_HOOKS) +#include "test/psa_memory_poisoning_wrappers.h" +#endif + /* If this comes up, it's a bug in the test code or in the test data. */ #define UNUSED 0xdeadbeef From c6977b489929cf01a01a634fec183a5ff5bab8dc Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 27 Nov 2023 17:43:05 +0000 Subject: [PATCH 131/184] Copy input and output in psa_cipher_encrypt() Signed-off-by: David Horstmann --- library/psa_crypto.c | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index ddc834f95c..36c5f75665 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -4346,6 +4346,20 @@ psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t default_iv_length = 0; psa_key_attributes_t attributes; + psa_crypto_local_input_t local_input = PSA_CRYPTO_LOCAL_INPUT_INIT; + status = psa_crypto_local_input_alloc(input, input_length, &local_input); + if (status != PSA_SUCCESS) { + goto exit; + } + input = local_input.buffer; + + psa_crypto_local_output_t local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; + status = psa_crypto_local_output_alloc(output, output_size, &local_output); + if (status != PSA_SUCCESS) { + goto exit; + } + output = local_output.buffer; + if (!PSA_ALG_IS_CIPHER(alg)) { status = PSA_ERROR_INVALID_ARGUMENT; goto exit; @@ -4401,6 +4415,9 @@ exit: *output_length = 0; } + psa_crypto_local_input_free(&local_input); + psa_crypto_local_output_free(&local_output); + return status; } From e138dce3292685a76a0f232e5dde6d2f2fcb701b Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Tue, 28 Nov 2023 15:21:56 +0000 Subject: [PATCH 132/184] Change to use test-hook-based approach Since we are applying hooks transparently to all tests, we cannot setup and teardown test hooks in the tests. Instead we must do this in the test wrappers which are used to pre-poison and unpoison memory. Signed-off-by: David Horstmann --- library/psa_crypto.c | 27 +++++++++++++------ library/psa_crypto_invasive.h | 8 ++++++ .../test/psa_memory_poisoning_wrappers.h | 24 +++++++++++++++++ 3 files changed, 51 insertions(+), 8 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 36c5f75665..9e6c2efc39 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -72,10 +72,6 @@ #include "mbedtls/sha512.h" #include "md_psa.h" -#if defined(MBEDTLS_TEST_HOOKS) -#include "test/memory.h" -#endif - #if defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXTRACT) || \ defined(MBEDTLS_PSA_BUILTIN_ALG_HKDF_EXPAND) @@ -8451,6 +8447,13 @@ psa_status_t psa_pake_abort( } #endif /* PSA_WANT_ALG_SOME_PAKE */ +/* Memory copying test hooks */ +#if defined(MBEDTLS_TEST_HOOKS) +void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len) = NULL; +void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len) = NULL; +void (*psa_output_pre_copy_hook)(const uint8_t *output, size_t output_len) = NULL; +void (*psa_output_post_copy_hook)(const uint8_t *output, size_t output_len) = NULL; +#endif /** Copy from an input buffer to a local copy. * @@ -8473,7 +8476,9 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, } #if defined(MBEDTLS_TEST_HOOKS) - MBEDTLS_TEST_MEMORY_UNPOISON(input, input_len); + if (psa_input_pre_copy_hook != NULL) { + psa_input_pre_copy_hook(input, input_len); + } #endif if (input_len > 0) { @@ -8481,7 +8486,9 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, } #if defined(MBEDTLS_TEST_HOOKS) - MBEDTLS_TEST_MEMORY_POISON(input, input_len); + if (psa_input_post_copy_hook != NULL) { + psa_input_post_copy_hook(input, input_len); + } #endif return PSA_SUCCESS; @@ -8508,7 +8515,9 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co } #if defined(MBEDTLS_TEST_HOOKS) - MBEDTLS_TEST_MEMORY_UNPOISON(output, output_len); + if (psa_output_pre_copy_hook != NULL) { + psa_output_pre_copy_hook(output, output_len); + } #endif if (output_copy_len > 0) { @@ -8516,7 +8525,9 @@ psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_co } #if defined(MBEDTLS_TEST_HOOKS) - MBEDTLS_TEST_MEMORY_POISON(output, output_len); + if (psa_output_post_copy_hook != NULL) { + psa_output_post_copy_hook(output, output_len); + } #endif return PSA_SUCCESS; diff --git a/library/psa_crypto_invasive.h b/library/psa_crypto_invasive.h index 6a1181f882..51c90c64a4 100644 --- a/library/psa_crypto_invasive.h +++ b/library/psa_crypto_invasive.h @@ -79,6 +79,14 @@ psa_status_t psa_crypto_copy_input(const uint8_t *input, size_t input_len, psa_status_t psa_crypto_copy_output(const uint8_t *output_copy, size_t output_copy_len, uint8_t *output, size_t output_len); +/* + * Test hooks to use for memory unpoisoning/poisoning in copy functions. + */ +extern void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len); +extern void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len); +extern void (*psa_output_pre_copy_hook)(const uint8_t *output, size_t output_len); +extern void (*psa_output_post_copy_hook)(const uint8_t *output, size_t output_len); + #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C */ #endif /* PSA_CRYPTO_INVASIVE_H */ diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index 08234b4948..e1642d2c17 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -2,6 +2,26 @@ #include "test/memory.h" +#include "psa_crypto_invasive.h" + +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + +static void setup_test_hooks(void) +{ + psa_input_pre_copy_hook = mbedtls_test_memory_unpoison; + psa_input_post_copy_hook = mbedtls_test_memory_poison; + psa_output_pre_copy_hook = mbedtls_test_memory_unpoison; + psa_output_post_copy_hook = mbedtls_test_memory_poison; +} + +static void teardown_test_hooks(void) +{ + psa_input_pre_copy_hook = NULL; + psa_input_post_copy_hook = NULL; + psa_output_pre_copy_hook = NULL; + psa_output_post_copy_hook = NULL; +} + psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, psa_algorithm_t alg, const uint8_t *input, @@ -10,6 +30,7 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t output_size, size_t *output_length) { + setup_test_hooks(); MBEDTLS_TEST_MEMORY_POISON(input, input_length); MBEDTLS_TEST_MEMORY_POISON(output, output_size); psa_status_t status = psa_cipher_encrypt(key, @@ -21,7 +42,10 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, output_length); MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); + teardown_test_hooks(); return status; } #define psa_cipher_encrypt(...) wrap_psa_cipher_encrypt(__VA_ARGS__) + +#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ From 410823730b0c76e7417d9f6e31d973c9300778be Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 30 Nov 2023 17:55:15 +0000 Subject: [PATCH 133/184] Remove write check in driver wrappers tests This check is intended to ensure that we do not write intermediate results to the shared output buffer. This check will be made obselete by generic memory-poisoning-based testing for all functions. Signed-off-by: David Horstmann --- .../test_suite_psa_crypto_driver_wrappers.function | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/tests/suites/test_suite_psa_crypto_driver_wrappers.function b/tests/suites/test_suite_psa_crypto_driver_wrappers.function index 1d96f72aca..69dda357ec 100644 --- a/tests/suites/test_suite_psa_crypto_driver_wrappers.function +++ b/tests/suites/test_suite_psa_crypto_driver_wrappers.function @@ -1486,14 +1486,7 @@ void cipher_entry_points(int alg_arg, int key_type_arg, output, output_buffer_size, &function_output_length); TEST_EQUAL(mbedtls_test_driver_cipher_hooks.hits, 1); TEST_EQUAL(status, PSA_ERROR_GENERIC_ERROR); - /* - * Check that the output buffer is still in the same state. - * This will fail if the output buffer is used by the core to pass the IV - * it generated to the driver (and is not restored). - */ - for (size_t i = 0; i < output_buffer_size; i++) { - TEST_EQUAL(output[i], 0xa5); - } + mbedtls_test_driver_cipher_hooks.hits = 0; /* Test setup call, encrypt */ From e9a88ab0d5d6a8f869e931bf3d0c53b66d33c996 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 29 Nov 2023 17:07:13 +0000 Subject: [PATCH 134/184] Use macros to manage buffer copies Signed-off-by: David Horstmann --- library/psa_crypto.c | 58 ++++++++++++++++++++++++++++++++------------ 1 file changed, 43 insertions(+), 15 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 9e6c2efc39..070484cf9d 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -110,6 +110,45 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = if (global_data.initialized == 0) \ return PSA_ERROR_BAD_STATE; +/* Substitute an input buffer for a local copy of itself. + * Assumptions: + * - psa_status_t status exists + * - An exit label is declared + * - The name _copy is not used for the given value of + */ +#define SWAP_FOR_LOCAL_INPUT(input, length) \ + psa_crypto_local_input_t input ## _copy = PSA_CRYPTO_LOCAL_INPUT_INIT; \ + status = psa_crypto_local_input_alloc(input, length, &input ## _copy); \ + if (status != PSA_SUCCESS) { \ + goto exit; \ + } \ + input = input ## _copy.buffer; + +#define FREE_LOCAL_INPUT(input) \ + psa_crypto_local_input_free(&input ## _copy); + +/* Substitute an output buffer for a local copy of itself. + * Assumptions: + * - psa_status_t status exists + * - An exit label is declared + * - The name _copy is not used for the given value of + */ +#define SWAP_FOR_LOCAL_OUTPUT(output, length) \ + psa_crypto_local_output_t output ## _copy = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ + status = psa_crypto_local_output_alloc(output, length, &output ## _copy); \ + if (status != PSA_SUCCESS) { \ + goto exit; \ + } \ + output = output ## _copy.buffer; + +#define FREE_LOCAL_OUTPUT(output) \ + psa_status_t local_output_free_status; \ + local_output_free_status = psa_crypto_local_output_free(&output ## _copy); \ + if (local_output_free_status != PSA_SUCCESS) { \ + status = local_output_free_status; \ + } + + int psa_can_do_hash(psa_algorithm_t hash_alg) { (void) hash_alg; @@ -4342,19 +4381,8 @@ psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t default_iv_length = 0; psa_key_attributes_t attributes; - psa_crypto_local_input_t local_input = PSA_CRYPTO_LOCAL_INPUT_INIT; - status = psa_crypto_local_input_alloc(input, input_length, &local_input); - if (status != PSA_SUCCESS) { - goto exit; - } - input = local_input.buffer; - - psa_crypto_local_output_t local_output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; - status = psa_crypto_local_output_alloc(output, output_size, &local_output); - if (status != PSA_SUCCESS) { - goto exit; - } - output = local_output.buffer; + SWAP_FOR_LOCAL_INPUT(input, input_length); + SWAP_FOR_LOCAL_OUTPUT(output, output_size); if (!PSA_ALG_IS_CIPHER(alg)) { status = PSA_ERROR_INVALID_ARGUMENT; @@ -4411,8 +4439,8 @@ exit: *output_length = 0; } - psa_crypto_local_input_free(&local_input); - psa_crypto_local_output_free(&local_output); + FREE_LOCAL_INPUT(input); + FREE_LOCAL_OUTPUT(output); return status; } From 513101b00f59fb576c5e0d8f68b3c6d71c7214fb Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 29 Nov 2023 17:24:08 +0000 Subject: [PATCH 135/184] Add MBEDTLS_PSA_COPY_CALLER_BUFFERS config option This allows us to entirely remove copying code, where the convenience macros are used for copying. Signed-off-by: David Horstmann --- include/mbedtls/mbedtls_config.h | 13 +++++++++++++ library/psa_crypto.c | 7 +++++++ 2 files changed, 20 insertions(+) diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index 96a3e437d5..9a75c02f14 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -1468,6 +1468,19 @@ */ //#define MBEDTLS_PSA_INJECT_ENTROPY +/** + * \def MBEDTLS_PSA_COPY_CALLER_BUFFERS + * + * Make local copies of buffers supplied by the callers of PSA functions. + * + * This should be enabled whenever caller-supplied buffers are owned by + * an untrusted party, for example where arguments to PSA calls are passed + * across a trust boundary. + * + * Note: Enabling this option increases memory usage and code size. + */ +#define MBEDTLS_PSA_COPY_CALLER_BUFFERS + /** * \def MBEDTLS_RSA_NO_CRT * diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 070484cf9d..667d4f4070 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -110,6 +110,7 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = if (global_data.initialized == 0) \ return PSA_ERROR_BAD_STATE; +#if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) /* Substitute an input buffer for a local copy of itself. * Assumptions: * - psa_status_t status exists @@ -147,6 +148,12 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = if (local_output_free_status != PSA_SUCCESS) { \ status = local_output_free_status; \ } +#else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ +#define SWAP_FOR_LOCAL_INPUT(input, length) +#define FREE_LOCAL_INPUT(input) +#define SWAP_FOR_LOCAL_OUTPUT(output, length) +#define FREE_LOCAL_OUTPUT(output) +#endif /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ int psa_can_do_hash(psa_algorithm_t hash_alg) From d596862418d67634c295a5657d4a70dee8bc4240 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 7 Dec 2023 14:09:32 +0000 Subject: [PATCH 136/184] Remove unnecessary include directory from CMake Since psa_crypto.c does not include tests/include/test/memory.h, we do not need the tests/include include path. Signed-off-by: David Horstmann --- library/CMakeLists.txt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/library/CMakeLists.txt b/library/CMakeLists.txt index 52178e474a..5c297e0a1f 100644 --- a/library/CMakeLists.txt +++ b/library/CMakeLists.txt @@ -329,8 +329,7 @@ foreach(target IN LISTS target_libraries) $ PRIVATE ${MBEDTLS_DIR}/library/ # Needed to include psa_crypto_driver_wrappers.h - ${CMAKE_CURRENT_BINARY_DIR} - ${MBEDTLS_DIR}/tests/include/) + ${CMAKE_CURRENT_BINARY_DIR}) # Pass-through MBEDTLS_CONFIG_FILE and MBEDTLS_USER_CONFIG_FILE if(MBEDTLS_CONFIG_FILE) target_compile_definitions(${target} From bbd44a767f7358942182b80a4cad50656a19dce7 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 7 Dec 2023 18:34:49 +0000 Subject: [PATCH 137/184] Add missing license header Signed-off-by: David Horstmann --- tests/include/test/psa_memory_poisoning_wrappers.h | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index e1642d2c17..3422339fd5 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -1,3 +1,8 @@ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + #include "psa/crypto.h" #include "test/memory.h" From 00d7a04b829a5acc44a61356fa66bf89bfeee94b Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 7 Dec 2023 18:39:17 +0000 Subject: [PATCH 138/184] Add more information to comment on test hooks Signed-off-by: David Horstmann --- library/psa_crypto.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 667d4f4070..b9583f781b 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -8482,7 +8482,10 @@ psa_status_t psa_pake_abort( } #endif /* PSA_WANT_ALG_SOME_PAKE */ -/* Memory copying test hooks */ +/* Memory copying test hooks. These are called before input copy, after input + * copy, before output copy and after output copy, respectively. + * They are used by memory-poisoning tests to temporarily unpoison buffers + * while they are copied. */ #if defined(MBEDTLS_TEST_HOOKS) void (*psa_input_pre_copy_hook)(const uint8_t *input, size_t input_len) = NULL; void (*psa_input_post_copy_hook)(const uint8_t *input, size_t input_len) = NULL; From b7a5b6ed35d9a6455e10be02a70c85d881661461 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 8 Dec 2023 12:09:04 +0000 Subject: [PATCH 139/184] Add comment explaining the purpose of header Explain why we have the wrappers in psa_memory_poisoning_wrappers.h Signed-off-by: David Horstmann --- tests/include/test/psa_memory_poisoning_wrappers.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index 3422339fd5..5192597a35 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -1,3 +1,9 @@ +/** Memory poisoning wrappers for PSA functions. + * + * These wrappers poison the input and output buffers of each function + * before calling it, to ensure that it does not access the buffers + * except by calling the approved buffer-copying functions. + */ /* * Copyright The Mbed TLS Contributors * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later From 3e72db4f514769b9c73f409a9d06e54a39a8fb72 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 8 Dec 2023 14:08:18 +0000 Subject: [PATCH 140/184] Improve FREE_LOCAL_INPUT() and FREE_LOCAL_OUTPUT() * Set swapped pointers to NULL when the buffers are freed. * Change example name to and to reduce confusion. * Document assumptions of FREE_LOCAL_ macros. * Add comment on error case in FREE_LOCAL_OUTPUT(), explaining why it's okay to mask the existing status code. Signed-off-by: David Horstmann --- library/psa_crypto.c | 27 +++++++++++++++++++++++++-- 1 file changed, 25 insertions(+), 2 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index b9583f781b..575c08cab9 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -115,7 +115,7 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * Assumptions: * - psa_status_t status exists * - An exit label is declared - * - The name _copy is not used for the given value of + * - The name _copy is not used for the given value of */ #define SWAP_FOR_LOCAL_INPUT(input, length) \ psa_crypto_local_input_t input ## _copy = PSA_CRYPTO_LOCAL_INPUT_INIT; \ @@ -125,14 +125,23 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = } \ input = input ## _copy.buffer; +/* Free the substituted input buffer copy created by SWAP_FOR_LOCAL_INPUT(). + * Note that this does not restore the pointer to the original buffer. + * Assumptions: + * - psa_crypto_local_input_t _copy exists, for the given value of + * + * - _copy was previously allocated by psa_crypto_local_input_alloc() + * - points to _copy.buffer + */ #define FREE_LOCAL_INPUT(input) \ + input = NULL; \ psa_crypto_local_input_free(&input ## _copy); /* Substitute an output buffer for a local copy of itself. * Assumptions: * - psa_status_t status exists * - An exit label is declared - * - The name _copy is not used for the given value of + * - The name _copy is not used for the given value of */ #define SWAP_FOR_LOCAL_OUTPUT(output, length) \ psa_crypto_local_output_t output ## _copy = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ @@ -142,10 +151,24 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = } \ output = output ## _copy.buffer; +/* Free the substituted output buffer copy created by SWAP_FOR_LOCAL_OUTPUT() + * after first copying back its contents to the original buffer. + * Note that this does not restore the pointer to the original buffer. + * Assumptions: + * - psa_crypto_local_output_t _copy exists, for the given value of + * + * - _copy was previously allocated by psa_crypto_local_output_alloc() + * - points to _copy.buffer + * - psa_status_t status exists + */ #define FREE_LOCAL_OUTPUT(output) \ + output = NULL; \ psa_status_t local_output_free_status; \ local_output_free_status = psa_crypto_local_output_free(&output ## _copy); \ if (local_output_free_status != PSA_SUCCESS) { \ + /* Since this error case is an internal error, it's more serious than \ + * any existing error code and so it's fine to overwrite the existing \ + * status. */ \ status = local_output_free_status; \ } #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ From bf4ec79085770abb29bc765e6ac1140094e3c911 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 11 Dec 2023 15:45:20 +0000 Subject: [PATCH 141/184] Make return statuses unique in FREE_LOCAL_OUTPUT() Previously the return from psa_crypto_local_output_free() had a fixed name, which meant that multiple outputs would cause redefinitions of the same variable. Signed-off-by: David Horstmann --- library/psa_crypto.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 575c08cab9..2847d70dee 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -160,16 +160,18 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * - _copy was previously allocated by psa_crypto_local_output_alloc() * - points to _copy.buffer * - psa_status_t status exists + * - The name _local_output_status is not used for the given value of + * */ #define FREE_LOCAL_OUTPUT(output) \ output = NULL; \ - psa_status_t local_output_free_status; \ - local_output_free_status = psa_crypto_local_output_free(&output ## _copy); \ - if (local_output_free_status != PSA_SUCCESS) { \ + psa_status_t output ## _local_output_status; \ + output ## _local_output_status = psa_crypto_local_output_free(&output ## _copy); \ + if (output ## _local_output_status != PSA_SUCCESS) { \ /* Since this error case is an internal error, it's more serious than \ * any existing error code and so it's fine to overwrite the existing \ * status. */ \ - status = local_output_free_status; \ + status = output ## _local_output_status; \ } #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ #define SWAP_FOR_LOCAL_INPUT(input, length) From d57c0731c9aacbe63b1a28d6413b232db867ae2d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 13 Dec 2023 14:03:40 +0000 Subject: [PATCH 142/184] Remove spaces around token-pasting macro operator Signed-off-by: David Horstmann --- library/psa_crypto.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 2847d70dee..c84bbd2371 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -118,12 +118,12 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * - The name _copy is not used for the given value of */ #define SWAP_FOR_LOCAL_INPUT(input, length) \ - psa_crypto_local_input_t input ## _copy = PSA_CRYPTO_LOCAL_INPUT_INIT; \ - status = psa_crypto_local_input_alloc(input, length, &input ## _copy); \ + psa_crypto_local_input_t input##_copy = PSA_CRYPTO_LOCAL_INPUT_INIT; \ + status = psa_crypto_local_input_alloc(input, length, &input##_copy); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - input = input ## _copy.buffer; + input = input##_copy.buffer; /* Free the substituted input buffer copy created by SWAP_FOR_LOCAL_INPUT(). * Note that this does not restore the pointer to the original buffer. @@ -135,7 +135,7 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = */ #define FREE_LOCAL_INPUT(input) \ input = NULL; \ - psa_crypto_local_input_free(&input ## _copy); + psa_crypto_local_input_free(&input##_copy); /* Substitute an output buffer for a local copy of itself. * Assumptions: @@ -144,12 +144,12 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * - The name _copy is not used for the given value of */ #define SWAP_FOR_LOCAL_OUTPUT(output, length) \ - psa_crypto_local_output_t output ## _copy = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ - status = psa_crypto_local_output_alloc(output, length, &output ## _copy); \ + psa_crypto_local_output_t output##_copy = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ + status = psa_crypto_local_output_alloc(output, length, &output##_copy); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - output = output ## _copy.buffer; + output = output##_copy.buffer; /* Free the substituted output buffer copy created by SWAP_FOR_LOCAL_OUTPUT() * after first copying back its contents to the original buffer. @@ -165,13 +165,13 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = */ #define FREE_LOCAL_OUTPUT(output) \ output = NULL; \ - psa_status_t output ## _local_output_status; \ - output ## _local_output_status = psa_crypto_local_output_free(&output ## _copy); \ - if (output ## _local_output_status != PSA_SUCCESS) { \ + psa_status_t output##_local_output_status; \ + output##_local_output_status = psa_crypto_local_output_free(&output##_copy); \ + if (output##_local_output_status != PSA_SUCCESS) { \ /* Since this error case is an internal error, it's more serious than \ * any existing error code and so it's fine to overwrite the existing \ * status. */ \ - status = output ## _local_output_status; \ + status = output##_local_output_status; \ } #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ #define SWAP_FOR_LOCAL_INPUT(input, length) From 5a945f584eacf474d4f7fde73191fddaec83d48c Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 13 Dec 2023 14:09:08 +0000 Subject: [PATCH 143/184] Put local output status in scope This means that a unique name is no longer needed. Signed-off-by: David Horstmann --- library/psa_crypto.c | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index c84bbd2371..7fc0d9677f 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -165,14 +165,16 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = */ #define FREE_LOCAL_OUTPUT(output) \ output = NULL; \ - psa_status_t output##_local_output_status; \ - output##_local_output_status = psa_crypto_local_output_free(&output##_copy); \ - if (output##_local_output_status != PSA_SUCCESS) { \ - /* Since this error case is an internal error, it's more serious than \ - * any existing error code and so it's fine to overwrite the existing \ - * status. */ \ - status = output##_local_output_status; \ - } + do { \ + psa_status_t local_output_status; \ + local_output_status = psa_crypto_local_output_free(&output##_copy); \ + if (local_output_status != PSA_SUCCESS) { \ + /* Since this error case is an internal error, it's more serious than \ + * any existing error code and so it's fine to overwrite the existing \ + * status. */ \ + status = local_output_status; \ + } \ + } while (0) #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ #define SWAP_FOR_LOCAL_INPUT(input, length) #define FREE_LOCAL_INPUT(input) From 36df4b24d46a218ec8f7302e76368d9223af11ff Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 13 Dec 2023 15:55:25 +0000 Subject: [PATCH 144/184] Redesign local copy handling macros * Separate initialization from allocation. * Rewrite description of macros to fit the new interface. * Use a longer name to store the local copy objects, to reduce the risk of shadowing. * Use different names for the original and the copy. Append the suffix '_external' to the original argument and use the previous name for the copy. Signed-off-by: David Horstmann --- library/psa_crypto.c | 120 ++++++++++++++++++++++++++++--------------- 1 file changed, 78 insertions(+), 42 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index 7fc0d9677f..bcda6d689d 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -111,63 +111,90 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = return PSA_ERROR_BAD_STATE; #if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) -/* Substitute an input buffer for a local copy of itself. + +/* Declare a local copy of an input buffer. + * + * Note: This macro must be called before any operations which may jump to + * the exit label, so that the local input copy object is safe to be freed. + * + * Assumptions: + * - input is the name of a pointer to the buffer to be copied + * - The name LOCAL_INPUT_COPY_OF_input is unused in the current scope + */ +#define LOCAL_INPUT_DECLARE(input) \ + psa_crypto_local_input_t LOCAL_INPUT_COPY_OF_##input = PSA_CRYPTO_LOCAL_INPUT_INIT; + +/* Allocate a copy of the buffer input and create a pointer with the name + * input_copy_name that points to the start of the copy. + * * Assumptions: * - psa_status_t status exists * - An exit label is declared - * - The name _copy is not used for the given value of + * - input is the name of a pointer to the buffer to be copied + * - LOCAL_INPUT_DECLARE(input) has previously been called + * - input_copy_name is a name that is not used in the current scope */ -#define SWAP_FOR_LOCAL_INPUT(input, length) \ - psa_crypto_local_input_t input##_copy = PSA_CRYPTO_LOCAL_INPUT_INIT; \ - status = psa_crypto_local_input_alloc(input, length, &input##_copy); \ +#define LOCAL_INPUT_ALLOC(input, length, input_copy_name) \ + status = psa_crypto_local_input_alloc(input, length, \ + &LOCAL_INPUT_COPY_OF_##input); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - input = input##_copy.buffer; + const uint8_t *input_copy_name = LOCAL_INPUT_COPY_OF_##input.buffer; -/* Free the substituted input buffer copy created by SWAP_FOR_LOCAL_INPUT(). - * Note that this does not restore the pointer to the original buffer. +/* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC() + * * Assumptions: - * - psa_crypto_local_input_t _copy exists, for the given value of - * - * - _copy was previously allocated by psa_crypto_local_input_alloc() - * - points to _copy.buffer + * - input_copy_name is the name of the input copy created by LOCAL_INPUT_ALLOC() + * - input is the name of the original buffer that was copied */ -#define FREE_LOCAL_INPUT(input) \ - input = NULL; \ - psa_crypto_local_input_free(&input##_copy); +#define LOCAL_INPUT_FREE(input_copy_name, input) \ + input_copy_name = NULL; \ + psa_crypto_local_input_free(&LOCAL_INPUT_COPY_OF_##input); -/* Substitute an output buffer for a local copy of itself. +/* Declare a local copy of an output buffer. + * + * Note: This macro must be called before any operations which may jump to + * the exit label, so that the local output copy object is safe to be freed. + * + * Assumptions: + * - output is the name of a pointer to the buffer to be copied + * - The name LOCAL_OUTPUT_COPY_OF_output is unused in the current scope + */ +#define LOCAL_OUTPUT_DECLARE(output) \ + psa_crypto_local_output_t LOCAL_OUTPUT_COPY_OF_##output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; + +/* Allocate a copy of the buffer output and create a pointer with the name + * output_copy_name that points to the start of the copy. + * * Assumptions: * - psa_status_t status exists * - An exit label is declared - * - The name _copy is not used for the given value of + * - output is the name of a pointer to the buffer to be copied + * - LOCAL_OUTPUT_DECLARE(output) has previously been called + * - output_copy_name is a name that is not used in the current scope */ -#define SWAP_FOR_LOCAL_OUTPUT(output, length) \ - psa_crypto_local_output_t output##_copy = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ - status = psa_crypto_local_output_alloc(output, length, &output##_copy); \ +#define LOCAL_OUTPUT_ALLOC(output, length, output_copy_name) \ + status = psa_crypto_local_output_alloc(output, length, \ + &LOCAL_OUTPUT_COPY_OF_##output); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - output = output##_copy.buffer; + uint8_t *output_copy_name = LOCAL_OUTPUT_COPY_OF_##output.buffer; -/* Free the substituted output buffer copy created by SWAP_FOR_LOCAL_OUTPUT() +/* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC() * after first copying back its contents to the original buffer. - * Note that this does not restore the pointer to the original buffer. + * * Assumptions: - * - psa_crypto_local_output_t _copy exists, for the given value of - * - * - _copy was previously allocated by psa_crypto_local_output_alloc() - * - points to _copy.buffer * - psa_status_t status exists - * - The name _local_output_status is not used for the given value of - * + * - input_copy_name is the name of the input copy created by LOCAL_INPUT_ALLOC() + * - input is the name of the original buffer that was copied */ -#define FREE_LOCAL_OUTPUT(output) \ - output = NULL; \ +#define LOCAL_OUTPUT_FREE(output_copy_name, output) \ + output_copy_name = NULL; \ do { \ psa_status_t local_output_status; \ - local_output_status = psa_crypto_local_output_free(&output##_copy); \ + local_output_status = psa_crypto_local_output_free(&LOCAL_OUTPUT_COPY_OF_##output); \ if (local_output_status != PSA_SUCCESS) { \ /* Since this error case is an internal error, it's more serious than \ * any existing error code and so it's fine to overwrite the existing \ @@ -176,10 +203,17 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = } \ } while (0) #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ -#define SWAP_FOR_LOCAL_INPUT(input, length) -#define FREE_LOCAL_INPUT(input) -#define SWAP_FOR_LOCAL_OUTPUT(output, length) -#define FREE_LOCAL_OUTPUT(output) +#define LOCAL_INPUT_DECLARE(input) +#define LOCAL_INPUT_ALLOC(input, length, input_copy_name) \ + const uint8_t *input_copy_name = input; +#define LOCAL_INPUT_FREE(input_copy_name, input) \ + input_copy_name = NULL; +#define LOCAL_OUTPUT_DECLARE(output) +#define LOCAL_OUTPUT_ALLOC(output, length, output_copy_name) \ + uint8_t *output_copy_name = output; +#define LOCAL_OUTPUT_FREE(output_copy_name, output) \ + output_copy_name = NULL; + #endif /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ @@ -4402,9 +4436,9 @@ psa_status_t psa_cipher_abort(psa_cipher_operation_t *operation) psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, psa_algorithm_t alg, - const uint8_t *input, + const uint8_t *input_external, size_t input_length, - uint8_t *output, + uint8_t *output_external, size_t output_size, size_t *output_length) { @@ -4415,8 +4449,10 @@ psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t default_iv_length = 0; psa_key_attributes_t attributes; - SWAP_FOR_LOCAL_INPUT(input, input_length); - SWAP_FOR_LOCAL_OUTPUT(output, output_size); + LOCAL_INPUT_DECLARE(input_external); + LOCAL_INPUT_ALLOC(input_external, input_length, input); + LOCAL_OUTPUT_DECLARE(output_external); + LOCAL_OUTPUT_ALLOC(output_external, output_size, output); if (!PSA_ALG_IS_CIPHER(alg)) { status = PSA_ERROR_INVALID_ARGUMENT; @@ -4473,8 +4509,8 @@ exit: *output_length = 0; } - FREE_LOCAL_INPUT(input); - FREE_LOCAL_OUTPUT(output); + LOCAL_INPUT_FREE(input, input_external); + LOCAL_OUTPUT_FREE(output, output_external); return status; } From a7cde5d2960c0205a4560a147801b59454064af8 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 13 Dec 2023 17:07:23 +0000 Subject: [PATCH 145/184] Move test hook setup functions into a C file Signed-off-by: David Horstmann --- .../test/psa_memory_poisoning_wrappers.h | 55 +++++++------------ tests/src/psa_memory_poisoning_wrappers.c | 53 ++++++++++++++++++ 2 files changed, 73 insertions(+), 35 deletions(-) create mode 100644 tests/src/psa_memory_poisoning_wrappers.c diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index 5192597a35..8b2dcfa04e 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -9,29 +9,28 @@ * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later */ +#ifndef PSA_MEMORY_POISONING_WRAPPERS_H +#define PSA_MEMORY_POISONING_WRAPPERS_H + #include "psa/crypto.h" #include "test/memory.h" -#include "psa_crypto_invasive.h" +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) -#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) +/** + * \brief Setup the memory poisoning test hooks used by + * psa_crypto_copy_input() and psa_crypto_copy_output() for + * memory poisoning. + */ +void mbedtls_poison_test_hooks_setup(void); -static void setup_test_hooks(void) -{ - psa_input_pre_copy_hook = mbedtls_test_memory_unpoison; - psa_input_post_copy_hook = mbedtls_test_memory_poison; - psa_output_pre_copy_hook = mbedtls_test_memory_unpoison; - psa_output_post_copy_hook = mbedtls_test_memory_poison; -} - -static void teardown_test_hooks(void) -{ - psa_input_pre_copy_hook = NULL; - psa_input_post_copy_hook = NULL; - psa_output_pre_copy_hook = NULL; - psa_output_post_copy_hook = NULL; -} +/** + * \brief Teardown the memory poisoning test hooks used by + * psa_crypto_copy_input() and psa_crypto_copy_output() for + * memory poisoning. + */ +void mbedtls_poison_test_hooks_teardown(void); psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, psa_algorithm_t alg, @@ -39,24 +38,10 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t input_length, uint8_t *output, size_t output_size, - size_t *output_length) -{ - setup_test_hooks(); - MBEDTLS_TEST_MEMORY_POISON(input, input_length); - MBEDTLS_TEST_MEMORY_POISON(output, output_size); - psa_status_t status = psa_cipher_encrypt(key, - alg, - input, - input_length, - output, - output_size, - output_length); - MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); - MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); - teardown_test_hooks(); - return status; -} + size_t *output_length); #define psa_cipher_encrypt(...) wrap_psa_cipher_encrypt(__VA_ARGS__) -#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ +#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_TEST_MEMORY_CAN_POISON */ + +#endif /* PSA_MEMORY_POISONING_WRAPPERS_H */ \ No newline at end of file diff --git a/tests/src/psa_memory_poisoning_wrappers.c b/tests/src/psa_memory_poisoning_wrappers.c new file mode 100644 index 0000000000..7af84dd765 --- /dev/null +++ b/tests/src/psa_memory_poisoning_wrappers.c @@ -0,0 +1,53 @@ +/** Helper functions for memory poisoning in tests. + */ +/* + * Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ +#include "test/memory.h" + +#include "psa_crypto_invasive.h" + +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + +void mbedtls_poison_test_hooks_setup(void) +{ + psa_input_pre_copy_hook = mbedtls_test_memory_unpoison; + psa_input_post_copy_hook = mbedtls_test_memory_poison; + psa_output_pre_copy_hook = mbedtls_test_memory_unpoison; + psa_output_post_copy_hook = mbedtls_test_memory_poison; +} + +void mbedtls_poison_test_hooks_teardown(void) +{ + psa_input_pre_copy_hook = NULL; + psa_input_post_copy_hook = NULL; + psa_output_pre_copy_hook = NULL; + psa_output_post_copy_hook = NULL; +} + +psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, + psa_algorithm_t alg, + const uint8_t *input, + size_t input_length, + uint8_t *output, + size_t output_size, + size_t *output_length) +{ + mbedtls_poison_test_hooks_setup(); + MBEDTLS_TEST_MEMORY_POISON(input, input_length); + MBEDTLS_TEST_MEMORY_POISON(output, output_size); + psa_status_t status = psa_cipher_encrypt(key, + alg, + input, + input_length, + output, + output_size, + output_length); + MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); + MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); + mbedtls_poison_test_hooks_teardown(); + return status; +} + +#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_TEST_MEMORY_CAN_POISON */ From b489257a0b566b1962333d216d92dbaeaa4bcd3a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 14 Dec 2023 14:17:04 +0000 Subject: [PATCH 146/184] Move test hook setup and teardown to helpers.c Setup and teardown test hooks during full test platform setup. Signed-off-by: David Horstmann --- tests/src/helpers.c | 12 ++++++++++++ tests/src/psa_memory_poisoning_wrappers.c | 2 -- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/tests/src/helpers.c b/tests/src/helpers.c index eb28919b8d..53a17abda2 100644 --- a/tests/src/helpers.c +++ b/tests/src/helpers.c @@ -13,6 +13,10 @@ #include #endif +#if defined(MBEDTLS_TEST_HOOKS) +#include +#endif + /*----------------------------------------------------------------------------*/ /* Static global variables */ @@ -29,6 +33,10 @@ int mbedtls_test_platform_setup(void) { int ret = 0; +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + mbedtls_poison_test_hooks_setup(); +#endif + #if defined(MBEDTLS_PSA_INJECT_ENTROPY) /* Make sure that injected entropy is present. Otherwise * psa_crypto_init() will fail. This is not necessary for test suites @@ -49,6 +57,10 @@ int mbedtls_test_platform_setup(void) void mbedtls_test_platform_teardown(void) { +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + mbedtls_poison_test_hooks_teardown(); +#endif + #if defined(MBEDTLS_PLATFORM_C) mbedtls_platform_teardown(&platform_ctx); #endif /* MBEDTLS_PLATFORM_C */ diff --git a/tests/src/psa_memory_poisoning_wrappers.c b/tests/src/psa_memory_poisoning_wrappers.c index 7af84dd765..c865d1f908 100644 --- a/tests/src/psa_memory_poisoning_wrappers.c +++ b/tests/src/psa_memory_poisoning_wrappers.c @@ -34,7 +34,6 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t output_size, size_t *output_length) { - mbedtls_poison_test_hooks_setup(); MBEDTLS_TEST_MEMORY_POISON(input, input_length); MBEDTLS_TEST_MEMORY_POISON(output, output_size); psa_status_t status = psa_cipher_encrypt(key, @@ -46,7 +45,6 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, output_length); MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); - mbedtls_poison_test_hooks_teardown(); return status; } From 7de0928fd10c99edeb315a326967fb60c2bb52e3 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 14 Dec 2023 14:27:50 +0000 Subject: [PATCH 147/184] Move wrapper include to psa_crypto_helpers.h This makes memory poisoning wrappers available to (almost) all tests. Signed-off-by: David Horstmann --- tests/include/test/psa_crypto_helpers.h | 3 +++ tests/suites/test_suite_psa_crypto.function | 4 ---- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/include/test/psa_crypto_helpers.h b/tests/include/test/psa_crypto_helpers.h index 04b90b9231..935e071819 100644 --- a/tests/include/test/psa_crypto_helpers.h +++ b/tests/include/test/psa_crypto_helpers.h @@ -16,6 +16,9 @@ #include #endif +#if defined(MBEDTLS_TEST_HOOKS) +#include "test/psa_memory_poisoning_wrappers.h" +#endif #if defined(MBEDTLS_PSA_CRYPTO_C) /** Initialize the PSA Crypto subsystem. */ diff --git a/tests/suites/test_suite_psa_crypto.function b/tests/suites/test_suite_psa_crypto.function index a024c1ee48..1962f7b33e 100644 --- a/tests/suites/test_suite_psa_crypto.function +++ b/tests/suites/test_suite_psa_crypto.function @@ -25,10 +25,6 @@ #define TEST_DRIVER_LOCATION 0x7fffff #endif -#if defined(MBEDTLS_TEST_HOOKS) -#include "test/psa_memory_poisoning_wrappers.h" -#endif - /* If this comes up, it's a bug in the test code or in the test data. */ #define UNUSED 0xdeadbeef From 0d405d8bb93d87101d6a928027037621bbefc55b Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 14 Dec 2023 16:14:41 +0000 Subject: [PATCH 148/184] Add note about support for buffer overlap Note that enabling MBEDTLS_PSA_COPY_CALLER_BUFFERS allows full buffer overlap support, whereas without it, overlap support is reduced to that documented in the function descriptions. Signed-off-by: David Horstmann --- include/mbedtls/mbedtls_config.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index 9a75c02f14..f3f3d2bd85 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -1477,7 +1477,10 @@ * an untrusted party, for example where arguments to PSA calls are passed * across a trust boundary. * - * Note: Enabling this option increases memory usage and code size. + * \note Enabling this option increases memory usage and code size. + * + * \note Enabling this option enables full support for overlap of input and + * output buffers passed to PSA functions. */ #define MBEDTLS_PSA_COPY_CALLER_BUFFERS From 853f9f97eba5c51c012b2f8bf2cdcf6335365034 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 14 Dec 2023 17:17:20 +0000 Subject: [PATCH 149/184] Add missing newline at end of file Signed-off-by: David Horstmann --- include/mbedtls/mbedtls_config.h | 2 +- tests/include/test/psa_memory_poisoning_wrappers.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index f3f3d2bd85..4b29ea2793 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -2070,7 +2070,7 @@ * * Uncomment to enable invasive tests. */ -//#define MBEDTLS_TEST_HOOKS +#define MBEDTLS_TEST_HOOKS /** * \def MBEDTLS_THREADING_ALT diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index 8b2dcfa04e..0052c2fae0 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -44,4 +44,4 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_TEST_MEMORY_CAN_POISON */ -#endif /* PSA_MEMORY_POISONING_WRAPPERS_H */ \ No newline at end of file +#endif /* PSA_MEMORY_POISONING_WRAPPERS_H */ From 62a56d966d92253120211039a2ec5f69d56656b1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 14 Dec 2023 18:12:47 +0000 Subject: [PATCH 150/184] Tweak the behaviour of copy handling macros Specifically: * Move the creation of the pointer to the copied buffer into the DECLARE() macro, to solve warnings about potentially skipping initialization. * Reorder the arguments of the FREE() macro - having a different order made it confusing, so keep the order the same throughout. Signed-off-by: David Horstmann --- library/psa_crypto.c | 88 +++++++++++++++++++++++--------------------- 1 file changed, 47 insertions(+), 41 deletions(-) diff --git a/library/psa_crypto.c b/library/psa_crypto.c index bcda6d689d..8c9f9de4de 100644 --- a/library/psa_crypto.c +++ b/library/psa_crypto.c @@ -112,7 +112,8 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = #if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) -/* Declare a local copy of an input buffer. +/* Declare a local copy of an input buffer and a variable that will be used + * to store a pointer to the start of the buffer. * * Note: This macro must be called before any operations which may jump to * the exit label, so that the local input copy object is safe to be freed. @@ -120,39 +121,41 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * Assumptions: * - input is the name of a pointer to the buffer to be copied * - The name LOCAL_INPUT_COPY_OF_input is unused in the current scope + * - input_copy_name is a name that is unused in the current scope */ -#define LOCAL_INPUT_DECLARE(input) \ - psa_crypto_local_input_t LOCAL_INPUT_COPY_OF_##input = PSA_CRYPTO_LOCAL_INPUT_INIT; +#define LOCAL_INPUT_DECLARE(input, input_copy_name) \ + psa_crypto_local_input_t LOCAL_INPUT_COPY_OF_##input = PSA_CRYPTO_LOCAL_INPUT_INIT; \ + const uint8_t *input_copy_name = NULL; -/* Allocate a copy of the buffer input and create a pointer with the name - * input_copy_name that points to the start of the copy. +/* Allocate a copy of the buffer input and set the pointer input_copy to + * point to the start of the copy. * * Assumptions: * - psa_status_t status exists * - An exit label is declared * - input is the name of a pointer to the buffer to be copied - * - LOCAL_INPUT_DECLARE(input) has previously been called - * - input_copy_name is a name that is not used in the current scope + * - LOCAL_INPUT_DECLARE(input, input_copy) has previously been called */ -#define LOCAL_INPUT_ALLOC(input, length, input_copy_name) \ +#define LOCAL_INPUT_ALLOC(input, length, input_copy) \ status = psa_crypto_local_input_alloc(input, length, \ &LOCAL_INPUT_COPY_OF_##input); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - const uint8_t *input_copy_name = LOCAL_INPUT_COPY_OF_##input.buffer; + input_copy = LOCAL_INPUT_COPY_OF_##input.buffer; /* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC() * * Assumptions: - * - input_copy_name is the name of the input copy created by LOCAL_INPUT_ALLOC() + * - input_copy is the name of the input copy pointer set by LOCAL_INPUT_ALLOC() * - input is the name of the original buffer that was copied */ -#define LOCAL_INPUT_FREE(input_copy_name, input) \ - input_copy_name = NULL; \ +#define LOCAL_INPUT_FREE(input, input_copy) \ + input_copy = NULL; \ psa_crypto_local_input_free(&LOCAL_INPUT_COPY_OF_##input); -/* Declare a local copy of an output buffer. +/* Declare a local copy of an output buffer and a variable that will be used + * to store a pointer to the start of the buffer. * * Note: This macro must be called before any operations which may jump to * the exit label, so that the local output copy object is safe to be freed. @@ -160,38 +163,39 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = * Assumptions: * - output is the name of a pointer to the buffer to be copied * - The name LOCAL_OUTPUT_COPY_OF_output is unused in the current scope + * - output_copy_name is a name that is unused in the current scope */ -#define LOCAL_OUTPUT_DECLARE(output) \ - psa_crypto_local_output_t LOCAL_OUTPUT_COPY_OF_##output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; +#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \ + psa_crypto_local_output_t LOCAL_OUTPUT_COPY_OF_##output = PSA_CRYPTO_LOCAL_OUTPUT_INIT; \ + uint8_t *output_copy_name = NULL; -/* Allocate a copy of the buffer output and create a pointer with the name - * output_copy_name that points to the start of the copy. +/* Allocate a copy of the buffer output and set the pointer output_copy to + * point to the start of the copy. * * Assumptions: * - psa_status_t status exists * - An exit label is declared * - output is the name of a pointer to the buffer to be copied - * - LOCAL_OUTPUT_DECLARE(output) has previously been called - * - output_copy_name is a name that is not used in the current scope + * - LOCAL_OUTPUT_DECLARE(output, output_copy) has previously been called */ -#define LOCAL_OUTPUT_ALLOC(output, length, output_copy_name) \ +#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \ status = psa_crypto_local_output_alloc(output, length, \ &LOCAL_OUTPUT_COPY_OF_##output); \ if (status != PSA_SUCCESS) { \ goto exit; \ } \ - uint8_t *output_copy_name = LOCAL_OUTPUT_COPY_OF_##output.buffer; + output_copy = LOCAL_OUTPUT_COPY_OF_##output.buffer; -/* Free the local input copy allocated previously by LOCAL_INPUT_ALLOC() +/* Free the local output copy allocated previously by LOCAL_OUTPUT_ALLOC() * after first copying back its contents to the original buffer. * * Assumptions: * - psa_status_t status exists - * - input_copy_name is the name of the input copy created by LOCAL_INPUT_ALLOC() - * - input is the name of the original buffer that was copied + * - output_copy is the name of the output copy pointer set by LOCAL_OUTPUT_ALLOC() + * - output is the name of the original buffer that was copied */ -#define LOCAL_OUTPUT_FREE(output_copy_name, output) \ - output_copy_name = NULL; \ +#define LOCAL_OUTPUT_FREE(output, output_copy) \ + output_copy = NULL; \ do { \ psa_status_t local_output_status; \ local_output_status = psa_crypto_local_output_free(&LOCAL_OUTPUT_COPY_OF_##output); \ @@ -203,17 +207,18 @@ mbedtls_psa_drbg_context_t *const mbedtls_psa_random_state = } \ } while (0) #else /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ -#define LOCAL_INPUT_DECLARE(input) -#define LOCAL_INPUT_ALLOC(input, length, input_copy_name) \ - const uint8_t *input_copy_name = input; -#define LOCAL_INPUT_FREE(input_copy_name, input) \ - input_copy_name = NULL; -#define LOCAL_OUTPUT_DECLARE(output) -#define LOCAL_OUTPUT_ALLOC(output, length, output_copy_name) \ - uint8_t *output_copy_name = output; -#define LOCAL_OUTPUT_FREE(output_copy_name, output) \ - output_copy_name = NULL; - +#define LOCAL_INPUT_DECLARE(input, input_copy_name) \ + const uint8_t *input_copy_name = NULL; +#define LOCAL_INPUT_ALLOC(input, length, input_copy) \ + input_copy = input; +#define LOCAL_INPUT_FREE(input, input_copy) \ + input_copy = NULL; +#define LOCAL_OUTPUT_DECLARE(output, output_copy_name) \ + uint8_t *output_copy_name = NULL; +#define LOCAL_OUTPUT_ALLOC(output, length, output_copy) \ + output_copy = output; +#define LOCAL_OUTPUT_FREE(output, output_copy) \ + output_copy = NULL; #endif /* MBEDTLS_PSA_COPY_CALLER_BUFFERS */ @@ -4449,9 +4454,10 @@ psa_status_t psa_cipher_encrypt(mbedtls_svc_key_id_t key, size_t default_iv_length = 0; psa_key_attributes_t attributes; - LOCAL_INPUT_DECLARE(input_external); + LOCAL_INPUT_DECLARE(input_external, input); + LOCAL_OUTPUT_DECLARE(output_external, output); + LOCAL_INPUT_ALLOC(input_external, input_length, input); - LOCAL_OUTPUT_DECLARE(output_external); LOCAL_OUTPUT_ALLOC(output_external, output_size, output); if (!PSA_ALG_IS_CIPHER(alg)) { @@ -4509,8 +4515,8 @@ exit: *output_length = 0; } - LOCAL_INPUT_FREE(input, input_external); - LOCAL_OUTPUT_FREE(output, output_external); + LOCAL_INPUT_FREE(input_external, input); + LOCAL_OUTPUT_FREE(output_external, output); return status; } From 666845322c76a7c6f5271ed8833b647ccfbde5b1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 15 Dec 2023 18:29:54 +0000 Subject: [PATCH 151/184] Improve guards around memory poisoning setup We should not setup or teardown test hooks when we do not have MBEDTLS_PSA_CRYPTO_C. Signed-off-by: David Horstmann --- tests/src/helpers.c | 6 ++++-- tests/src/psa_memory_poisoning_wrappers.c | 6 ++++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/tests/src/helpers.c b/tests/src/helpers.c index 53a17abda2..1c58faefab 100644 --- a/tests/src/helpers.c +++ b/tests/src/helpers.c @@ -33,7 +33,8 @@ int mbedtls_test_platform_setup(void) { int ret = 0; -#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) mbedtls_poison_test_hooks_setup(); #endif @@ -57,7 +58,8 @@ int mbedtls_test_platform_setup(void) void mbedtls_test_platform_teardown(void) { -#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) mbedtls_poison_test_hooks_teardown(); #endif diff --git a/tests/src/psa_memory_poisoning_wrappers.c b/tests/src/psa_memory_poisoning_wrappers.c index c865d1f908..89830a596b 100644 --- a/tests/src/psa_memory_poisoning_wrappers.c +++ b/tests/src/psa_memory_poisoning_wrappers.c @@ -8,7 +8,8 @@ #include "psa_crypto_invasive.h" -#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) void mbedtls_poison_test_hooks_setup(void) { @@ -48,4 +49,5 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, return status; } -#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_TEST_MEMORY_CAN_POISON */ +#endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C && + MBEDTLS_TEST_MEMORY_CAN_POISON */ \ No newline at end of file From d20ffaf06fa76237cebd5d79abb748ad45f10387 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Fri, 15 Dec 2023 19:05:40 +0000 Subject: [PATCH 152/184] Remove accidental addition of MBEDTLS_TEST_HOOKS Remove MBEDTLS_TEST_HOOKS from the default config, to which it was erroneously added. Signed-off-by: David Horstmann --- include/mbedtls/mbedtls_config.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index 4b29ea2793..f3f3d2bd85 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -2070,7 +2070,7 @@ * * Uncomment to enable invasive tests. */ -#define MBEDTLS_TEST_HOOKS +//#define MBEDTLS_TEST_HOOKS /** * \def MBEDTLS_THREADING_ALT From 83ece2fe494ffac9004587622c800b72a91785b9 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 18 Dec 2023 15:30:46 +0000 Subject: [PATCH 153/184] Add extra MBEDTLS_PSA_CRYPTO_C guard for header Do not include psa_memory_poisoning_wrappers.h unless MBEDTLS_PSA_CRYPTO_C is set. Signed-off-by: David Horstmann --- tests/include/test/psa_crypto_helpers.h | 2 +- tests/src/helpers.c | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/include/test/psa_crypto_helpers.h b/tests/include/test/psa_crypto_helpers.h index 935e071819..3d421d7fed 100644 --- a/tests/include/test/psa_crypto_helpers.h +++ b/tests/include/test/psa_crypto_helpers.h @@ -16,7 +16,7 @@ #include #endif -#if defined(MBEDTLS_TEST_HOOKS) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) #include "test/psa_memory_poisoning_wrappers.h" #endif diff --git a/tests/src/helpers.c b/tests/src/helpers.c index 1c58faefab..843a9d4146 100644 --- a/tests/src/helpers.c +++ b/tests/src/helpers.c @@ -13,7 +13,7 @@ #include #endif -#if defined(MBEDTLS_TEST_HOOKS) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) #include #endif From 6d43e6d76a924b5870cf6d8501fd5115703d0e04 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Mon, 18 Dec 2023 15:58:17 +0000 Subject: [PATCH 154/184] Add missing newline at end of file Signed-off-by: David Horstmann --- tests/src/psa_memory_poisoning_wrappers.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/src/psa_memory_poisoning_wrappers.c b/tests/src/psa_memory_poisoning_wrappers.c index 89830a596b..a53e875d4b 100644 --- a/tests/src/psa_memory_poisoning_wrappers.c +++ b/tests/src/psa_memory_poisoning_wrappers.c @@ -50,4 +50,4 @@ psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, } #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C && - MBEDTLS_TEST_MEMORY_CAN_POISON */ \ No newline at end of file + MBEDTLS_TEST_MEMORY_CAN_POISON */ From c09f36dd1b47706c68eb9ad245e30861f0003ac6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 20 Dec 2023 10:57:43 +0000 Subject: [PATCH 155/184] Invert note about buffer overlap support When MBEDTLS_PSA_COPY_CALLER_BUFFERS is disabled, it causes overlap to not be supported. Signed-off-by: David Horstmann --- include/mbedtls/mbedtls_config.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mbedtls/mbedtls_config.h b/include/mbedtls/mbedtls_config.h index f3f3d2bd85..ed33828077 100644 --- a/include/mbedtls/mbedtls_config.h +++ b/include/mbedtls/mbedtls_config.h @@ -1479,8 +1479,8 @@ * * \note Enabling this option increases memory usage and code size. * - * \note Enabling this option enables full support for overlap of input and - * output buffers passed to PSA functions. + * \note Disabling this option causes overlap of input and output buffers + * not to be supported by PSA functions. */ #define MBEDTLS_PSA_COPY_CALLER_BUFFERS From 0f06bde936ff094d271cef0264c387f74ea4b0a3 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 20 Dec 2023 11:10:16 +0000 Subject: [PATCH 156/184] Add all.sh coponent to test with copying disabled Signed-off-by: David Horstmann --- tests/scripts/all.sh | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index c3d2fb88c1..4465d05068 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -1216,6 +1216,17 @@ component_test_psa_crypto_key_id_encodes_owner () { make test } +component_test_no_psa_copy_caller_buffers () { + msg "build: full config - MBEDTLS_PSA_COPY_CALLER_BUFFERS, cmake, gcc, ASan" + scripts/config.py full + scripts/config.py unset MBEDTLS_PSA_COPY_CALLER_BUFFERS + CC=gcc cmake -D CMAKE_BUILD_TYPE:String=Asan . + make + + msg "test: full config - MBEDTLS_PSA_COPY_CALLER_BUFFERS, cmake, gcc, ASan" + make test +} + # check_renamed_symbols HEADER LIB # Check that if HEADER contains '#define MACRO ...' then MACRO is not a symbol # name is LIB. From 2de5abf2842e912a69bb19ef281c76f5b29c925f Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 20 Dec 2023 11:26:40 +0000 Subject: [PATCH 157/184] Only poison memory when buffer copying is enabled Make sure that we don't enable memory poisoning when MBEDTLS_PSA_COPY_CALLER_BUFFERS is disabled. Signed-off-by: David Horstmann --- tests/include/test/psa_crypto_helpers.h | 3 ++- tests/src/helpers.c | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/tests/include/test/psa_crypto_helpers.h b/tests/include/test/psa_crypto_helpers.h index 3d421d7fed..41b7752bea 100644 --- a/tests/include/test/psa_crypto_helpers.h +++ b/tests/include/test/psa_crypto_helpers.h @@ -16,7 +16,8 @@ #include #endif -#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) +#if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) #include "test/psa_memory_poisoning_wrappers.h" #endif diff --git a/tests/src/helpers.c b/tests/src/helpers.c index 843a9d4146..36564fe29a 100644 --- a/tests/src/helpers.c +++ b/tests/src/helpers.c @@ -34,6 +34,7 @@ int mbedtls_test_platform_setup(void) int ret = 0; #if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) \ && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) mbedtls_poison_test_hooks_setup(); #endif @@ -59,6 +60,7 @@ int mbedtls_test_platform_setup(void) void mbedtls_test_platform_teardown(void) { #if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ + && defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) \ && defined(MBEDTLS_TEST_MEMORY_CAN_POISON) mbedtls_poison_test_hooks_teardown(); #endif From dad6d6666141cd9aee7c18f4cda403e738d1f5fd Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Wed, 27 Dec 2023 10:22:59 +0000 Subject: [PATCH 158/184] RSA: document Montgomery trick in unblind Signed-off-by: Janos Follath --- library/rsa.c | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 8ddef2dac3..803759b6b2 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1003,9 +1003,14 @@ static int rsa_unblind(mbedtls_mpi *T, mbedtls_mpi *Vf, mbedtls_mpi *N) MBEDTLS_MPI_CHK(mbedtls_mpi_grow(T, nlimbs)); MBEDTLS_MPI_CHK(mbedtls_mpi_grow(Vf, nlimbs)); - // T = T * R mod N + /* T = T * Vf mod N + * Reminder: montmul(A, B, N) = A * B * R^-1 mod N + * Usually both operands are multiplied by R mod N beforehand (by calling + * `to_mont_rep()` on them), yielding a result that's also * R mod N (aka + * "in the Montgomery domain"). Here we only multiply one operand by R mod + * N, so the result is directly what we want - no need to call + * `from_mont_rep()` on it. */ mbedtls_mpi_core_to_mont_rep(T->p, T->p, N->p, nlimbs, mm, RR.p, M_T.p); - // T = T * Vf mod N mbedtls_mpi_core_montmul(T->p, T->p, Vf->p, nlimbs, N->p, nlimbs, mm, M_T.p); cleanup: From c762521e7389dbe2f17d40217890e4307b7a1d18 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Wed, 27 Dec 2023 10:33:00 +0000 Subject: [PATCH 159/184] RSA: remove unneeded temporaries Signed-off-by: Janos Follath --- library/rsa.c | 21 +++------------------ 1 file changed, 3 insertions(+), 18 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 803759b6b2..1357e1bc02 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -1068,18 +1068,9 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, /* Temporaries holding the blinded exponents for * the mod p resp. mod q computation (if used). */ mbedtls_mpi DP_blind, DQ_blind; - - /* Pointers to actual exponents to be used - either the unblinded - * or the blinded ones, depending on the presence of a PRNG. */ - mbedtls_mpi *DP = &ctx->DP; - mbedtls_mpi *DQ = &ctx->DQ; #else /* Temporary holding the blinded exponent (if used). */ mbedtls_mpi D_blind; - - /* Pointer to actual exponent to be used - either the unblinded - * or the blinded one, depending on the presence of a PRNG. */ - mbedtls_mpi *D = &ctx->D; #endif /* MBEDTLS_RSA_NO_CRT */ /* Temporaries holding the initial input and the double @@ -1155,8 +1146,6 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&D_blind, &P1, &Q1)); MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&D_blind, &D_blind, &R)); MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&D_blind, &D_blind, &ctx->D)); - - D = &D_blind; #else /* * DP_blind = ( P - 1 ) * R + DP @@ -1167,8 +1156,6 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&DP_blind, &DP_blind, &ctx->DP)); - DP = &DP_blind; - /* * DQ_blind = ( Q - 1 ) * R + DQ */ @@ -1177,12 +1164,10 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&DQ_blind, &Q1, &R)); MBEDTLS_MPI_CHK(mbedtls_mpi_add_mpi(&DQ_blind, &DQ_blind, &ctx->DQ)); - - DQ = &DQ_blind; #endif /* MBEDTLS_RSA_NO_CRT */ #if defined(MBEDTLS_RSA_NO_CRT) - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&T, &T, D, &ctx->N, &ctx->RN)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&T, &T, &D_blind, &ctx->N, &ctx->RN)); #else /* * Faster decryption using the CRT @@ -1191,8 +1176,8 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, * TQ = input ^ dQ mod Q */ - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TP, &T, DP, &ctx->P, &ctx->RP)); - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TQ, &T, DQ, &ctx->Q, &ctx->RQ)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TP, &T, &DP_blind, &ctx->P, &ctx->RP)); + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&TQ, &T, &DQ_blind, &ctx->Q, &ctx->RQ)); /* * T = (TP - TQ) * (Q^-1 mod P) mod P From d83dc85a10f52e0c3d0942d61474bbeb49f230f5 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Wed, 27 Dec 2023 10:44:36 +0000 Subject: [PATCH 160/184] RSA: improve readability Signed-off-by: Janos Follath --- library/rsa.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/library/rsa.c b/library/rsa.c index 1357e1bc02..9e59e9de26 100644 --- a/library/rsa.c +++ b/library/rsa.c @@ -986,7 +986,7 @@ cleanup: * Unblind * T = T * Vf mod N */ -static int rsa_unblind(mbedtls_mpi *T, mbedtls_mpi *Vf, mbedtls_mpi *N) +static int rsa_unblind(mbedtls_mpi *T, mbedtls_mpi *Vf, const mbedtls_mpi *N) { int ret = MBEDTLS_ERR_ERROR_CORRUPTION_DETECTED; const mbedtls_mpi_uint mm = mbedtls_mpi_core_montmul_init(N->p); @@ -1075,7 +1075,7 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, /* Temporaries holding the initial input and the double * checked result; should be the same in the end. */ - mbedtls_mpi I, C; + mbedtls_mpi input_blinded, check_result_blinded; if (f_rng == NULL) { return MBEDTLS_ERR_RSA_BAD_INPUT_DATA; @@ -1110,8 +1110,8 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, mbedtls_mpi_init(&TP); mbedtls_mpi_init(&TQ); #endif - mbedtls_mpi_init(&I); - mbedtls_mpi_init(&C); + mbedtls_mpi_init(&input_blinded); + mbedtls_mpi_init(&check_result_blinded); /* End of MPI initialization */ @@ -1129,7 +1129,7 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, MBEDTLS_MPI_CHK(mbedtls_mpi_mul_mpi(&T, &T, &ctx->Vi)); MBEDTLS_MPI_CHK(mbedtls_mpi_mod_mpi(&T, &T, &ctx->N)); - MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&I, &T)); + MBEDTLS_MPI_CHK(mbedtls_mpi_copy(&input_blinded, &T)); /* * Exponent blinding @@ -1194,9 +1194,9 @@ int mbedtls_rsa_private(mbedtls_rsa_context *ctx, #endif /* MBEDTLS_RSA_NO_CRT */ /* Verify the result to prevent glitching attacks. */ - MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&C, &T, &ctx->E, + MBEDTLS_MPI_CHK(mbedtls_mpi_exp_mod(&check_result_blinded, &T, &ctx->E, &ctx->N, &ctx->RN)); - if (mbedtls_mpi_cmp_mpi(&C, &I) != 0) { + if (mbedtls_mpi_cmp_mpi(&check_result_blinded, &input_blinded) != 0) { ret = MBEDTLS_ERR_RSA_VERIFY_FAILED; goto cleanup; } @@ -1234,8 +1234,8 @@ cleanup: mbedtls_mpi_free(&TP); mbedtls_mpi_free(&TQ); #endif - mbedtls_mpi_free(&C); - mbedtls_mpi_free(&I); + mbedtls_mpi_free(&check_result_blinded); + mbedtls_mpi_free(&input_blinded); if (ret != 0 && ret >= -0x007f) { return MBEDTLS_ERROR_ADD(MBEDTLS_ERR_RSA_PRIVATE_FAILED, ret); From 8c4cabf6aa65e92b70c2b410021014276586c6d0 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Wed, 27 Dec 2023 10:47:21 +0000 Subject: [PATCH 161/184] Fix typo Signed-off-by: Janos Follath --- ChangeLog.d/fix-Marvin-attack.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ChangeLog.d/fix-Marvin-attack.txt b/ChangeLog.d/fix-Marvin-attack.txt index f729304eef..017f7b1f80 100644 --- a/ChangeLog.d/fix-Marvin-attack.txt +++ b/ChangeLog.d/fix-Marvin-attack.txt @@ -1,6 +1,6 @@ Security * Fix a timing side channel in RSA private operations. This side channel could be sufficient for a local attacker to recover the plaintext. It - requires the attecker to send a large number of messages for decryption. + requires the attacker to send a large number of messages for decryption. For details, see "Everlasting ROBOT: the Marvin Attack", Hubert Kario. Reported by Hubert Kario, Red Hat. From 2125443aef349e08885da39f622beb56619091cd Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Fri, 29 Dec 2023 11:14:58 +0000 Subject: [PATCH 162/184] Add warning for PKCS 1.5 decryption Any timing variance dependant on the output of this function enables a Bleichenbacher attack. It is extremely difficult to use safely. In the Marvin attack paper (https://people.redhat.com/~hkario/marvin/marvin-attack-paper.pdf) the author suggests that implementations of PKCS 1.5 decryption that don't include a countermeasure should be considered inherently dangerous. They suggest that all libraries implement the same countermeasure, as implementing different countermeasures across libraries enables the Bleichenbacher attack as well. This is extremely fragile and therefore we don't implement it. The use of PKCS 1.5 in Mbed TLS implements the countermeasures recommended in the TLS standard (7.4.7.1 of RFC 5246) and is not vulnerable. Add a warning to PKCS 1.5 decryption to warn users about this. Signed-off-by: Janos Follath --- include/mbedtls/rsa.h | 9 +++++++++ include/psa/crypto_values.h | 7 +++++++ 2 files changed, 16 insertions(+) diff --git a/include/mbedtls/rsa.h b/include/mbedtls/rsa.h index 69f3981ede..84d5d16e9a 100644 --- a/include/mbedtls/rsa.h +++ b/include/mbedtls/rsa.h @@ -696,6 +696,10 @@ int mbedtls_rsa_rsaes_oaep_encrypt(mbedtls_rsa_context *ctx, * It is the generic wrapper for performing a PKCS#1 decryption * operation. * + * \warning When \p ctx->padding is set to #MBEDTLS_RSA_PKCS_V15, + * mbedtls_rsa_rsaes_pkcs1_v15_decrypt() is called, which is an + * inherently dangerous function (CWE-242). + * * \note The output buffer length \c output_max_len should be * as large as the size \p ctx->len of \p ctx->N (for example, * 128 Bytes if RSA-1024 is used) to be able to hold an @@ -732,6 +736,11 @@ int mbedtls_rsa_pkcs1_decrypt(mbedtls_rsa_context *ctx, * \brief This function performs a PKCS#1 v1.5 decryption * operation (RSAES-PKCS1-v1_5-DECRYPT). * + * \warning This is an inherently dangerous function (CWE-242). Unless + * it is used in a side channel free and safe way (eg. + * implementing the TLS protocol as per 7.4.7.1 of RFC 5246), + * the calling code is vulnerable. + * * \note The output buffer length \c output_max_len should be * as large as the size \p ctx->len of \p ctx->N, for example, * 128 Bytes if RSA-1024 is used, to be able to hold an diff --git a/include/psa/crypto_values.h b/include/psa/crypto_values.h index 241b7c80d1..f59f7fde12 100644 --- a/include/psa/crypto_values.h +++ b/include/psa/crypto_values.h @@ -1748,6 +1748,13 @@ 0) /** RSA PKCS#1 v1.5 encryption. + * + * \warning Calling psa_asymmetric_decrypt() with this algorithm as a + * parameter is considered an inherently dangerous function + * (CWE-242). Unless it is used in a side channel free and safe + * way (eg. implementing the TLS protocol as per 7.4.7.1 of + * RFC 5246), the calling code is vulnerable. + * */ #define PSA_ALG_RSA_PKCS1V15_CRYPT ((psa_algorithm_t) 0x07000200) From 0b39d1ed7d74b934515f23e9eef43b46438735d2 Mon Sep 17 00:00:00 2001 From: Janos Follath Date: Thu, 11 Jan 2024 14:24:02 +0000 Subject: [PATCH 163/184] Update Marvin fix Changelog entry Upon further consideration we think that a remote attacker close to the victim might be able to have precise enough timing information to exploit the side channel as well. Update the Changelog to reflect this. Signed-off-by: Janos Follath --- ChangeLog.d/fix-Marvin-attack.txt | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/ChangeLog.d/fix-Marvin-attack.txt b/ChangeLog.d/fix-Marvin-attack.txt index 017f7b1f80..763533c25c 100644 --- a/ChangeLog.d/fix-Marvin-attack.txt +++ b/ChangeLog.d/fix-Marvin-attack.txt @@ -1,6 +1,8 @@ Security - * Fix a timing side channel in RSA private operations. This side channel - could be sufficient for a local attacker to recover the plaintext. It - requires the attacker to send a large number of messages for decryption. - For details, see "Everlasting ROBOT: the Marvin Attack", Hubert Kario. - Reported by Hubert Kario, Red Hat. + * Fix a timing side channel in private key RSA operations. This side channel + could be sufficient for an attacker to recover the plaintext. A local + attacker or a remote attacker who is close to the victim on the network + might have precise enough timing measurements to exploit this. It requires + the attacker to send a large number of messages for decryption. For + details, see "Everlasting ROBOT: the Marvin Attack", Hubert Kario. Reported + by Hubert Kario, Red Hat. From 8ba3f68561224c0b6b2ac34822ef45ab7895821d Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 11 Jan 2024 15:53:52 +0000 Subject: [PATCH 164/184] Stop enforcing C99 in CMakeLists.txt This allows us to sometimes use C11 features. Signed-off-by: David Horstmann --- CMakeLists.txt | 3 --- 1 file changed, 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 36baa3b402..210cc38f34 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,9 +177,6 @@ string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}") include(CheckCCompilerFlag) -set(CMAKE_C_EXTENSIONS OFF) -set(CMAKE_C_STANDARD 99) - if(CMAKE_COMPILER_IS_GNU) # some warnings we want are not available with old GCC versions # note: starting with CMake 2.8 we could use CMAKE_C_COMPILER_VERSION From 8174478f82c6e78a008cd266212b404528a3207a Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 10 Jan 2024 14:33:17 +0000 Subject: [PATCH 165/184] Use thread-local flag to enable memory poisoning Allow memory poisoning to be enabled and disabled at runtime using a thread-local flag. This allows poisoning to be disabled whenever a PSA function is called but not through the test wrappers, removing false positive use-after-poisons. Signed-off-by: David Horstmann --- tests/include/test/memory.h | 21 ++++++++++++++++++--- tests/src/test_memory.c | 12 +++++++++--- 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index c22ef53f7c..e0437517da 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -22,9 +22,12 @@ * memory as poisoned, which can be used to enforce some memory access * policies. * + * Support for the C11 thread_local keyword is also required. + * * Currently, only Asan (Address Sanitizer) is supported. */ -#if defined(MBEDTLS_TEST_HAVE_ASAN) +#if defined(MBEDTLS_TEST_HAVE_ASAN) && \ + (__STDC_VERSION__ >= 201112L) # define MBEDTLS_TEST_MEMORY_CAN_POISON #endif @@ -62,6 +65,12 @@ #if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) +/** Thread-local variable used to enable memory poisoning. This is set and + * unset in the test wrappers so that calls to PSA functions from the library + * do not poison memory. + */ +extern _Thread_local int mbedtls_test_memory_poisoning_enabled; + /** Poison a memory area so that any attempt to read or write from it will * cause a runtime failure. * @@ -69,7 +78,10 @@ */ void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size); #define MBEDTLS_TEST_MEMORY_POISON(ptr, size) \ - mbedtls_test_memory_poison(ptr, size) + do { \ + mbedtls_test_memory_poisoning_enabled = 1; \ + mbedtls_test_memory_poison(ptr, size); \ + } while (0) /** Undo the effect of mbedtls_test_memory_poison(). * @@ -80,7 +92,10 @@ void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size); */ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); #define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) \ - mbedtls_test_memory_unpoison(ptr, size) + do { \ + mbedtls_test_memory_unpoison(ptr, size); \ + mbedtls_test_memory_poisoning_enabled = 0; \ + } while (0) #else /* MBEDTLS_TEST_MEMORY_CAN_POISON */ #define MBEDTLS_TEST_MEMORY_POISON(ptr, size) ((void) (ptr), (void) (size)) diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index c277be85ab..dbb7b20de2 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -13,12 +13,15 @@ #include #include -#if defined(MBEDTLS_TEST_HAVE_ASAN) +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) #include #include #endif -#if defined(MBEDTLS_TEST_HAVE_ASAN) +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) + +_Thread_local int mbedtls_test_memory_poisoning_enabled = 0; + static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) { uintptr_t start = (uintptr_t) *p_ptr; @@ -36,6 +39,9 @@ static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) { + if (!mbedtls_test_memory_poisoning_enabled) { + return; + } if (size == 0) { return; } @@ -51,4 +57,4 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size) align_for_asan(&ptr, &size); __asan_unpoison_memory_region(ptr, size); } -#endif /* Asan */ +#endif /* Memory poisoning */ From 9432e64933f77fb3392b9346607670ea7caaacd6 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Thu, 11 Jan 2024 16:33:46 +0000 Subject: [PATCH 166/184] Only run memory poisoning metatests when poisoning When we cannot memory poison due to platform constraints, do not attempt to run memory poisoning metatests (but still run other ASan metatests). Signed-off-by: David Horstmann --- programs/test/metatest.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index 392b638420..bdcb1df9af 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -362,6 +362,7 @@ metatest_t metatests[] = { { "double_free", "asan", double_free }, { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, +#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) { "test_memory_poison_0_0_8_r", "asan", test_memory_poison }, { "test_memory_poison_0_0_8_w", "asan", test_memory_poison }, { "test_memory_poison_0_7_8_r", "asan", test_memory_poison }, @@ -378,6 +379,7 @@ metatest_t metatests[] = { { "test_memory_poison_7_0_1_w", "asan", test_memory_poison }, { "test_memory_poison_7_1_2_r", "asan", test_memory_poison }, { "test_memory_poison_7_1_2_w", "asan", test_memory_poison }, +#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, From 00ed38c25b2937fb237404beccfff4d9fd07b879 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 22 Nov 2023 19:24:31 +0100 Subject: [PATCH 167/184] Python module to parse function declarations from a header file Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/c_parsing_helper.py | 127 ++++++++++++++++++++++++ 1 file changed, 127 insertions(+) create mode 100644 scripts/mbedtls_dev/c_parsing_helper.py diff --git a/scripts/mbedtls_dev/c_parsing_helper.py b/scripts/mbedtls_dev/c_parsing_helper.py new file mode 100644 index 0000000000..3bb6f0405e --- /dev/null +++ b/scripts/mbedtls_dev/c_parsing_helper.py @@ -0,0 +1,127 @@ +"""Helper functions to parse C code in heavily constrained scenarios. + +Currently supported functionality: + +* read_function_declarations: read function declarations from a header file. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import re +from typing import Dict, Iterable, Iterator, List, Optional, Tuple + + +class ArgumentInfo: + """Information about an argument to an API function.""" + #pylint: disable=too-few-public-methods + + _KEYWORDS = [ + 'const', 'register', 'restrict', + 'int', 'long', 'short', 'signed', 'unsigned', + ] + _DECLARATION_RE = re.compile( + r'(?P\w[\w\s*]*?)\s*' + + r'(?!(?:' + r'|'.join(_KEYWORDS) + r'))(?P\b\w+\b)?' + + r'\s*(?P\[[^][]*\])?\Z', + re.A | re.S) + + @classmethod + def normalize_type(cls, typ: str) -> str: + """Normalize whitespace in a type.""" + typ = re.sub(r'\s+', r' ', typ) + typ = re.sub(r'\s*\*', r' *', typ) + return typ + + def __init__(self, decl: str) -> None: + self.decl = decl.strip() + m = self._DECLARATION_RE.match(self.decl) + if not m: + raise ValueError(self.decl) + self.type = self.normalize_type(m.group('type')) #type: str + self.name = m.group('name') #type: Optional[str] + self.suffix = m.group('suffix') if m.group('suffix') else '' #type: str + + +class FunctionInfo: + """Information about an API function.""" + #pylint: disable=too-few-public-methods + + # Regex matching the declaration of a function that returns void. + VOID_RE = re.compile(r'\s*\bvoid\s*\Z', re.A) + + def __init__(self, #pylint: disable=too-many-arguments + filename: str, + line_number: int, + qualifiers: Iterable[str], + return_type: str, + name: str, + arguments: List[str]) -> None: + self.filename = filename + self.line_number = line_number + self.qualifiers = frozenset(qualifiers) + self.return_type = return_type + self.name = name + self.arguments = [ArgumentInfo(arg) for arg in arguments] + + def returns_void(self) -> bool: + """Whether the function returns void.""" + return bool(self.VOID_RE.search(self.return_type)) + + +# Match one C comment. +# Note that we match both comment types, so things like // in a /*...*/ +# comment are handled correctly. +_C_COMMENT_RE = re.compile(r'//[^n]*|/\*.*?\*/', re.S) +_NOT_NEWLINES_RE = re.compile(r'[^\n]+') + +def read_logical_lines(filename: str) -> Iterator[Tuple[int, str]]: + """Read logical lines from a file. + + Logical lines are one or more physical line, with balanced parentheses. + """ + with open(filename, encoding='utf-8') as inp: + content = inp.read() + # Strip comments, but keep newlines for line numbering + content = re.sub(_C_COMMENT_RE, + lambda m: re.sub(_NOT_NEWLINES_RE, "", m.group(0)), + content) + lines = enumerate(content.splitlines(), 1) + for line_number, line in lines: + # Read a logical line, containing balanced parentheses. + # We assume that parentheses are balanced (this should be ok + # since comments have been stripped), otherwise there will be + # a gigantic logical line at the end. + paren_level = line.count('(') - line.count(')') + while paren_level > 0: + _, more = next(lines) #pylint: disable=stop-iteration-return + paren_level += more.count('(') - more.count(')') + line += '\n' + more + yield line_number, line + +_C_FUNCTION_DECLARATION_RE = re.compile( + r'(?P(?:(?:extern|inline|static)\b\s*)*)' + r'(?P\w[\w\s*]*?)\s*' + + r'\b(?P\w+)' + + r'\s*\((?P.*)\)\s*;', + re.A | re.S) + +def read_function_declarations(functions: Dict[str, FunctionInfo], + filename: str) -> None: + """Collect function declarations from a C header file.""" + for line_number, line in read_logical_lines(filename): + m = _C_FUNCTION_DECLARATION_RE.match(line) + if not m: + continue + qualifiers = m.group('qualifiers').split() + return_type = m.group('return_type') + name = m.group('name') + arguments = m.group('arguments').split(',') + if len(arguments) == 1 and re.match(FunctionInfo.VOID_RE, arguments[0]): + arguments = [] + # Note: we replace any existing declaration for the same name. + functions[name] = FunctionInfo(filename, line_number, + qualifiers, + return_type, + name, + arguments) From d022093ea691423339a5a620bedffdcbd95e5b0c Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 22 Nov 2023 19:24:59 +0100 Subject: [PATCH 168/184] C function wrapper generator The Base class generates trivial wrappers that just call the underlying function. It is meant as a base class to construct useful wrapper generators. The Logging class generates wrappers that can log the inputs and outputs to a function. Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/c_wrapper_generator.py | 459 +++++++++++++++++++++ 1 file changed, 459 insertions(+) create mode 100644 scripts/mbedtls_dev/c_wrapper_generator.py diff --git a/scripts/mbedtls_dev/c_wrapper_generator.py b/scripts/mbedtls_dev/c_wrapper_generator.py new file mode 100644 index 0000000000..26da9b2f26 --- /dev/null +++ b/scripts/mbedtls_dev/c_wrapper_generator.py @@ -0,0 +1,459 @@ +"""Generate C wrapper functions. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import os +import re +import sys +import typing +from typing import Dict, List, Optional, Tuple + +from .c_parsing_helper import ArgumentInfo, FunctionInfo +from . import typing_util + + +def c_declare(prefix: str, name: str, suffix: str) -> str: + """Format a declaration of name with the given type prefix and suffix.""" + if not prefix.endswith('*'): + prefix += ' ' + return prefix + name + suffix + + +WrapperInfo = typing.NamedTuple('WrapperInfo', [ + ('argument_names', List[str]), + ('guard', Optional[str]), + ('wrapper_name', str), +]) + + +class Base: + """Generate a C source file containing wrapper functions.""" + + # This class is designed to have many methods potentially overloaded. + # Tell pylint not to complain about methods that have unused arguments: + # child classes are likely to override those methods and need the + # arguments in question. + #pylint: disable=no-self-use,unused-argument + + # Prefix prepended to the function's name to form the wrapper name. + _WRAPPER_NAME_PREFIX = '' + # Suffix appended to the function's name to form the wrapper name. + _WRAPPER_NAME_SUFFIX = '_wrap' + + # Functions with one of these qualifiers are skipped. + _SKIP_FUNCTION_WITH_QUALIFIERS = frozenset(['inline', 'static']) + + def __init__(self): + """Construct a wrapper generator object. + """ + self.program_name = os.path.basename(sys.argv[0]) + # To be populated in a derived class + self.functions = {} #type: Dict[str, FunctionInfo] + # Preprocessor symbol used as a guard against multiple inclusion in the + # header. Must be set before writing output to a header. + # Not used when writing .c output. + self.header_guard = None #type: Optional[str] + + def _write_prologue(self, out: typing_util.Writable, header: bool) -> None: + """Write the prologue of a C file. + + This includes a description comment and some include directives. + """ + out.write("""/* Automatically generated by {}, do not edit! */ + +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ +""" + .format(self.program_name)) + if header: + out.write(""" +#ifndef {guard} +#define {guard} + +#ifdef __cplusplus +extern "C" {{ +#endif +""" + .format(guard=self.header_guard)) + out.write(""" +#include +""") + + def _write_epilogue(self, out: typing_util.Writable, header: bool) -> None: + """Write the epilogue of a C file. + """ + if header: + out.write(""" +#ifdef __cplusplus +}} +#endif + +#endif /* {guard} */ +""" + .format(guard=self.header_guard)) + out.write(""" +/* End of automatically generated file. */ +""") + + def _wrapper_function_name(self, original_name: str) -> str: + """The name of the wrapper function. + + By default, this adds a suffix. + """ + return (self._WRAPPER_NAME_PREFIX + + original_name + + self._WRAPPER_NAME_SUFFIX) + + def _wrapper_declaration_start(self, + function: FunctionInfo, + wrapper_name: str) -> str: + """The beginning of the wrapper function declaration. + + This ends just before the opening parenthesis of the argument list. + + This is a string containing at least the return type and the + function name. It may start with additional qualifiers or attributes + such as `static`, `__attribute__((...))`, etc. + """ + return c_declare(function.return_type, wrapper_name, '') + + def _argument_name(self, + function_name: str, + num: int, + arg: ArgumentInfo) -> str: + """Name to use for the given argument in the wrapper function. + + Argument numbers count from 0. + """ + name = 'arg' + str(num) + if arg.name: + name += '_' + arg.name + return name + + def _wrapper_declaration_argument(self, + function_name: str, + num: int, name: str, + arg: ArgumentInfo) -> str: + """One argument definition in the wrapper function declaration. + + Argument numbers count from 0. + """ + return c_declare(arg.type, name, arg.suffix) + + def _underlying_function_name(self, function: FunctionInfo) -> str: + """The name of the underlying function. + + By default, this is the name of the wrapped function. + """ + return function.name + + def _return_variable_name(self, function: FunctionInfo) -> str: + """The name of the variable that will contain the return value.""" + return 'retval' + + def _write_function_call(self, out: typing_util.Writable, + function: FunctionInfo, + argument_names: List[str]) -> None: + """Write the call to the underlying function. + """ + # Note that the function name is in parentheses, to avoid calling + # a function-like macro with the same name, since in typical usage + # there is a function-like macro with the same name which is the + # wrapper. + call = '({})({})'.format(self._underlying_function_name(function), + ', '.join(argument_names)) + if function.returns_void(): + out.write(' {};\n'.format(call)) + else: + ret_name = self._return_variable_name(function) + ret_decl = c_declare(function.return_type, ret_name, '') + out.write(' {} = {};\n'.format(ret_decl, call)) + + def _write_function_return(self, out: typing_util.Writable, + function: FunctionInfo, + if_void: bool = False) -> None: + """Write a return statement. + + If the function returns void, only write a statement if if_void is true. + """ + if function.returns_void(): + if if_void: + out.write(' return;\n') + else: + ret_name = self._return_variable_name(function) + out.write(' return {};\n'.format(ret_name)) + + def _write_function_body(self, out: typing_util.Writable, + function: FunctionInfo, + argument_names: List[str]) -> None: + """Write the body of the wrapper code for the specified function. + """ + self._write_function_call(out, function, argument_names) + self._write_function_return(out, function) + + def _skip_function(self, function: FunctionInfo) -> bool: + """Whether to skip this function. + + By default, static or inline functions are skipped. + """ + if not self._SKIP_FUNCTION_WITH_QUALIFIERS.isdisjoint(function.qualifiers): + return True + return False + + _FUNCTION_GUARDS = { + } #type: Dict[str, str] + + def _function_guard(self, function: FunctionInfo) -> Optional[str]: + """A preprocessor condition for this function. + + The wrapper will be guarded with `#if` on this condition, if not None. + """ + return self._FUNCTION_GUARDS.get(function.name) + + def _wrapper_info(self, function: FunctionInfo) -> Optional[WrapperInfo]: + """Information about the wrapper for one function. + + Return None if the function should be skipped. + """ + if self._skip_function(function): + return None + argument_names = [self._argument_name(function.name, num, arg) + for num, arg in enumerate(function.arguments)] + return WrapperInfo( + argument_names=argument_names, + guard=self._function_guard(function), + wrapper_name=self._wrapper_function_name(function.name), + ) + + def _write_function_prototype(self, out: typing_util.Writable, + function: FunctionInfo, + wrapper: WrapperInfo, + header: bool) -> None: + """Write the prototype of a wrapper function. + + If header is true, write a function declaration, with a semicolon at + the end. Otherwise just write the prototype, intended to be followed + by the function's body. + """ + declaration_start = self._wrapper_declaration_start(function, + wrapper.wrapper_name) + arg_indent = ' ' + terminator = ';\n' if header else '\n' + if function.arguments: + out.write(declaration_start + '(\n') + for num in range(len(function.arguments)): + arg_def = self._wrapper_declaration_argument( + function.name, + num, wrapper.argument_names[num], function.arguments[num]) + arg_terminator = \ + (')' + terminator if num == len(function.arguments) - 1 else + ',\n') + out.write(arg_indent + arg_def + arg_terminator) + else: + out.write(declaration_start + '(void)' + terminator) + + def _write_c_function(self, out: typing_util.Writable, + function: FunctionInfo) -> None: + """Write wrapper code for one function. + + Do nothing if the function is skipped. + """ + wrapper = self._wrapper_info(function) + if wrapper is None: + return + out.write(""" +/* Wrapper for {} */ +""" + .format(function.name)) + if wrapper.guard is not None: + out.write('#if {}\n'.format(wrapper.guard)) + self._write_function_prototype(out, function, wrapper, False) + out.write('{\n') + self._write_function_body(out, function, wrapper.argument_names) + out.write('}\n') + if wrapper.guard is not None: + out.write('#endif /* {} */\n'.format(wrapper.guard)) + + def _write_h_function(self, out: typing_util.Writable, + function: FunctionInfo, + wrapper: WrapperInfo) -> None: + """Write the declaration of one wrapper function. + """ + out.write('\n') + if wrapper.guard is not None: + out.write('#if {}\n'.format(wrapper.guard)) + self._write_function_prototype(out, function, wrapper, True) + if wrapper.guard is not None: + out.write('#endif /* {} */\n'.format(wrapper.guard)) + + def _write_h_macro(self, out: typing_util.Writable, + function: FunctionInfo, + wrapper: WrapperInfo) -> None: + """Write the macro definition for one wrapper. + """ + arg_list = ', '.join(wrapper.argument_names) + out.write('#define {function_name}({args}) \\\n {wrapper_name}({args})\n' + .format(function_name=function.name, + wrapper_name=wrapper.wrapper_name, + args=arg_list)) + + def write_c_file(self, filename: str) -> None: + """Output a whole C file containing function wrapper definitions.""" + with open(filename, 'w', encoding='utf-8') as out: + self._write_prologue(out, False) + for name in sorted(self.functions): + self._write_c_function(out, self.functions[name]) + self._write_epilogue(out, False) + + def _header_guard_from_file_name(self, filename: str) -> str: + """Preprocessor symbol used as a guard against multiple inclusion.""" + # Heuristic to strip irrelevant leading directories + filename = re.sub(r'.*include[\\/]', r'', filename) + return re.sub(r'[^0-9A-Za-z]', r'_', filename, re.A).upper() + + def write_h_file(self, filename: str) -> None: + """Output a header file with function wrapper declarations and macro definitions.""" + self.header_guard = self._header_guard_from_file_name(filename) + with open(filename, 'w', encoding='utf-8') as out: + self._write_prologue(out, True) + for name in sorted(self.functions.keys()): + function = self.functions[name] + wrapper = self._wrapper_info(function) + if wrapper is None: + continue + self._write_h_function(out, function, wrapper) + self._write_h_macro(out, function, wrapper) + self._write_epilogue(out, True) + + +class UnknownTypeForPrintf(Exception): + """Exception raised when attempting to generate code that logs a value of an unknown type.""" + + def __init__(self, typ: str) -> None: + super().__init__("Unknown type for printf format generation: " + typ) + + +class Logging(Base): + """Generate wrapper functions that log the inputs and outputs.""" + + def __init__(self) -> None: + """Construct a wrapper generator including logging of inputs and outputs. + + Log to stdout by default. Call `set_stream` to change this. + """ + super().__init__() + self.stream = 'stdout' + + def set_stream(self, stream: str) -> None: + """Set the stdio stream to log to. + + Call this method before calling `write_c_output` or `write_h_output`. + """ + self.stream = stream + + def _write_prologue(self, out: typing_util.Writable, header: bool) -> None: + super()._write_prologue(out, header) + if not header: + out.write(""" +#if defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) +#include +#include +#include // for MBEDTLS_PRINTF_SIZET +#include // for mbedtls_fprintf +#endif /* defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) */ +""") + + _PRINTF_SIMPLE_FORMAT = { + 'int': '%d', + 'long': '%ld', + 'long long': '%lld', + 'size_t': '%"MBEDTLS_PRINTF_SIZET"', + 'unsigned': '0x%08x', + 'unsigned int': '0x%08x', + 'unsigned long': '0x%08lx', + 'unsigned long long': '0x%016llx', + } + + def _printf_simple_format(self, typ: str) -> Optional[str]: + """Use this printf format for a value of typ. + + Return None if values of typ need more complex handling. + """ + return self._PRINTF_SIMPLE_FORMAT.get(typ) + + _PRINTF_TYPE_CAST = { + 'int32_t': 'int', + 'uint32_t': 'unsigned', + 'uint64_t': 'unsigned long long', + } #type: Dict[str, str] + + def _printf_type_cast(self, typ: str) -> Optional[str]: + """Cast values of typ to this type before passing them to printf. + + Return None if values of the given type do not need a cast. + """ + return self._PRINTF_TYPE_CAST.get(typ) + + _POINTER_TYPE_RE = re.compile(r'\s*\*\Z') + + def _printf_parameters(self, typ: str, var: str) -> Tuple[str, List[str]]: + """The printf format and arguments for a value of type typ stored in var. + """ + expr = var + base_type = typ + # For outputs via a pointer, get the value that has been written. + # Note: we don't support pointers to pointers here. + pointer_match = self._POINTER_TYPE_RE.search(base_type) + if pointer_match: + base_type = base_type[:pointer_match.start(0)] + expr = '*({})'.format(expr) + # Maybe cast the value to a standard type. + cast_to = self._printf_type_cast(base_type) + if cast_to is not None: + expr = '({}) {}'.format(cast_to, expr) + base_type = cast_to + # Try standard types. + fmt = self._printf_simple_format(base_type) + if fmt is not None: + return '{}={}'.format(var, fmt), [expr] + raise UnknownTypeForPrintf(typ) + + def _write_function_logging(self, out: typing_util.Writable, + function: FunctionInfo, + argument_names: List[str]) -> None: + """Write code to log the function's inputs and outputs.""" + formats, values = '%s', ['"' + function.name + '"'] + for arg_info, arg_name in zip(function.arguments, argument_names): + fmt, vals = self._printf_parameters(arg_info.type, arg_name) + if fmt: + formats += ' ' + fmt + values += vals + if not function.returns_void(): + ret_name = self._return_variable_name(function) + fmt, vals = self._printf_parameters(function.return_type, ret_name) + if fmt: + formats += ' ' + fmt + values += vals + out.write("""\ +#if defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) + if ({stream}) {{ + mbedtls_fprintf({stream}, "{formats}\\n", + {values}); + }} +#endif /* defined(MBEDTLS_FS_IO) && defined(MBEDTLS_TEST_HOOKS) */ +""" + .format(stream=self.stream, + formats=formats, + values=', '.join(values))) + + def _write_function_body(self, out: typing_util.Writable, + function: FunctionInfo, + argument_names: List[str]) -> None: + """Write the body of the wrapper code for the specified function. + """ + self._write_function_call(out, function, argument_names) + self._write_function_logging(out, function, argument_names) + self._write_function_return(out, function) From f838eb2259ac74167508101377e88645534f81f5 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 23 Nov 2023 14:12:29 +0100 Subject: [PATCH 169/184] Guard the macro definition It doesn't make sense to define a macro expanding to a non-existent function. Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/c_wrapper_generator.py | 46 +++++++++++++--------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/scripts/mbedtls_dev/c_wrapper_generator.py b/scripts/mbedtls_dev/c_wrapper_generator.py index 26da9b2f26..de99ddb9ae 100644 --- a/scripts/mbedtls_dev/c_wrapper_generator.py +++ b/scripts/mbedtls_dev/c_wrapper_generator.py @@ -277,21 +277,16 @@ extern "C" {{ if wrapper.guard is not None: out.write('#endif /* {} */\n'.format(wrapper.guard)) - def _write_h_function(self, out: typing_util.Writable, - function: FunctionInfo, - wrapper: WrapperInfo) -> None: + def _write_h_function_declaration(self, out: typing_util.Writable, + function: FunctionInfo, + wrapper: WrapperInfo) -> None: """Write the declaration of one wrapper function. """ - out.write('\n') - if wrapper.guard is not None: - out.write('#if {}\n'.format(wrapper.guard)) self._write_function_prototype(out, function, wrapper, True) - if wrapper.guard is not None: - out.write('#endif /* {} */\n'.format(wrapper.guard)) - def _write_h_macro(self, out: typing_util.Writable, - function: FunctionInfo, - wrapper: WrapperInfo) -> None: + def _write_h_macro_definition(self, out: typing_util.Writable, + function: FunctionInfo, + wrapper: WrapperInfo) -> None: """Write the macro definition for one wrapper. """ arg_list = ', '.join(wrapper.argument_names) @@ -300,6 +295,26 @@ extern "C" {{ wrapper_name=wrapper.wrapper_name, args=arg_list)) + def _write_h_function(self, out: typing_util.Writable, + function: FunctionInfo) -> None: + """Write the complete header content for one wrapper. + + This is the declaration of the wrapper function, and the + definition of a function-like macro that calls the wrapper function. + + Do nothing if the function is skipped. + """ + wrapper = self._wrapper_info(function) + if wrapper is None: + return + out.write('\n') + if wrapper.guard is not None: + out.write('#if {}\n'.format(wrapper.guard)) + self._write_h_function_declaration(out, function, wrapper) + self._write_h_macro_definition(out, function, wrapper) + if wrapper.guard is not None: + out.write('#endif /* {} */\n'.format(wrapper.guard)) + def write_c_file(self, filename: str) -> None: """Output a whole C file containing function wrapper definitions.""" with open(filename, 'w', encoding='utf-8') as out: @@ -319,13 +334,8 @@ extern "C" {{ self.header_guard = self._header_guard_from_file_name(filename) with open(filename, 'w', encoding='utf-8') as out: self._write_prologue(out, True) - for name in sorted(self.functions.keys()): - function = self.functions[name] - wrapper = self._wrapper_info(function) - if wrapper is None: - continue - self._write_h_function(out, function, wrapper) - self._write_h_macro(out, function, wrapper) + for name in sorted(self.functions): + self._write_h_function(out, self.functions[name]) self._write_epilogue(out, True) From 5294bb347c84f9c6252fde00f18ab8d2046c4732 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 16:38:17 +0100 Subject: [PATCH 170/184] PSA wrapper generator The new script `tests/scripts/generate_psa_wrappers.py` generates the implementation of wrapper functions for PSA API functions, as well as a header that defines macros that redirect calls to the wrapper functions. By default, the wrapper functions just call the underlying library function. With `--log`, the wrapper functions log the arguments and return values. This commit only introduces the new script. Subsequent commits will integrate the wrappers in the build. Signed-off-by: Gilles Peskine --- tests/scripts/generate_psa_wrappers.py | 159 +++++++++++++++++++++++++ 1 file changed, 159 insertions(+) create mode 100755 tests/scripts/generate_psa_wrappers.py diff --git a/tests/scripts/generate_psa_wrappers.py b/tests/scripts/generate_psa_wrappers.py new file mode 100755 index 0000000000..9bb6eb758b --- /dev/null +++ b/tests/scripts/generate_psa_wrappers.py @@ -0,0 +1,159 @@ +#!/usr/bin/env python3 +"""Generate wrapper functions for PSA function calls. +""" + +# Copyright The Mbed TLS Contributors +# SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + +import argparse +import os +from typing import List, Tuple + +import scripts_path #pylint: disable=unused-import +from mbedtls_dev import build_tree +from mbedtls_dev import c_parsing_helper +from mbedtls_dev import c_wrapper_generator +from mbedtls_dev import typing_util + + +class PSAWrapperGenerator(c_wrapper_generator.Base): + """Generate a C source file containing wrapper functions for PSA Crypto API calls.""" + + _CPP_GUARDS = 'defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS)' + _WRAPPER_NAME_PREFIX = 'mbedtls_test_wrap_' + _WRAPPER_NAME_SUFFIX = '' + + def gather_data(self) -> None: + root_dir = build_tree.guess_mbedtls_root() + for header_name in ['crypto.h', 'crypto_extra.h']: + header_path = os.path.join(root_dir, 'include', 'psa', header_name) + c_parsing_helper.read_function_declarations(self.functions, header_path) + + _SKIP_FUNCTIONS = frozenset([ + 'mbedtls_psa_external_get_random', # not a library function + 'psa_get_key_domain_parameters', # client-side function + 'psa_get_key_slot_number', # client-side function + 'psa_key_derivation_verify_bytes', # not implemented yet + 'psa_key_derivation_verify_key', # not implemented yet + 'psa_set_key_domain_parameters', # client-side function + ]) + + def _skip_function(self, function: c_wrapper_generator.FunctionInfo) -> bool: + if function.return_type != 'psa_status_t': + return True + if function.name in self._SKIP_FUNCTIONS: + return True + return False + + # PAKE stuff: not implemented yet + _PAKE_STUFF = frozenset([ + 'psa_crypto_driver_pake_inputs_t *', + 'psa_pake_cipher_suite_t *', + ]) + + def _return_variable_name(self, + function: c_wrapper_generator.FunctionInfo) -> str: + """The name of the variable that will contain the return value.""" + if function.return_type == 'psa_status_t': + return 'status' + return super()._return_variable_name(function) + + _FUNCTION_GUARDS = c_wrapper_generator.Base._FUNCTION_GUARDS.copy() \ + #pylint: disable=protected-access + _FUNCTION_GUARDS.update({ + 'mbedtls_psa_register_se_key': 'defined(MBEDTLS_PSA_CRYPTO_SE_C)', + 'mbedtls_psa_inject_entropy': 'defined(MBEDTLS_PSA_INJECT_ENTROPY)', + 'mbedtls_psa_external_get_random': 'defined(MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG)', + 'mbedtls_psa_platform_get_builtin_key': 'defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)', + }) + + def _write_prologue(self, out: typing_util.Writable, header: bool) -> None: + super()._write_prologue(out, header) + out.write(""" +#if {} + +#include + +#include +#include +""" + .format(self._CPP_GUARDS)) + + def _write_epilogue(self, out: typing_util.Writable, header: bool) -> None: + out.write(""" +#endif /* {} */ +""" + .format(self._CPP_GUARDS)) + super()._write_epilogue(out, header) + + +class PSALoggingWrapperGenerator(PSAWrapperGenerator, c_wrapper_generator.Logging): + """Generate a C source file containing wrapper functions that log PSA Crypto API calls.""" + + def __init__(self, stream: str) -> None: + super().__init__() + self.set_stream(stream) + + _PRINTF_TYPE_CAST = c_wrapper_generator.Logging._PRINTF_TYPE_CAST.copy() + _PRINTF_TYPE_CAST.update({ + 'mbedtls_svc_key_id_t': 'unsigned', + 'psa_algorithm_t': 'unsigned', + 'psa_drv_slot_number_t': 'unsigned long long', + 'psa_key_derivation_step_t': 'int', + 'psa_key_id_t': 'unsigned', + 'psa_key_slot_number_t': 'unsigned long long', + 'psa_key_lifetime_t': 'unsigned', + 'psa_key_type_t': 'unsigned', + 'psa_key_usage_flags_t': 'unsigned', + 'psa_pake_role_t': 'int', + 'psa_pake_step_t': 'int', + 'psa_status_t': 'int', + }) + + def _printf_parameters(self, typ: str, var: str) -> Tuple[str, List[str]]: + if typ.startswith('const '): + typ = typ[6:] + if typ == 'uint8_t *': + # Skip buffers + return '', [] + if typ.endswith('operation_t *'): + return '', [] + if typ in self._PAKE_STUFF: + return '', [] + if typ == 'psa_key_attributes_t *': + return (var + '={id=%u, lifetime=0x%08x, type=0x%08x, bits=%u, alg=%08x, usage=%08x}', + ['(unsigned) psa_get_key_{}({})'.format(field, var) + for field in ['id', 'lifetime', 'type', 'bits', 'algorithm', 'usage_flags']]) + return super()._printf_parameters(typ, var) + + +DEFAULT_C_OUTPUT_FILE_NAME = 'tests/src/psa_test_wrappers.c' +DEFAULT_H_OUTPUT_FILE_NAME = 'tests/include/test/psa_test_wrappers.h' + +def main() -> None: + parser = argparse.ArgumentParser(description=globals()['__doc__']) + parser.add_argument('--log', + help='Stream to log to (default: no logging code)') + parser.add_argument('--output-c', + metavar='FILENAME', + default=DEFAULT_C_OUTPUT_FILE_NAME, + help=('Output .c file path (default: {}; skip .c output if empty)' + .format(DEFAULT_C_OUTPUT_FILE_NAME))) + parser.add_argument('--output-h', + metavar='FILENAME', + default=DEFAULT_H_OUTPUT_FILE_NAME, + help=('Output .h file path (default: {}; skip .h output if empty)' + .format(DEFAULT_H_OUTPUT_FILE_NAME))) + options = parser.parse_args() + if options.log: + generator = PSALoggingWrapperGenerator(options.log) #type: PSAWrapperGenerator + else: + generator = PSAWrapperGenerator() + generator.gather_data() + if options.output_h: + generator.write_h_file(options.output_h) + if options.output_c: + generator.write_c_file(options.output_c) + +if __name__ == '__main__': + main() From 6e4332cc24f18480ac388590fbc57e9e22b1f237 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 16:42:40 +0100 Subject: [PATCH 171/184] Commit generated PSA wrappers Commit files generated by `tests/scripts/generate_psa_wrappers.py`. As of this commit, the new code is neither useful (the wrappers just call the underlying functions) nor used (the wrapper functions are not called from anywhere). This will change in subsequent commits. This is a deviation from our normal practice of generating configuration-independent platform-independent files as part of the build in the development branch. The PSA test wrappers will be committed to the repository for some time for two reasons: * In the short term, we will review the generated code but not fully review the generator script. * The build scripts cannot yet accommodate a generated header file. Signed-off-by: Gilles Peskine --- tests/include/test/psa_test_wrappers.h | 719 ++++++++++++++++++ tests/src/psa_test_wrappers.c | 973 +++++++++++++++++++++++++ 2 files changed, 1692 insertions(+) create mode 100644 tests/include/test/psa_test_wrappers.h create mode 100644 tests/src/psa_test_wrappers.c diff --git a/tests/include/test/psa_test_wrappers.h b/tests/include/test/psa_test_wrappers.h new file mode 100644 index 0000000000..18417800ce --- /dev/null +++ b/tests/include/test/psa_test_wrappers.h @@ -0,0 +1,719 @@ +/* Automatically generated by generate_psa_wrappers.py, do not edit! */ + +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#ifndef TEST_PSA_TEST_WRAPPERS_H +#define TEST_PSA_TEST_WRAPPERS_H + +#ifdef __cplusplus +extern "C" { +#endif + +#include + +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) + +#include + +#include +#include + +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +psa_status_t mbedtls_test_wrap_mbedtls_psa_inject_entropy( + const uint8_t *arg0_seed, + size_t arg1_seed_size); +#define mbedtls_psa_inject_entropy(arg0_seed, arg1_seed_size) \ + mbedtls_test_wrap_mbedtls_psa_inject_entropy(arg0_seed, arg1_seed_size) +#endif /* defined(MBEDTLS_PSA_INJECT_ENTROPY) */ + +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) +psa_status_t mbedtls_test_wrap_mbedtls_psa_platform_get_builtin_key( + mbedtls_svc_key_id_t arg0_key_id, + psa_key_lifetime_t *arg1_lifetime, + psa_drv_slot_number_t *arg2_slot_number); +#define mbedtls_psa_platform_get_builtin_key(arg0_key_id, arg1_lifetime, arg2_slot_number) \ + mbedtls_test_wrap_mbedtls_psa_platform_get_builtin_key(arg0_key_id, arg1_lifetime, arg2_slot_number) +#endif /* defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) */ + +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +psa_status_t mbedtls_test_wrap_mbedtls_psa_register_se_key( + const psa_key_attributes_t *arg0_attributes); +#define mbedtls_psa_register_se_key(arg0_attributes) \ + mbedtls_test_wrap_mbedtls_psa_register_se_key(arg0_attributes) +#endif /* defined(MBEDTLS_PSA_CRYPTO_SE_C) */ + +psa_status_t mbedtls_test_wrap_psa_aead_abort( + psa_aead_operation_t *arg0_operation); +#define psa_aead_abort(arg0_operation) \ + mbedtls_test_wrap_psa_aead_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_aead_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_nonce, + size_t arg3_nonce_length, + const uint8_t *arg4_additional_data, + size_t arg5_additional_data_length, + const uint8_t *arg6_ciphertext, + size_t arg7_ciphertext_length, + uint8_t *arg8_plaintext, + size_t arg9_plaintext_size, + size_t *arg10_plaintext_length); +#define psa_aead_decrypt(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_ciphertext, arg7_ciphertext_length, arg8_plaintext, arg9_plaintext_size, arg10_plaintext_length) \ + mbedtls_test_wrap_psa_aead_decrypt(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_ciphertext, arg7_ciphertext_length, arg8_plaintext, arg9_plaintext_size, arg10_plaintext_length) + +psa_status_t mbedtls_test_wrap_psa_aead_decrypt_setup( + psa_aead_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_aead_decrypt_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_aead_decrypt_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_aead_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_nonce, + size_t arg3_nonce_length, + const uint8_t *arg4_additional_data, + size_t arg5_additional_data_length, + const uint8_t *arg6_plaintext, + size_t arg7_plaintext_length, + uint8_t *arg8_ciphertext, + size_t arg9_ciphertext_size, + size_t *arg10_ciphertext_length); +#define psa_aead_encrypt(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_plaintext, arg7_plaintext_length, arg8_ciphertext, arg9_ciphertext_size, arg10_ciphertext_length) \ + mbedtls_test_wrap_psa_aead_encrypt(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_plaintext, arg7_plaintext_length, arg8_ciphertext, arg9_ciphertext_size, arg10_ciphertext_length) + +psa_status_t mbedtls_test_wrap_psa_aead_encrypt_setup( + psa_aead_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_aead_encrypt_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_aead_encrypt_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_aead_finish( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_ciphertext, + size_t arg2_ciphertext_size, + size_t *arg3_ciphertext_length, + uint8_t *arg4_tag, + size_t arg5_tag_size, + size_t *arg6_tag_length); +#define psa_aead_finish(arg0_operation, arg1_ciphertext, arg2_ciphertext_size, arg3_ciphertext_length, arg4_tag, arg5_tag_size, arg6_tag_length) \ + mbedtls_test_wrap_psa_aead_finish(arg0_operation, arg1_ciphertext, arg2_ciphertext_size, arg3_ciphertext_length, arg4_tag, arg5_tag_size, arg6_tag_length) + +psa_status_t mbedtls_test_wrap_psa_aead_generate_nonce( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_nonce, + size_t arg2_nonce_size, + size_t *arg3_nonce_length); +#define psa_aead_generate_nonce(arg0_operation, arg1_nonce, arg2_nonce_size, arg3_nonce_length) \ + mbedtls_test_wrap_psa_aead_generate_nonce(arg0_operation, arg1_nonce, arg2_nonce_size, arg3_nonce_length) + +psa_status_t mbedtls_test_wrap_psa_aead_set_lengths( + psa_aead_operation_t *arg0_operation, + size_t arg1_ad_length, + size_t arg2_plaintext_length); +#define psa_aead_set_lengths(arg0_operation, arg1_ad_length, arg2_plaintext_length) \ + mbedtls_test_wrap_psa_aead_set_lengths(arg0_operation, arg1_ad_length, arg2_plaintext_length) + +psa_status_t mbedtls_test_wrap_psa_aead_set_nonce( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_nonce, + size_t arg2_nonce_length); +#define psa_aead_set_nonce(arg0_operation, arg1_nonce, arg2_nonce_length) \ + mbedtls_test_wrap_psa_aead_set_nonce(arg0_operation, arg1_nonce, arg2_nonce_length) + +psa_status_t mbedtls_test_wrap_psa_aead_update( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_output, + size_t arg4_output_size, + size_t *arg5_output_length); +#define psa_aead_update(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length) \ + mbedtls_test_wrap_psa_aead_update(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length) + +psa_status_t mbedtls_test_wrap_psa_aead_update_ad( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length); +#define psa_aead_update_ad(arg0_operation, arg1_input, arg2_input_length) \ + mbedtls_test_wrap_psa_aead_update_ad(arg0_operation, arg1_input, arg2_input_length) + +psa_status_t mbedtls_test_wrap_psa_aead_verify( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_plaintext, + size_t arg2_plaintext_size, + size_t *arg3_plaintext_length, + const uint8_t *arg4_tag, + size_t arg5_tag_length); +#define psa_aead_verify(arg0_operation, arg1_plaintext, arg2_plaintext_size, arg3_plaintext_length, arg4_tag, arg5_tag_length) \ + mbedtls_test_wrap_psa_aead_verify(arg0_operation, arg1_plaintext, arg2_plaintext_size, arg3_plaintext_length, arg4_tag, arg5_tag_length) + +psa_status_t mbedtls_test_wrap_psa_asymmetric_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_salt, + size_t arg5_salt_length, + uint8_t *arg6_output, + size_t arg7_output_size, + size_t *arg8_output_length); +#define psa_asymmetric_decrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length) \ + mbedtls_test_wrap_psa_asymmetric_decrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length) + +psa_status_t mbedtls_test_wrap_psa_asymmetric_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_salt, + size_t arg5_salt_length, + uint8_t *arg6_output, + size_t arg7_output_size, + size_t *arg8_output_length); +#define psa_asymmetric_encrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length) \ + mbedtls_test_wrap_psa_asymmetric_encrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_abort( + psa_cipher_operation_t *arg0_operation); +#define psa_cipher_abort(arg0_operation) \ + mbedtls_test_wrap_psa_cipher_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_cipher_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length); +#define psa_cipher_decrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length) \ + mbedtls_test_wrap_psa_cipher_decrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_decrypt_setup( + psa_cipher_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_cipher_decrypt_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_cipher_decrypt_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_cipher_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length); +#define psa_cipher_encrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length) \ + mbedtls_test_wrap_psa_cipher_encrypt(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_encrypt_setup( + psa_cipher_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_cipher_encrypt_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_cipher_encrypt_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_cipher_finish( + psa_cipher_operation_t *arg0_operation, + uint8_t *arg1_output, + size_t arg2_output_size, + size_t *arg3_output_length); +#define psa_cipher_finish(arg0_operation, arg1_output, arg2_output_size, arg3_output_length) \ + mbedtls_test_wrap_psa_cipher_finish(arg0_operation, arg1_output, arg2_output_size, arg3_output_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_generate_iv( + psa_cipher_operation_t *arg0_operation, + uint8_t *arg1_iv, + size_t arg2_iv_size, + size_t *arg3_iv_length); +#define psa_cipher_generate_iv(arg0_operation, arg1_iv, arg2_iv_size, arg3_iv_length) \ + mbedtls_test_wrap_psa_cipher_generate_iv(arg0_operation, arg1_iv, arg2_iv_size, arg3_iv_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_set_iv( + psa_cipher_operation_t *arg0_operation, + const uint8_t *arg1_iv, + size_t arg2_iv_length); +#define psa_cipher_set_iv(arg0_operation, arg1_iv, arg2_iv_length) \ + mbedtls_test_wrap_psa_cipher_set_iv(arg0_operation, arg1_iv, arg2_iv_length) + +psa_status_t mbedtls_test_wrap_psa_cipher_update( + psa_cipher_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_output, + size_t arg4_output_size, + size_t *arg5_output_length); +#define psa_cipher_update(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length) \ + mbedtls_test_wrap_psa_cipher_update(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length) + +psa_status_t mbedtls_test_wrap_psa_copy_key( + mbedtls_svc_key_id_t arg0_source_key, + const psa_key_attributes_t *arg1_attributes, + mbedtls_svc_key_id_t *arg2_target_key); +#define psa_copy_key(arg0_source_key, arg1_attributes, arg2_target_key) \ + mbedtls_test_wrap_psa_copy_key(arg0_source_key, arg1_attributes, arg2_target_key) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_cipher_suite( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + psa_pake_cipher_suite_t *arg1_cipher_suite); +#define psa_crypto_driver_pake_get_cipher_suite(arg0_inputs, arg1_cipher_suite) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_cipher_suite(arg0_inputs, arg1_cipher_suite) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_password( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_buffer, + size_t arg2_buffer_size, + size_t *arg3_buffer_length); +#define psa_crypto_driver_pake_get_password(arg0_inputs, arg1_buffer, arg2_buffer_size, arg3_buffer_length) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_password(arg0_inputs, arg1_buffer, arg2_buffer_size, arg3_buffer_length) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_password_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_password_len); +#define psa_crypto_driver_pake_get_password_len(arg0_inputs, arg1_password_len) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_password_len(arg0_inputs, arg1_password_len) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_peer( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_peer_id, + size_t arg2_peer_id_size, + size_t *arg3_peer_id_length); +#define psa_crypto_driver_pake_get_peer(arg0_inputs, arg1_peer_id, arg2_peer_id_size, arg3_peer_id_length) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_peer(arg0_inputs, arg1_peer_id, arg2_peer_id_size, arg3_peer_id_length) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_peer_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_peer_len); +#define psa_crypto_driver_pake_get_peer_len(arg0_inputs, arg1_peer_len) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_peer_len(arg0_inputs, arg1_peer_len) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_user( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_user_id, + size_t arg2_user_id_size, + size_t *arg3_user_id_len); +#define psa_crypto_driver_pake_get_user(arg0_inputs, arg1_user_id, arg2_user_id_size, arg3_user_id_len) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_user(arg0_inputs, arg1_user_id, arg2_user_id_size, arg3_user_id_len) + +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_user_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_user_len); +#define psa_crypto_driver_pake_get_user_len(arg0_inputs, arg1_user_len) \ + mbedtls_test_wrap_psa_crypto_driver_pake_get_user_len(arg0_inputs, arg1_user_len) + +psa_status_t mbedtls_test_wrap_psa_crypto_init(void); +#define psa_crypto_init() \ + mbedtls_test_wrap_psa_crypto_init() + +psa_status_t mbedtls_test_wrap_psa_destroy_key( + mbedtls_svc_key_id_t arg0_key); +#define psa_destroy_key(arg0_key) \ + mbedtls_test_wrap_psa_destroy_key(arg0_key) + +psa_status_t mbedtls_test_wrap_psa_export_key( + mbedtls_svc_key_id_t arg0_key, + uint8_t *arg1_data, + size_t arg2_data_size, + size_t *arg3_data_length); +#define psa_export_key(arg0_key, arg1_data, arg2_data_size, arg3_data_length) \ + mbedtls_test_wrap_psa_export_key(arg0_key, arg1_data, arg2_data_size, arg3_data_length) + +psa_status_t mbedtls_test_wrap_psa_export_public_key( + mbedtls_svc_key_id_t arg0_key, + uint8_t *arg1_data, + size_t arg2_data_size, + size_t *arg3_data_length); +#define psa_export_public_key(arg0_key, arg1_data, arg2_data_size, arg3_data_length) \ + mbedtls_test_wrap_psa_export_public_key(arg0_key, arg1_data, arg2_data_size, arg3_data_length) + +psa_status_t mbedtls_test_wrap_psa_generate_key( + const psa_key_attributes_t *arg0_attributes, + mbedtls_svc_key_id_t *arg1_key); +#define psa_generate_key(arg0_attributes, arg1_key) \ + mbedtls_test_wrap_psa_generate_key(arg0_attributes, arg1_key) + +psa_status_t mbedtls_test_wrap_psa_generate_random( + uint8_t *arg0_output, + size_t arg1_output_size); +#define psa_generate_random(arg0_output, arg1_output_size) \ + mbedtls_test_wrap_psa_generate_random(arg0_output, arg1_output_size) + +psa_status_t mbedtls_test_wrap_psa_get_key_attributes( + mbedtls_svc_key_id_t arg0_key, + psa_key_attributes_t *arg1_attributes); +#define psa_get_key_attributes(arg0_key, arg1_attributes) \ + mbedtls_test_wrap_psa_get_key_attributes(arg0_key, arg1_attributes) + +psa_status_t mbedtls_test_wrap_psa_hash_abort( + psa_hash_operation_t *arg0_operation); +#define psa_hash_abort(arg0_operation) \ + mbedtls_test_wrap_psa_hash_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_hash_clone( + const psa_hash_operation_t *arg0_source_operation, + psa_hash_operation_t *arg1_target_operation); +#define psa_hash_clone(arg0_source_operation, arg1_target_operation) \ + mbedtls_test_wrap_psa_hash_clone(arg0_source_operation, arg1_target_operation) + +psa_status_t mbedtls_test_wrap_psa_hash_compare( + psa_algorithm_t arg0_alg, + const uint8_t *arg1_input, + size_t arg2_input_length, + const uint8_t *arg3_hash, + size_t arg4_hash_length); +#define psa_hash_compare(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_length) \ + mbedtls_test_wrap_psa_hash_compare(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_length) + +psa_status_t mbedtls_test_wrap_psa_hash_compute( + psa_algorithm_t arg0_alg, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_hash, + size_t arg4_hash_size, + size_t *arg5_hash_length); +#define psa_hash_compute(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_size, arg5_hash_length) \ + mbedtls_test_wrap_psa_hash_compute(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_size, arg5_hash_length) + +psa_status_t mbedtls_test_wrap_psa_hash_finish( + psa_hash_operation_t *arg0_operation, + uint8_t *arg1_hash, + size_t arg2_hash_size, + size_t *arg3_hash_length); +#define psa_hash_finish(arg0_operation, arg1_hash, arg2_hash_size, arg3_hash_length) \ + mbedtls_test_wrap_psa_hash_finish(arg0_operation, arg1_hash, arg2_hash_size, arg3_hash_length) + +psa_status_t mbedtls_test_wrap_psa_hash_setup( + psa_hash_operation_t *arg0_operation, + psa_algorithm_t arg1_alg); +#define psa_hash_setup(arg0_operation, arg1_alg) \ + mbedtls_test_wrap_psa_hash_setup(arg0_operation, arg1_alg) + +psa_status_t mbedtls_test_wrap_psa_hash_update( + psa_hash_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length); +#define psa_hash_update(arg0_operation, arg1_input, arg2_input_length) \ + mbedtls_test_wrap_psa_hash_update(arg0_operation, arg1_input, arg2_input_length) + +psa_status_t mbedtls_test_wrap_psa_hash_verify( + psa_hash_operation_t *arg0_operation, + const uint8_t *arg1_hash, + size_t arg2_hash_length); +#define psa_hash_verify(arg0_operation, arg1_hash, arg2_hash_length) \ + mbedtls_test_wrap_psa_hash_verify(arg0_operation, arg1_hash, arg2_hash_length) + +psa_status_t mbedtls_test_wrap_psa_import_key( + const psa_key_attributes_t *arg0_attributes, + const uint8_t *arg1_data, + size_t arg2_data_length, + mbedtls_svc_key_id_t *arg3_key); +#define psa_import_key(arg0_attributes, arg1_data, arg2_data_length, arg3_key) \ + mbedtls_test_wrap_psa_import_key(arg0_attributes, arg1_data, arg2_data_length, arg3_key) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_abort( + psa_key_derivation_operation_t *arg0_operation); +#define psa_key_derivation_abort(arg0_operation) \ + mbedtls_test_wrap_psa_key_derivation_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_get_capacity( + const psa_key_derivation_operation_t *arg0_operation, + size_t *arg1_capacity); +#define psa_key_derivation_get_capacity(arg0_operation, arg1_capacity) \ + mbedtls_test_wrap_psa_key_derivation_get_capacity(arg0_operation, arg1_capacity) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_bytes( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + const uint8_t *arg2_data, + size_t arg3_data_length); +#define psa_key_derivation_input_bytes(arg0_operation, arg1_step, arg2_data, arg3_data_length) \ + mbedtls_test_wrap_psa_key_derivation_input_bytes(arg0_operation, arg1_step, arg2_data, arg3_data_length) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_integer( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + uint64_t arg2_value); +#define psa_key_derivation_input_integer(arg0_operation, arg1_step, arg2_value) \ + mbedtls_test_wrap_psa_key_derivation_input_integer(arg0_operation, arg1_step, arg2_value) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_key( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + mbedtls_svc_key_id_t arg2_key); +#define psa_key_derivation_input_key(arg0_operation, arg1_step, arg2_key) \ + mbedtls_test_wrap_psa_key_derivation_input_key(arg0_operation, arg1_step, arg2_key) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_key_agreement( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + mbedtls_svc_key_id_t arg2_private_key, + const uint8_t *arg3_peer_key, + size_t arg4_peer_key_length); +#define psa_key_derivation_key_agreement(arg0_operation, arg1_step, arg2_private_key, arg3_peer_key, arg4_peer_key_length) \ + mbedtls_test_wrap_psa_key_derivation_key_agreement(arg0_operation, arg1_step, arg2_private_key, arg3_peer_key, arg4_peer_key_length) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_output_bytes( + psa_key_derivation_operation_t *arg0_operation, + uint8_t *arg1_output, + size_t arg2_output_length); +#define psa_key_derivation_output_bytes(arg0_operation, arg1_output, arg2_output_length) \ + mbedtls_test_wrap_psa_key_derivation_output_bytes(arg0_operation, arg1_output, arg2_output_length) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_output_key( + const psa_key_attributes_t *arg0_attributes, + psa_key_derivation_operation_t *arg1_operation, + mbedtls_svc_key_id_t *arg2_key); +#define psa_key_derivation_output_key(arg0_attributes, arg1_operation, arg2_key) \ + mbedtls_test_wrap_psa_key_derivation_output_key(arg0_attributes, arg1_operation, arg2_key) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_set_capacity( + psa_key_derivation_operation_t *arg0_operation, + size_t arg1_capacity); +#define psa_key_derivation_set_capacity(arg0_operation, arg1_capacity) \ + mbedtls_test_wrap_psa_key_derivation_set_capacity(arg0_operation, arg1_capacity) + +psa_status_t mbedtls_test_wrap_psa_key_derivation_setup( + psa_key_derivation_operation_t *arg0_operation, + psa_algorithm_t arg1_alg); +#define psa_key_derivation_setup(arg0_operation, arg1_alg) \ + mbedtls_test_wrap_psa_key_derivation_setup(arg0_operation, arg1_alg) + +psa_status_t mbedtls_test_wrap_psa_mac_abort( + psa_mac_operation_t *arg0_operation); +#define psa_mac_abort(arg0_operation) \ + mbedtls_test_wrap_psa_mac_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_mac_compute( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_mac, + size_t arg5_mac_size, + size_t *arg6_mac_length); +#define psa_mac_compute(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_size, arg6_mac_length) \ + mbedtls_test_wrap_psa_mac_compute(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_size, arg6_mac_length) + +psa_status_t mbedtls_test_wrap_psa_mac_sign_finish( + psa_mac_operation_t *arg0_operation, + uint8_t *arg1_mac, + size_t arg2_mac_size, + size_t *arg3_mac_length); +#define psa_mac_sign_finish(arg0_operation, arg1_mac, arg2_mac_size, arg3_mac_length) \ + mbedtls_test_wrap_psa_mac_sign_finish(arg0_operation, arg1_mac, arg2_mac_size, arg3_mac_length) + +psa_status_t mbedtls_test_wrap_psa_mac_sign_setup( + psa_mac_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_mac_sign_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_mac_sign_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_mac_update( + psa_mac_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length); +#define psa_mac_update(arg0_operation, arg1_input, arg2_input_length) \ + mbedtls_test_wrap_psa_mac_update(arg0_operation, arg1_input, arg2_input_length) + +psa_status_t mbedtls_test_wrap_psa_mac_verify( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_mac, + size_t arg5_mac_length); +#define psa_mac_verify(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_length) \ + mbedtls_test_wrap_psa_mac_verify(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_length) + +psa_status_t mbedtls_test_wrap_psa_mac_verify_finish( + psa_mac_operation_t *arg0_operation, + const uint8_t *arg1_mac, + size_t arg2_mac_length); +#define psa_mac_verify_finish(arg0_operation, arg1_mac, arg2_mac_length) \ + mbedtls_test_wrap_psa_mac_verify_finish(arg0_operation, arg1_mac, arg2_mac_length) + +psa_status_t mbedtls_test_wrap_psa_mac_verify_setup( + psa_mac_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg); +#define psa_mac_verify_setup(arg0_operation, arg1_key, arg2_alg) \ + mbedtls_test_wrap_psa_mac_verify_setup(arg0_operation, arg1_key, arg2_alg) + +psa_status_t mbedtls_test_wrap_psa_pake_abort( + psa_pake_operation_t *arg0_operation); +#define psa_pake_abort(arg0_operation) \ + mbedtls_test_wrap_psa_pake_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_pake_get_implicit_key( + psa_pake_operation_t *arg0_operation, + psa_key_derivation_operation_t *arg1_output); +#define psa_pake_get_implicit_key(arg0_operation, arg1_output) \ + mbedtls_test_wrap_psa_pake_get_implicit_key(arg0_operation, arg1_output) + +psa_status_t mbedtls_test_wrap_psa_pake_input( + psa_pake_operation_t *arg0_operation, + psa_pake_step_t arg1_step, + const uint8_t *arg2_input, + size_t arg3_input_length); +#define psa_pake_input(arg0_operation, arg1_step, arg2_input, arg3_input_length) \ + mbedtls_test_wrap_psa_pake_input(arg0_operation, arg1_step, arg2_input, arg3_input_length) + +psa_status_t mbedtls_test_wrap_psa_pake_output( + psa_pake_operation_t *arg0_operation, + psa_pake_step_t arg1_step, + uint8_t *arg2_output, + size_t arg3_output_size, + size_t *arg4_output_length); +#define psa_pake_output(arg0_operation, arg1_step, arg2_output, arg3_output_size, arg4_output_length) \ + mbedtls_test_wrap_psa_pake_output(arg0_operation, arg1_step, arg2_output, arg3_output_size, arg4_output_length) + +psa_status_t mbedtls_test_wrap_psa_pake_set_password_key( + psa_pake_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_password); +#define psa_pake_set_password_key(arg0_operation, arg1_password) \ + mbedtls_test_wrap_psa_pake_set_password_key(arg0_operation, arg1_password) + +psa_status_t mbedtls_test_wrap_psa_pake_set_peer( + psa_pake_operation_t *arg0_operation, + const uint8_t *arg1_peer_id, + size_t arg2_peer_id_len); +#define psa_pake_set_peer(arg0_operation, arg1_peer_id, arg2_peer_id_len) \ + mbedtls_test_wrap_psa_pake_set_peer(arg0_operation, arg1_peer_id, arg2_peer_id_len) + +psa_status_t mbedtls_test_wrap_psa_pake_set_role( + psa_pake_operation_t *arg0_operation, + psa_pake_role_t arg1_role); +#define psa_pake_set_role(arg0_operation, arg1_role) \ + mbedtls_test_wrap_psa_pake_set_role(arg0_operation, arg1_role) + +psa_status_t mbedtls_test_wrap_psa_pake_set_user( + psa_pake_operation_t *arg0_operation, + const uint8_t *arg1_user_id, + size_t arg2_user_id_len); +#define psa_pake_set_user(arg0_operation, arg1_user_id, arg2_user_id_len) \ + mbedtls_test_wrap_psa_pake_set_user(arg0_operation, arg1_user_id, arg2_user_id_len) + +psa_status_t mbedtls_test_wrap_psa_pake_setup( + psa_pake_operation_t *arg0_operation, + const psa_pake_cipher_suite_t *arg1_cipher_suite); +#define psa_pake_setup(arg0_operation, arg1_cipher_suite) \ + mbedtls_test_wrap_psa_pake_setup(arg0_operation, arg1_cipher_suite) + +psa_status_t mbedtls_test_wrap_psa_purge_key( + mbedtls_svc_key_id_t arg0_key); +#define psa_purge_key(arg0_key) \ + mbedtls_test_wrap_psa_purge_key(arg0_key) + +psa_status_t mbedtls_test_wrap_psa_raw_key_agreement( + psa_algorithm_t arg0_alg, + mbedtls_svc_key_id_t arg1_private_key, + const uint8_t *arg2_peer_key, + size_t arg3_peer_key_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length); +#define psa_raw_key_agreement(arg0_alg, arg1_private_key, arg2_peer_key, arg3_peer_key_length, arg4_output, arg5_output_size, arg6_output_length) \ + mbedtls_test_wrap_psa_raw_key_agreement(arg0_alg, arg1_private_key, arg2_peer_key, arg3_peer_key_length, arg4_output, arg5_output_size, arg6_output_length) + +psa_status_t mbedtls_test_wrap_psa_sign_hash( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_hash, + size_t arg3_hash_length, + uint8_t *arg4_signature, + size_t arg5_signature_size, + size_t *arg6_signature_length); +#define psa_sign_hash(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_size, arg6_signature_length) \ + mbedtls_test_wrap_psa_sign_hash(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_size, arg6_signature_length) + +psa_status_t mbedtls_test_wrap_psa_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *arg0_operation); +#define psa_sign_hash_abort(arg0_operation) \ + mbedtls_test_wrap_psa_sign_hash_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *arg0_operation, + uint8_t *arg1_signature, + size_t arg2_signature_size, + size_t *arg3_signature_length); +#define psa_sign_hash_complete(arg0_operation, arg1_signature, arg2_signature_size, arg3_signature_length) \ + mbedtls_test_wrap_psa_sign_hash_complete(arg0_operation, arg1_signature, arg2_signature_size, arg3_signature_length) + +psa_status_t mbedtls_test_wrap_psa_sign_hash_start( + psa_sign_hash_interruptible_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg, + const uint8_t *arg3_hash, + size_t arg4_hash_length); +#define psa_sign_hash_start(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length) \ + mbedtls_test_wrap_psa_sign_hash_start(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length) + +psa_status_t mbedtls_test_wrap_psa_sign_message( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_signature, + size_t arg5_signature_size, + size_t *arg6_signature_length); +#define psa_sign_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_size, arg6_signature_length) \ + mbedtls_test_wrap_psa_sign_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_size, arg6_signature_length) + +psa_status_t mbedtls_test_wrap_psa_verify_hash( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_hash, + size_t arg3_hash_length, + const uint8_t *arg4_signature, + size_t arg5_signature_length); +#define psa_verify_hash(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_length) \ + mbedtls_test_wrap_psa_verify_hash(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_length) + +psa_status_t mbedtls_test_wrap_psa_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *arg0_operation); +#define psa_verify_hash_abort(arg0_operation) \ + mbedtls_test_wrap_psa_verify_hash_abort(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *arg0_operation); +#define psa_verify_hash_complete(arg0_operation) \ + mbedtls_test_wrap_psa_verify_hash_complete(arg0_operation) + +psa_status_t mbedtls_test_wrap_psa_verify_hash_start( + psa_verify_hash_interruptible_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg, + const uint8_t *arg3_hash, + size_t arg4_hash_length, + const uint8_t *arg5_signature, + size_t arg6_signature_length); +#define psa_verify_hash_start(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length, arg5_signature, arg6_signature_length) \ + mbedtls_test_wrap_psa_verify_hash_start(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length, arg5_signature, arg6_signature_length) + +psa_status_t mbedtls_test_wrap_psa_verify_message( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_signature, + size_t arg5_signature_length); +#define psa_verify_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_length) \ + mbedtls_test_wrap_psa_verify_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_length) + +#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) */ + +#ifdef __cplusplus +} +#endif + +#endif /* TEST_PSA_TEST_WRAPPERS_H */ + +/* End of automatically generated file. */ diff --git a/tests/src/psa_test_wrappers.c b/tests/src/psa_test_wrappers.c new file mode 100644 index 0000000000..f25f1aa9b2 --- /dev/null +++ b/tests/src/psa_test_wrappers.c @@ -0,0 +1,973 @@ +/* Automatically generated by generate_psa_wrappers.py, do not edit! */ + +/* Copyright The Mbed TLS Contributors + * SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later + */ + +#include + +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) + +#include + +#include +#include + +/* Wrapper for mbedtls_psa_inject_entropy */ +#if defined(MBEDTLS_PSA_INJECT_ENTROPY) +psa_status_t mbedtls_test_wrap_mbedtls_psa_inject_entropy( + const uint8_t *arg0_seed, + size_t arg1_seed_size) +{ + psa_status_t status = (mbedtls_psa_inject_entropy)(arg0_seed, arg1_seed_size); + return status; +} +#endif /* defined(MBEDTLS_PSA_INJECT_ENTROPY) */ + +/* Wrapper for mbedtls_psa_platform_get_builtin_key */ +#if defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) +psa_status_t mbedtls_test_wrap_mbedtls_psa_platform_get_builtin_key( + mbedtls_svc_key_id_t arg0_key_id, + psa_key_lifetime_t *arg1_lifetime, + psa_drv_slot_number_t *arg2_slot_number) +{ + psa_status_t status = (mbedtls_psa_platform_get_builtin_key)(arg0_key_id, arg1_lifetime, arg2_slot_number); + return status; +} +#endif /* defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS) */ + +/* Wrapper for mbedtls_psa_register_se_key */ +#if defined(MBEDTLS_PSA_CRYPTO_SE_C) +psa_status_t mbedtls_test_wrap_mbedtls_psa_register_se_key( + const psa_key_attributes_t *arg0_attributes) +{ + psa_status_t status = (mbedtls_psa_register_se_key)(arg0_attributes); + return status; +} +#endif /* defined(MBEDTLS_PSA_CRYPTO_SE_C) */ + +/* Wrapper for psa_aead_abort */ +psa_status_t mbedtls_test_wrap_psa_aead_abort( + psa_aead_operation_t *arg0_operation) +{ + psa_status_t status = (psa_aead_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_aead_decrypt */ +psa_status_t mbedtls_test_wrap_psa_aead_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_nonce, + size_t arg3_nonce_length, + const uint8_t *arg4_additional_data, + size_t arg5_additional_data_length, + const uint8_t *arg6_ciphertext, + size_t arg7_ciphertext_length, + uint8_t *arg8_plaintext, + size_t arg9_plaintext_size, + size_t *arg10_plaintext_length) +{ + psa_status_t status = (psa_aead_decrypt)(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_ciphertext, arg7_ciphertext_length, arg8_plaintext, arg9_plaintext_size, arg10_plaintext_length); + return status; +} + +/* Wrapper for psa_aead_decrypt_setup */ +psa_status_t mbedtls_test_wrap_psa_aead_decrypt_setup( + psa_aead_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_aead_decrypt_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_aead_encrypt */ +psa_status_t mbedtls_test_wrap_psa_aead_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_nonce, + size_t arg3_nonce_length, + const uint8_t *arg4_additional_data, + size_t arg5_additional_data_length, + const uint8_t *arg6_plaintext, + size_t arg7_plaintext_length, + uint8_t *arg8_ciphertext, + size_t arg9_ciphertext_size, + size_t *arg10_ciphertext_length) +{ + psa_status_t status = (psa_aead_encrypt)(arg0_key, arg1_alg, arg2_nonce, arg3_nonce_length, arg4_additional_data, arg5_additional_data_length, arg6_plaintext, arg7_plaintext_length, arg8_ciphertext, arg9_ciphertext_size, arg10_ciphertext_length); + return status; +} + +/* Wrapper for psa_aead_encrypt_setup */ +psa_status_t mbedtls_test_wrap_psa_aead_encrypt_setup( + psa_aead_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_aead_encrypt_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_aead_finish */ +psa_status_t mbedtls_test_wrap_psa_aead_finish( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_ciphertext, + size_t arg2_ciphertext_size, + size_t *arg3_ciphertext_length, + uint8_t *arg4_tag, + size_t arg5_tag_size, + size_t *arg6_tag_length) +{ + psa_status_t status = (psa_aead_finish)(arg0_operation, arg1_ciphertext, arg2_ciphertext_size, arg3_ciphertext_length, arg4_tag, arg5_tag_size, arg6_tag_length); + return status; +} + +/* Wrapper for psa_aead_generate_nonce */ +psa_status_t mbedtls_test_wrap_psa_aead_generate_nonce( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_nonce, + size_t arg2_nonce_size, + size_t *arg3_nonce_length) +{ + psa_status_t status = (psa_aead_generate_nonce)(arg0_operation, arg1_nonce, arg2_nonce_size, arg3_nonce_length); + return status; +} + +/* Wrapper for psa_aead_set_lengths */ +psa_status_t mbedtls_test_wrap_psa_aead_set_lengths( + psa_aead_operation_t *arg0_operation, + size_t arg1_ad_length, + size_t arg2_plaintext_length) +{ + psa_status_t status = (psa_aead_set_lengths)(arg0_operation, arg1_ad_length, arg2_plaintext_length); + return status; +} + +/* Wrapper for psa_aead_set_nonce */ +psa_status_t mbedtls_test_wrap_psa_aead_set_nonce( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_nonce, + size_t arg2_nonce_length) +{ + psa_status_t status = (psa_aead_set_nonce)(arg0_operation, arg1_nonce, arg2_nonce_length); + return status; +} + +/* Wrapper for psa_aead_update */ +psa_status_t mbedtls_test_wrap_psa_aead_update( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_output, + size_t arg4_output_size, + size_t *arg5_output_length) +{ + psa_status_t status = (psa_aead_update)(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length); + return status; +} + +/* Wrapper for psa_aead_update_ad */ +psa_status_t mbedtls_test_wrap_psa_aead_update_ad( + psa_aead_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length) +{ + psa_status_t status = (psa_aead_update_ad)(arg0_operation, arg1_input, arg2_input_length); + return status; +} + +/* Wrapper for psa_aead_verify */ +psa_status_t mbedtls_test_wrap_psa_aead_verify( + psa_aead_operation_t *arg0_operation, + uint8_t *arg1_plaintext, + size_t arg2_plaintext_size, + size_t *arg3_plaintext_length, + const uint8_t *arg4_tag, + size_t arg5_tag_length) +{ + psa_status_t status = (psa_aead_verify)(arg0_operation, arg1_plaintext, arg2_plaintext_size, arg3_plaintext_length, arg4_tag, arg5_tag_length); + return status; +} + +/* Wrapper for psa_asymmetric_decrypt */ +psa_status_t mbedtls_test_wrap_psa_asymmetric_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_salt, + size_t arg5_salt_length, + uint8_t *arg6_output, + size_t arg7_output_size, + size_t *arg8_output_length) +{ + psa_status_t status = (psa_asymmetric_decrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length); + return status; +} + +/* Wrapper for psa_asymmetric_encrypt */ +psa_status_t mbedtls_test_wrap_psa_asymmetric_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_salt, + size_t arg5_salt_length, + uint8_t *arg6_output, + size_t arg7_output_size, + size_t *arg8_output_length) +{ + psa_status_t status = (psa_asymmetric_encrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_salt, arg5_salt_length, arg6_output, arg7_output_size, arg8_output_length); + return status; +} + +/* Wrapper for psa_cipher_abort */ +psa_status_t mbedtls_test_wrap_psa_cipher_abort( + psa_cipher_operation_t *arg0_operation) +{ + psa_status_t status = (psa_cipher_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_cipher_decrypt */ +psa_status_t mbedtls_test_wrap_psa_cipher_decrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length) +{ + psa_status_t status = (psa_cipher_decrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length); + return status; +} + +/* Wrapper for psa_cipher_decrypt_setup */ +psa_status_t mbedtls_test_wrap_psa_cipher_decrypt_setup( + psa_cipher_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_cipher_decrypt_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_cipher_encrypt */ +psa_status_t mbedtls_test_wrap_psa_cipher_encrypt( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length) +{ + psa_status_t status = (psa_cipher_encrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length); + return status; +} + +/* Wrapper for psa_cipher_encrypt_setup */ +psa_status_t mbedtls_test_wrap_psa_cipher_encrypt_setup( + psa_cipher_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_cipher_encrypt_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_cipher_finish */ +psa_status_t mbedtls_test_wrap_psa_cipher_finish( + psa_cipher_operation_t *arg0_operation, + uint8_t *arg1_output, + size_t arg2_output_size, + size_t *arg3_output_length) +{ + psa_status_t status = (psa_cipher_finish)(arg0_operation, arg1_output, arg2_output_size, arg3_output_length); + return status; +} + +/* Wrapper for psa_cipher_generate_iv */ +psa_status_t mbedtls_test_wrap_psa_cipher_generate_iv( + psa_cipher_operation_t *arg0_operation, + uint8_t *arg1_iv, + size_t arg2_iv_size, + size_t *arg3_iv_length) +{ + psa_status_t status = (psa_cipher_generate_iv)(arg0_operation, arg1_iv, arg2_iv_size, arg3_iv_length); + return status; +} + +/* Wrapper for psa_cipher_set_iv */ +psa_status_t mbedtls_test_wrap_psa_cipher_set_iv( + psa_cipher_operation_t *arg0_operation, + const uint8_t *arg1_iv, + size_t arg2_iv_length) +{ + psa_status_t status = (psa_cipher_set_iv)(arg0_operation, arg1_iv, arg2_iv_length); + return status; +} + +/* Wrapper for psa_cipher_update */ +psa_status_t mbedtls_test_wrap_psa_cipher_update( + psa_cipher_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_output, + size_t arg4_output_size, + size_t *arg5_output_length) +{ + psa_status_t status = (psa_cipher_update)(arg0_operation, arg1_input, arg2_input_length, arg3_output, arg4_output_size, arg5_output_length); + return status; +} + +/* Wrapper for psa_copy_key */ +psa_status_t mbedtls_test_wrap_psa_copy_key( + mbedtls_svc_key_id_t arg0_source_key, + const psa_key_attributes_t *arg1_attributes, + mbedtls_svc_key_id_t *arg2_target_key) +{ + psa_status_t status = (psa_copy_key)(arg0_source_key, arg1_attributes, arg2_target_key); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_cipher_suite */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_cipher_suite( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + psa_pake_cipher_suite_t *arg1_cipher_suite) +{ + psa_status_t status = (psa_crypto_driver_pake_get_cipher_suite)(arg0_inputs, arg1_cipher_suite); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_password */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_password( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_buffer, + size_t arg2_buffer_size, + size_t *arg3_buffer_length) +{ + psa_status_t status = (psa_crypto_driver_pake_get_password)(arg0_inputs, arg1_buffer, arg2_buffer_size, arg3_buffer_length); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_password_len */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_password_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_password_len) +{ + psa_status_t status = (psa_crypto_driver_pake_get_password_len)(arg0_inputs, arg1_password_len); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_peer */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_peer( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_peer_id, + size_t arg2_peer_id_size, + size_t *arg3_peer_id_length) +{ + psa_status_t status = (psa_crypto_driver_pake_get_peer)(arg0_inputs, arg1_peer_id, arg2_peer_id_size, arg3_peer_id_length); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_peer_len */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_peer_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_peer_len) +{ + psa_status_t status = (psa_crypto_driver_pake_get_peer_len)(arg0_inputs, arg1_peer_len); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_user */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_user( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + uint8_t *arg1_user_id, + size_t arg2_user_id_size, + size_t *arg3_user_id_len) +{ + psa_status_t status = (psa_crypto_driver_pake_get_user)(arg0_inputs, arg1_user_id, arg2_user_id_size, arg3_user_id_len); + return status; +} + +/* Wrapper for psa_crypto_driver_pake_get_user_len */ +psa_status_t mbedtls_test_wrap_psa_crypto_driver_pake_get_user_len( + const psa_crypto_driver_pake_inputs_t *arg0_inputs, + size_t *arg1_user_len) +{ + psa_status_t status = (psa_crypto_driver_pake_get_user_len)(arg0_inputs, arg1_user_len); + return status; +} + +/* Wrapper for psa_crypto_init */ +psa_status_t mbedtls_test_wrap_psa_crypto_init(void) +{ + psa_status_t status = (psa_crypto_init)(); + return status; +} + +/* Wrapper for psa_destroy_key */ +psa_status_t mbedtls_test_wrap_psa_destroy_key( + mbedtls_svc_key_id_t arg0_key) +{ + psa_status_t status = (psa_destroy_key)(arg0_key); + return status; +} + +/* Wrapper for psa_export_key */ +psa_status_t mbedtls_test_wrap_psa_export_key( + mbedtls_svc_key_id_t arg0_key, + uint8_t *arg1_data, + size_t arg2_data_size, + size_t *arg3_data_length) +{ + psa_status_t status = (psa_export_key)(arg0_key, arg1_data, arg2_data_size, arg3_data_length); + return status; +} + +/* Wrapper for psa_export_public_key */ +psa_status_t mbedtls_test_wrap_psa_export_public_key( + mbedtls_svc_key_id_t arg0_key, + uint8_t *arg1_data, + size_t arg2_data_size, + size_t *arg3_data_length) +{ + psa_status_t status = (psa_export_public_key)(arg0_key, arg1_data, arg2_data_size, arg3_data_length); + return status; +} + +/* Wrapper for psa_generate_key */ +psa_status_t mbedtls_test_wrap_psa_generate_key( + const psa_key_attributes_t *arg0_attributes, + mbedtls_svc_key_id_t *arg1_key) +{ + psa_status_t status = (psa_generate_key)(arg0_attributes, arg1_key); + return status; +} + +/* Wrapper for psa_generate_random */ +psa_status_t mbedtls_test_wrap_psa_generate_random( + uint8_t *arg0_output, + size_t arg1_output_size) +{ + psa_status_t status = (psa_generate_random)(arg0_output, arg1_output_size); + return status; +} + +/* Wrapper for psa_get_key_attributes */ +psa_status_t mbedtls_test_wrap_psa_get_key_attributes( + mbedtls_svc_key_id_t arg0_key, + psa_key_attributes_t *arg1_attributes) +{ + psa_status_t status = (psa_get_key_attributes)(arg0_key, arg1_attributes); + return status; +} + +/* Wrapper for psa_hash_abort */ +psa_status_t mbedtls_test_wrap_psa_hash_abort( + psa_hash_operation_t *arg0_operation) +{ + psa_status_t status = (psa_hash_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_hash_clone */ +psa_status_t mbedtls_test_wrap_psa_hash_clone( + const psa_hash_operation_t *arg0_source_operation, + psa_hash_operation_t *arg1_target_operation) +{ + psa_status_t status = (psa_hash_clone)(arg0_source_operation, arg1_target_operation); + return status; +} + +/* Wrapper for psa_hash_compare */ +psa_status_t mbedtls_test_wrap_psa_hash_compare( + psa_algorithm_t arg0_alg, + const uint8_t *arg1_input, + size_t arg2_input_length, + const uint8_t *arg3_hash, + size_t arg4_hash_length) +{ + psa_status_t status = (psa_hash_compare)(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_length); + return status; +} + +/* Wrapper for psa_hash_compute */ +psa_status_t mbedtls_test_wrap_psa_hash_compute( + psa_algorithm_t arg0_alg, + const uint8_t *arg1_input, + size_t arg2_input_length, + uint8_t *arg3_hash, + size_t arg4_hash_size, + size_t *arg5_hash_length) +{ + psa_status_t status = (psa_hash_compute)(arg0_alg, arg1_input, arg2_input_length, arg3_hash, arg4_hash_size, arg5_hash_length); + return status; +} + +/* Wrapper for psa_hash_finish */ +psa_status_t mbedtls_test_wrap_psa_hash_finish( + psa_hash_operation_t *arg0_operation, + uint8_t *arg1_hash, + size_t arg2_hash_size, + size_t *arg3_hash_length) +{ + psa_status_t status = (psa_hash_finish)(arg0_operation, arg1_hash, arg2_hash_size, arg3_hash_length); + return status; +} + +/* Wrapper for psa_hash_setup */ +psa_status_t mbedtls_test_wrap_psa_hash_setup( + psa_hash_operation_t *arg0_operation, + psa_algorithm_t arg1_alg) +{ + psa_status_t status = (psa_hash_setup)(arg0_operation, arg1_alg); + return status; +} + +/* Wrapper for psa_hash_update */ +psa_status_t mbedtls_test_wrap_psa_hash_update( + psa_hash_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length) +{ + psa_status_t status = (psa_hash_update)(arg0_operation, arg1_input, arg2_input_length); + return status; +} + +/* Wrapper for psa_hash_verify */ +psa_status_t mbedtls_test_wrap_psa_hash_verify( + psa_hash_operation_t *arg0_operation, + const uint8_t *arg1_hash, + size_t arg2_hash_length) +{ + psa_status_t status = (psa_hash_verify)(arg0_operation, arg1_hash, arg2_hash_length); + return status; +} + +/* Wrapper for psa_import_key */ +psa_status_t mbedtls_test_wrap_psa_import_key( + const psa_key_attributes_t *arg0_attributes, + const uint8_t *arg1_data, + size_t arg2_data_length, + mbedtls_svc_key_id_t *arg3_key) +{ + psa_status_t status = (psa_import_key)(arg0_attributes, arg1_data, arg2_data_length, arg3_key); + return status; +} + +/* Wrapper for psa_key_derivation_abort */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_abort( + psa_key_derivation_operation_t *arg0_operation) +{ + psa_status_t status = (psa_key_derivation_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_key_derivation_get_capacity */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_get_capacity( + const psa_key_derivation_operation_t *arg0_operation, + size_t *arg1_capacity) +{ + psa_status_t status = (psa_key_derivation_get_capacity)(arg0_operation, arg1_capacity); + return status; +} + +/* Wrapper for psa_key_derivation_input_bytes */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_bytes( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + const uint8_t *arg2_data, + size_t arg3_data_length) +{ + psa_status_t status = (psa_key_derivation_input_bytes)(arg0_operation, arg1_step, arg2_data, arg3_data_length); + return status; +} + +/* Wrapper for psa_key_derivation_input_integer */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_integer( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + uint64_t arg2_value) +{ + psa_status_t status = (psa_key_derivation_input_integer)(arg0_operation, arg1_step, arg2_value); + return status; +} + +/* Wrapper for psa_key_derivation_input_key */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_input_key( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + mbedtls_svc_key_id_t arg2_key) +{ + psa_status_t status = (psa_key_derivation_input_key)(arg0_operation, arg1_step, arg2_key); + return status; +} + +/* Wrapper for psa_key_derivation_key_agreement */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_key_agreement( + psa_key_derivation_operation_t *arg0_operation, + psa_key_derivation_step_t arg1_step, + mbedtls_svc_key_id_t arg2_private_key, + const uint8_t *arg3_peer_key, + size_t arg4_peer_key_length) +{ + psa_status_t status = (psa_key_derivation_key_agreement)(arg0_operation, arg1_step, arg2_private_key, arg3_peer_key, arg4_peer_key_length); + return status; +} + +/* Wrapper for psa_key_derivation_output_bytes */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_output_bytes( + psa_key_derivation_operation_t *arg0_operation, + uint8_t *arg1_output, + size_t arg2_output_length) +{ + psa_status_t status = (psa_key_derivation_output_bytes)(arg0_operation, arg1_output, arg2_output_length); + return status; +} + +/* Wrapper for psa_key_derivation_output_key */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_output_key( + const psa_key_attributes_t *arg0_attributes, + psa_key_derivation_operation_t *arg1_operation, + mbedtls_svc_key_id_t *arg2_key) +{ + psa_status_t status = (psa_key_derivation_output_key)(arg0_attributes, arg1_operation, arg2_key); + return status; +} + +/* Wrapper for psa_key_derivation_set_capacity */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_set_capacity( + psa_key_derivation_operation_t *arg0_operation, + size_t arg1_capacity) +{ + psa_status_t status = (psa_key_derivation_set_capacity)(arg0_operation, arg1_capacity); + return status; +} + +/* Wrapper for psa_key_derivation_setup */ +psa_status_t mbedtls_test_wrap_psa_key_derivation_setup( + psa_key_derivation_operation_t *arg0_operation, + psa_algorithm_t arg1_alg) +{ + psa_status_t status = (psa_key_derivation_setup)(arg0_operation, arg1_alg); + return status; +} + +/* Wrapper for psa_mac_abort */ +psa_status_t mbedtls_test_wrap_psa_mac_abort( + psa_mac_operation_t *arg0_operation) +{ + psa_status_t status = (psa_mac_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_mac_compute */ +psa_status_t mbedtls_test_wrap_psa_mac_compute( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_mac, + size_t arg5_mac_size, + size_t *arg6_mac_length) +{ + psa_status_t status = (psa_mac_compute)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_size, arg6_mac_length); + return status; +} + +/* Wrapper for psa_mac_sign_finish */ +psa_status_t mbedtls_test_wrap_psa_mac_sign_finish( + psa_mac_operation_t *arg0_operation, + uint8_t *arg1_mac, + size_t arg2_mac_size, + size_t *arg3_mac_length) +{ + psa_status_t status = (psa_mac_sign_finish)(arg0_operation, arg1_mac, arg2_mac_size, arg3_mac_length); + return status; +} + +/* Wrapper for psa_mac_sign_setup */ +psa_status_t mbedtls_test_wrap_psa_mac_sign_setup( + psa_mac_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_mac_sign_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_mac_update */ +psa_status_t mbedtls_test_wrap_psa_mac_update( + psa_mac_operation_t *arg0_operation, + const uint8_t *arg1_input, + size_t arg2_input_length) +{ + psa_status_t status = (psa_mac_update)(arg0_operation, arg1_input, arg2_input_length); + return status; +} + +/* Wrapper for psa_mac_verify */ +psa_status_t mbedtls_test_wrap_psa_mac_verify( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_mac, + size_t arg5_mac_length) +{ + psa_status_t status = (psa_mac_verify)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_mac, arg5_mac_length); + return status; +} + +/* Wrapper for psa_mac_verify_finish */ +psa_status_t mbedtls_test_wrap_psa_mac_verify_finish( + psa_mac_operation_t *arg0_operation, + const uint8_t *arg1_mac, + size_t arg2_mac_length) +{ + psa_status_t status = (psa_mac_verify_finish)(arg0_operation, arg1_mac, arg2_mac_length); + return status; +} + +/* Wrapper for psa_mac_verify_setup */ +psa_status_t mbedtls_test_wrap_psa_mac_verify_setup( + psa_mac_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg) +{ + psa_status_t status = (psa_mac_verify_setup)(arg0_operation, arg1_key, arg2_alg); + return status; +} + +/* Wrapper for psa_pake_abort */ +psa_status_t mbedtls_test_wrap_psa_pake_abort( + psa_pake_operation_t *arg0_operation) +{ + psa_status_t status = (psa_pake_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_pake_get_implicit_key */ +psa_status_t mbedtls_test_wrap_psa_pake_get_implicit_key( + psa_pake_operation_t *arg0_operation, + psa_key_derivation_operation_t *arg1_output) +{ + psa_status_t status = (psa_pake_get_implicit_key)(arg0_operation, arg1_output); + return status; +} + +/* Wrapper for psa_pake_input */ +psa_status_t mbedtls_test_wrap_psa_pake_input( + psa_pake_operation_t *arg0_operation, + psa_pake_step_t arg1_step, + const uint8_t *arg2_input, + size_t arg3_input_length) +{ + psa_status_t status = (psa_pake_input)(arg0_operation, arg1_step, arg2_input, arg3_input_length); + return status; +} + +/* Wrapper for psa_pake_output */ +psa_status_t mbedtls_test_wrap_psa_pake_output( + psa_pake_operation_t *arg0_operation, + psa_pake_step_t arg1_step, + uint8_t *arg2_output, + size_t arg3_output_size, + size_t *arg4_output_length) +{ + psa_status_t status = (psa_pake_output)(arg0_operation, arg1_step, arg2_output, arg3_output_size, arg4_output_length); + return status; +} + +/* Wrapper for psa_pake_set_password_key */ +psa_status_t mbedtls_test_wrap_psa_pake_set_password_key( + psa_pake_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_password) +{ + psa_status_t status = (psa_pake_set_password_key)(arg0_operation, arg1_password); + return status; +} + +/* Wrapper for psa_pake_set_peer */ +psa_status_t mbedtls_test_wrap_psa_pake_set_peer( + psa_pake_operation_t *arg0_operation, + const uint8_t *arg1_peer_id, + size_t arg2_peer_id_len) +{ + psa_status_t status = (psa_pake_set_peer)(arg0_operation, arg1_peer_id, arg2_peer_id_len); + return status; +} + +/* Wrapper for psa_pake_set_role */ +psa_status_t mbedtls_test_wrap_psa_pake_set_role( + psa_pake_operation_t *arg0_operation, + psa_pake_role_t arg1_role) +{ + psa_status_t status = (psa_pake_set_role)(arg0_operation, arg1_role); + return status; +} + +/* Wrapper for psa_pake_set_user */ +psa_status_t mbedtls_test_wrap_psa_pake_set_user( + psa_pake_operation_t *arg0_operation, + const uint8_t *arg1_user_id, + size_t arg2_user_id_len) +{ + psa_status_t status = (psa_pake_set_user)(arg0_operation, arg1_user_id, arg2_user_id_len); + return status; +} + +/* Wrapper for psa_pake_setup */ +psa_status_t mbedtls_test_wrap_psa_pake_setup( + psa_pake_operation_t *arg0_operation, + const psa_pake_cipher_suite_t *arg1_cipher_suite) +{ + psa_status_t status = (psa_pake_setup)(arg0_operation, arg1_cipher_suite); + return status; +} + +/* Wrapper for psa_purge_key */ +psa_status_t mbedtls_test_wrap_psa_purge_key( + mbedtls_svc_key_id_t arg0_key) +{ + psa_status_t status = (psa_purge_key)(arg0_key); + return status; +} + +/* Wrapper for psa_raw_key_agreement */ +psa_status_t mbedtls_test_wrap_psa_raw_key_agreement( + psa_algorithm_t arg0_alg, + mbedtls_svc_key_id_t arg1_private_key, + const uint8_t *arg2_peer_key, + size_t arg3_peer_key_length, + uint8_t *arg4_output, + size_t arg5_output_size, + size_t *arg6_output_length) +{ + psa_status_t status = (psa_raw_key_agreement)(arg0_alg, arg1_private_key, arg2_peer_key, arg3_peer_key_length, arg4_output, arg5_output_size, arg6_output_length); + return status; +} + +/* Wrapper for psa_sign_hash */ +psa_status_t mbedtls_test_wrap_psa_sign_hash( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_hash, + size_t arg3_hash_length, + uint8_t *arg4_signature, + size_t arg5_signature_size, + size_t *arg6_signature_length) +{ + psa_status_t status = (psa_sign_hash)(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_size, arg6_signature_length); + return status; +} + +/* Wrapper for psa_sign_hash_abort */ +psa_status_t mbedtls_test_wrap_psa_sign_hash_abort( + psa_sign_hash_interruptible_operation_t *arg0_operation) +{ + psa_status_t status = (psa_sign_hash_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_sign_hash_complete */ +psa_status_t mbedtls_test_wrap_psa_sign_hash_complete( + psa_sign_hash_interruptible_operation_t *arg0_operation, + uint8_t *arg1_signature, + size_t arg2_signature_size, + size_t *arg3_signature_length) +{ + psa_status_t status = (psa_sign_hash_complete)(arg0_operation, arg1_signature, arg2_signature_size, arg3_signature_length); + return status; +} + +/* Wrapper for psa_sign_hash_start */ +psa_status_t mbedtls_test_wrap_psa_sign_hash_start( + psa_sign_hash_interruptible_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg, + const uint8_t *arg3_hash, + size_t arg4_hash_length) +{ + psa_status_t status = (psa_sign_hash_start)(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length); + return status; +} + +/* Wrapper for psa_sign_message */ +psa_status_t mbedtls_test_wrap_psa_sign_message( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + uint8_t *arg4_signature, + size_t arg5_signature_size, + size_t *arg6_signature_length) +{ + psa_status_t status = (psa_sign_message)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_size, arg6_signature_length); + return status; +} + +/* Wrapper for psa_verify_hash */ +psa_status_t mbedtls_test_wrap_psa_verify_hash( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_hash, + size_t arg3_hash_length, + const uint8_t *arg4_signature, + size_t arg5_signature_length) +{ + psa_status_t status = (psa_verify_hash)(arg0_key, arg1_alg, arg2_hash, arg3_hash_length, arg4_signature, arg5_signature_length); + return status; +} + +/* Wrapper for psa_verify_hash_abort */ +psa_status_t mbedtls_test_wrap_psa_verify_hash_abort( + psa_verify_hash_interruptible_operation_t *arg0_operation) +{ + psa_status_t status = (psa_verify_hash_abort)(arg0_operation); + return status; +} + +/* Wrapper for psa_verify_hash_complete */ +psa_status_t mbedtls_test_wrap_psa_verify_hash_complete( + psa_verify_hash_interruptible_operation_t *arg0_operation) +{ + psa_status_t status = (psa_verify_hash_complete)(arg0_operation); + return status; +} + +/* Wrapper for psa_verify_hash_start */ +psa_status_t mbedtls_test_wrap_psa_verify_hash_start( + psa_verify_hash_interruptible_operation_t *arg0_operation, + mbedtls_svc_key_id_t arg1_key, + psa_algorithm_t arg2_alg, + const uint8_t *arg3_hash, + size_t arg4_hash_length, + const uint8_t *arg5_signature, + size_t arg6_signature_length) +{ + psa_status_t status = (psa_verify_hash_start)(arg0_operation, arg1_key, arg2_alg, arg3_hash, arg4_hash_length, arg5_signature, arg6_signature_length); + return status; +} + +/* Wrapper for psa_verify_message */ +psa_status_t mbedtls_test_wrap_psa_verify_message( + mbedtls_svc_key_id_t arg0_key, + psa_algorithm_t arg1_alg, + const uint8_t *arg2_input, + size_t arg3_input_length, + const uint8_t *arg4_signature, + size_t arg5_signature_length) +{ + psa_status_t status = (psa_verify_message)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_length); + return status; +} + +#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) */ + +/* End of automatically generated file. */ From e00150df4a364ba7243b0842acd35602ef77eb13 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 16:46:00 +0100 Subject: [PATCH 172/184] Declare the outputs from generate_psa_wrappers.py as generated files Signed-off-by: Gilles Peskine --- tests/scripts/check-generated-files.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tests/scripts/check-generated-files.sh b/tests/scripts/check-generated-files.sh index 67dedeb265..0a1c47748c 100755 --- a/tests/scripts/check-generated-files.sh +++ b/tests/scripts/check-generated-files.sh @@ -127,3 +127,8 @@ check scripts/generate_psa_constants.py programs/psa/psa_constant_names_generate check tests/scripts/generate_bignum_tests.py $(tests/scripts/generate_bignum_tests.py --list) check tests/scripts/generate_ecp_tests.py $(tests/scripts/generate_ecp_tests.py --list) check tests/scripts/generate_psa_tests.py $(tests/scripts/generate_psa_tests.py --list) + +# Generated files that are present in the repository even in the development +# branch. (This is intended to be temporary, until the generator scripts are +# fully reviewed and the build scripts support a generated header file.) +check tests/scripts/generate_psa_wrappers.py tests/include/test/psa_test_wrappers.h tests/src/psa_test_wrappers.c From 2b106dec8be1b56db1e06934fc2af6c1d5ea7963 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 16:44:16 +0100 Subject: [PATCH 173/184] Enable generated PSA wrappers Code in unit tests (`tests/suites/*.function`) and in test support code (`tests/src/**.c`) will now go through the wrapper functions when they call a PSA API function and `MBEDTLS_TEST_HOOKS` is enabled. Signed-off-by: Gilles Peskine --- tests/include/test/psa_crypto_helpers.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/include/test/psa_crypto_helpers.h b/tests/include/test/psa_crypto_helpers.h index 41b7752bea..96a8c1c82e 100644 --- a/tests/include/test/psa_crypto_helpers.h +++ b/tests/include/test/psa_crypto_helpers.h @@ -16,6 +16,8 @@ #include #endif +#include "test/psa_test_wrappers.h" + #if defined(MBEDTLS_TEST_HOOKS) && defined(MBEDTLS_PSA_CRYPTO_C) \ && defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) #include "test/psa_memory_poisoning_wrappers.h" From 4adacac142e42fb0e418a524e84f1e13f58914d0 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Wed, 6 Dec 2023 19:32:52 +0100 Subject: [PATCH 174/184] Generated PSA wrappers: poison/unpoison buffer parameters For now, only instrument the one function for which buffer copying has been implemented, namely `psa_cipher_encrypt`. Signed-off-by: Gilles Peskine --- tests/scripts/generate_psa_wrappers.py | 78 +++++++++++++++++++++++++- 1 file changed, 77 insertions(+), 1 deletion(-) diff --git a/tests/scripts/generate_psa_wrappers.py b/tests/scripts/generate_psa_wrappers.py index 9bb6eb758b..3fa4135cb0 100755 --- a/tests/scripts/generate_psa_wrappers.py +++ b/tests/scripts/generate_psa_wrappers.py @@ -6,8 +6,9 @@ # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later import argparse +import itertools import os -from typing import List, Tuple +from typing import Iterator, List, Optional, Tuple import scripts_path #pylint: disable=unused-import from mbedtls_dev import build_tree @@ -16,6 +17,27 @@ from mbedtls_dev import c_wrapper_generator from mbedtls_dev import typing_util +class BufferParameter: + """Description of an input or output buffer parameter sequence to a PSA function.""" + #pylint: disable=too-few-public-methods + + def __init__(self, i: int, is_output: bool, + buffer_name: str, size_name: str) -> None: + """Initialize the parameter information. + + i is the index of the function argument that is the pointer to the buffer. + The size is argument i+1. For a variable-size output, the actual length + goes in argument i+2. + + buffer_name and size_names are the names of arguments i and i+1. + This class does not yet help with the output length. + """ + self.index = i + self.buffer_name = buffer_name + self.size_name = size_name + self.is_output = is_output + + class PSAWrapperGenerator(c_wrapper_generator.Base): """Generate a C source file containing wrapper functions for PSA Crypto API calls.""" @@ -67,6 +89,59 @@ class PSAWrapperGenerator(c_wrapper_generator.Base): 'mbedtls_psa_platform_get_builtin_key': 'defined(MBEDTLS_PSA_CRYPTO_BUILTIN_KEYS)', }) + @staticmethod + def _detect_buffer_parameters(arguments: List[c_parsing_helper.ArgumentInfo], + argument_names: List[str]) -> Iterator[BufferParameter]: + """Detect function arguments that are buffers (pointer, size [,length]).""" + types = ['' if arg.suffix else arg.type for arg in arguments] + # pairs = list of (type_of_arg_N, type_of_arg_N+1) + # where each type_of_arg_X is the empty string if the type is an array + # or there is no argument X. + pairs = enumerate(itertools.zip_longest(types, types[1:], fillvalue='')) + for i, t01 in pairs: + if (t01[0] == 'const uint8_t *' or t01[0] == 'uint8_t *') and \ + t01[1] == 'size_t': + yield BufferParameter(i, not t01[0].startswith('const '), + argument_names[i], argument_names[i+1]) + + @staticmethod + def _write_poison_buffer_parameter(out: typing_util.Writable, + param: BufferParameter, + poison: bool) -> None: + """Write poisoning or unpoisoning code for a buffer parameter. + + Write poisoning code if poison is true, unpoisoning code otherwise. + """ + out.write(' MBEDTLS_TEST_MEMORY_{}({}, {});\n'.format( + 'POISON' if poison else 'UNPOISON', + param.buffer_name, param.size_name + )) + + @staticmethod + def _parameter_should_be_copied(function_name: str, + _buffer_name: Optional[str]) -> bool: + """Whether the specified buffer argument to a PSA function should be copied. + """ + # Proof-of-concept: just instrument one function for now + if function_name == 'psa_cipher_encrypt': + return True + return False + + def _write_function_call(self, out: typing_util.Writable, + function: c_wrapper_generator.FunctionInfo, + argument_names: List[str]) -> None: + buffer_parameters = list( + param + for param in self._detect_buffer_parameters(function.arguments, + argument_names) + if self._parameter_should_be_copied(function.name, + function.arguments[param.index].name)) + for param in buffer_parameters: + self._write_poison_buffer_parameter(out, param, True) + super()._write_function_call(out, function, argument_names) + for param in buffer_parameters: + self._write_poison_buffer_parameter(out, param, False) + def _write_prologue(self, out: typing_util.Writable, header: bool) -> None: super()._write_prologue(out, header) out.write(""" @@ -74,6 +149,7 @@ class PSAWrapperGenerator(c_wrapper_generator.Base): #include +#include #include #include """ From 90d14d7fc28b939e908148d580514eda84d041fd Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 16:59:28 +0100 Subject: [PATCH 175/184] Update generated PSA wrappers Signed-off-by: Gilles Peskine --- tests/include/test/psa_test_wrappers.h | 1 + tests/src/psa_test_wrappers.c | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/tests/include/test/psa_test_wrappers.h b/tests/include/test/psa_test_wrappers.h index 18417800ce..bd673b80ba 100644 --- a/tests/include/test/psa_test_wrappers.h +++ b/tests/include/test/psa_test_wrappers.h @@ -17,6 +17,7 @@ extern "C" { #include +#include #include #include diff --git a/tests/src/psa_test_wrappers.c b/tests/src/psa_test_wrappers.c index f25f1aa9b2..c26835fbfa 100644 --- a/tests/src/psa_test_wrappers.c +++ b/tests/src/psa_test_wrappers.c @@ -10,6 +10,7 @@ #include +#include #include #include @@ -265,7 +266,11 @@ psa_status_t mbedtls_test_wrap_psa_cipher_encrypt( size_t arg5_output_size, size_t *arg6_output_length) { + MBEDTLS_TEST_MEMORY_POISON(arg2_input, arg3_input_length); + MBEDTLS_TEST_MEMORY_POISON(arg4_output, arg5_output_size); psa_status_t status = (psa_cipher_encrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length); + MBEDTLS_TEST_MEMORY_UNPOISON(arg2_input, arg3_input_length); + MBEDTLS_TEST_MEMORY_UNPOISON(arg4_output, arg5_output_size); return status; } From 8e7960b685336eba7794f00fd86edd1566263042 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 17:11:54 +0100 Subject: [PATCH 176/184] Remove the manually written poisoning wrapper This fixes the build with ASan + MBEDTLS_TEST_HOOKS. Signed-off-by: Gilles Peskine --- .../test/psa_memory_poisoning_wrappers.h | 17 +++++--------- tests/src/psa_memory_poisoning_wrappers.c | 22 ------------------- 2 files changed, 5 insertions(+), 34 deletions(-) diff --git a/tests/include/test/psa_memory_poisoning_wrappers.h b/tests/include/test/psa_memory_poisoning_wrappers.h index 0052c2fae0..3f30b65c04 100644 --- a/tests/include/test/psa_memory_poisoning_wrappers.h +++ b/tests/include/test/psa_memory_poisoning_wrappers.h @@ -1,8 +1,11 @@ -/** Memory poisoning wrappers for PSA functions. +/** Support for memory poisoning wrappers for PSA functions. * - * These wrappers poison the input and output buffers of each function + * The wrappers poison the input and output buffers of each function * before calling it, to ensure that it does not access the buffers * except by calling the approved buffer-copying functions. + * + * This header declares support functions. The wrappers themselves are + * decalred in the automatically generated file `test/psa_test_wrappers.h`. */ /* * Copyright The Mbed TLS Contributors @@ -32,16 +35,6 @@ void mbedtls_poison_test_hooks_setup(void); */ void mbedtls_poison_test_hooks_teardown(void); -psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *output, - size_t output_size, - size_t *output_length); - -#define psa_cipher_encrypt(...) wrap_psa_cipher_encrypt(__VA_ARGS__) - #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_TEST_MEMORY_CAN_POISON */ #endif /* PSA_MEMORY_POISONING_WRAPPERS_H */ diff --git a/tests/src/psa_memory_poisoning_wrappers.c b/tests/src/psa_memory_poisoning_wrappers.c index a53e875d4b..05cba18ee7 100644 --- a/tests/src/psa_memory_poisoning_wrappers.c +++ b/tests/src/psa_memory_poisoning_wrappers.c @@ -27,27 +27,5 @@ void mbedtls_poison_test_hooks_teardown(void) psa_output_post_copy_hook = NULL; } -psa_status_t wrap_psa_cipher_encrypt(mbedtls_svc_key_id_t key, - psa_algorithm_t alg, - const uint8_t *input, - size_t input_length, - uint8_t *output, - size_t output_size, - size_t *output_length) -{ - MBEDTLS_TEST_MEMORY_POISON(input, input_length); - MBEDTLS_TEST_MEMORY_POISON(output, output_size); - psa_status_t status = psa_cipher_encrypt(key, - alg, - input, - input_length, - output, - output_size, - output_length); - MBEDTLS_TEST_MEMORY_UNPOISON(input, input_length); - MBEDTLS_TEST_MEMORY_UNPOISON(output, output_size); - return status; -} - #endif /* MBEDTLS_TEST_HOOKS && MBEDTLS_PSA_CRYPTO_C && MBEDTLS_TEST_MEMORY_CAN_POISON */ From a1871f318bb4c4a36672f78159dacd0e917344b3 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 17:28:59 +0100 Subject: [PATCH 177/184] Add review exception warning Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/c_parsing_helper.py | 4 ++++ scripts/mbedtls_dev/c_wrapper_generator.py | 4 ++++ tests/scripts/generate_psa_wrappers.py | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/scripts/mbedtls_dev/c_parsing_helper.py b/scripts/mbedtls_dev/c_parsing_helper.py index 3bb6f0405e..72214d51c9 100644 --- a/scripts/mbedtls_dev/c_parsing_helper.py +++ b/scripts/mbedtls_dev/c_parsing_helper.py @@ -8,6 +8,10 @@ Currently supported functionality: # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +### WARNING: the code in this file has not been extensively reviewed yet. +### We do not think it is harmful, but it may be below our normal standards +### for robustness and maintainability. + import re from typing import Dict, Iterable, Iterator, List, Optional, Tuple diff --git a/scripts/mbedtls_dev/c_wrapper_generator.py b/scripts/mbedtls_dev/c_wrapper_generator.py index de99ddb9ae..3cf1e05ebb 100644 --- a/scripts/mbedtls_dev/c_wrapper_generator.py +++ b/scripts/mbedtls_dev/c_wrapper_generator.py @@ -4,6 +4,10 @@ # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +### WARNING: the code in this file has not been extensively reviewed yet. +### We do not think it is harmful, but it may be below our normal standards +### for robustness and maintainability. + import os import re import sys diff --git a/tests/scripts/generate_psa_wrappers.py b/tests/scripts/generate_psa_wrappers.py index 3fa4135cb0..551acbd23f 100755 --- a/tests/scripts/generate_psa_wrappers.py +++ b/tests/scripts/generate_psa_wrappers.py @@ -5,6 +5,10 @@ # Copyright The Mbed TLS Contributors # SPDX-License-Identifier: Apache-2.0 OR GPL-2.0-or-later +### WARNING: the code in this file has not been extensively reviewed yet. +### We do not think it is harmful, but it may be below our normal standards +### for robustness and maintainability. + import argparse import itertools import os From 88385c2f74d46c26abb10e5d80ec16d7a257aefa Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 20:33:29 +0100 Subject: [PATCH 178/184] PSA wrappers: don't poison buffers when buffer copying is disabled Signed-off-by: Gilles Peskine --- tests/scripts/generate_psa_wrappers.py | 20 ++++++++++++++++---- tests/src/psa_test_wrappers.c | 4 ++++ 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/tests/scripts/generate_psa_wrappers.py b/tests/scripts/generate_psa_wrappers.py index 551acbd23f..85c9278552 100755 --- a/tests/scripts/generate_psa_wrappers.py +++ b/tests/scripts/generate_psa_wrappers.py @@ -121,6 +121,20 @@ class PSAWrapperGenerator(c_wrapper_generator.Base): param.buffer_name, param.size_name )) + def _write_poison_buffer_parameters(self, out: typing_util.Writable, + buffer_parameters: List[BufferParameter], + poison: bool) -> None: + """Write poisoning or unpoisoning code for the buffer parameters. + + Write poisoning code if poison is true, unpoisoning code otherwise. + """ + if not buffer_parameters: + return + out.write('#if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS)\n') + for param in buffer_parameters: + self._write_poison_buffer_parameter(out, param, poison) + out.write('#endif /* defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) */\n') + @staticmethod def _parameter_should_be_copied(function_name: str, _buffer_name: Optional[str]) -> bool: @@ -140,11 +154,9 @@ class PSAWrapperGenerator(c_wrapper_generator.Base): argument_names) if self._parameter_should_be_copied(function.name, function.arguments[param.index].name)) - for param in buffer_parameters: - self._write_poison_buffer_parameter(out, param, True) + self._write_poison_buffer_parameters(out, buffer_parameters, True) super()._write_function_call(out, function, argument_names) - for param in buffer_parameters: - self._write_poison_buffer_parameter(out, param, False) + self._write_poison_buffer_parameters(out, buffer_parameters, False) def _write_prologue(self, out: typing_util.Writable, header: bool) -> None: super()._write_prologue(out, header) diff --git a/tests/src/psa_test_wrappers.c b/tests/src/psa_test_wrappers.c index c26835fbfa..f3fb852e16 100644 --- a/tests/src/psa_test_wrappers.c +++ b/tests/src/psa_test_wrappers.c @@ -266,11 +266,15 @@ psa_status_t mbedtls_test_wrap_psa_cipher_encrypt( size_t arg5_output_size, size_t *arg6_output_length) { +#if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) MBEDTLS_TEST_MEMORY_POISON(arg2_input, arg3_input_length); MBEDTLS_TEST_MEMORY_POISON(arg4_output, arg5_output_size); +#endif /* defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) */ psa_status_t status = (psa_cipher_encrypt)(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_output, arg5_output_size, arg6_output_length); +#if defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) MBEDTLS_TEST_MEMORY_UNPOISON(arg2_input, arg3_input_length); MBEDTLS_TEST_MEMORY_UNPOISON(arg4_output, arg5_output_size); +#endif /* defined(MBEDTLS_PSA_COPY_CALLER_BUFFERS) */ return status; } From 4411c9c1f8d3c0fb5ca7b743c9eb032c3f97370d Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Thu, 4 Jan 2024 20:51:38 +0100 Subject: [PATCH 179/184] Disable PSA wrappers psa_collect_statuses builds `psa_collect_statuses.py` runs `make RECORD_PSA_STATUS_COVERAGE_LOG=1`, which builds with `RECORD_PSA_STATUS_COVERAGE_LOG`. In this mode, the build includes wrappers for PSA functions, which conflict with the newly introduced wrappers that are enabled whenever `MBEDTLS_TEST_HOOKS` is enabled. In the future, the collect-statuses mechanism should use the new generic wrapper mechanism. For the time being, keep the old wrappers and avoid the new wrappers when doing the collect-statuses build. Signed-off-by: Gilles Peskine --- programs/Makefile | 4 ++++ tests/include/test/psa_test_wrappers.h | 6 ++++-- tests/scripts/generate_psa_wrappers.py | 4 +++- tests/src/psa_test_wrappers.c | 6 ++++-- 4 files changed, 15 insertions(+), 5 deletions(-) diff --git a/programs/Makefile b/programs/Makefile index a3fa81679f..c16199208c 100644 --- a/programs/Makefile +++ b/programs/Makefile @@ -26,6 +26,10 @@ endif include ../3rdparty/Makefile.inc LOCAL_CFLAGS+=$(THIRDPARTY_INCLUDES) +ifdef RECORD_PSA_STATUS_COVERAGE_LOG +LOCAL_CFLAGS += -Werror -DRECORD_PSA_STATUS_COVERAGE_LOG +endif + ifndef SHARED MBEDLIBS=../library/libmbedcrypto.a ../library/libmbedx509.a ../library/libmbedtls.a else diff --git a/tests/include/test/psa_test_wrappers.h b/tests/include/test/psa_test_wrappers.h index bd673b80ba..86ead0a126 100644 --- a/tests/include/test/psa_test_wrappers.h +++ b/tests/include/test/psa_test_wrappers.h @@ -13,7 +13,8 @@ extern "C" { #include -#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) && \ + !defined(RECORD_PSA_STATUS_COVERAGE_LOG) #include @@ -709,7 +710,8 @@ psa_status_t mbedtls_test_wrap_psa_verify_message( #define psa_verify_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_length) \ mbedtls_test_wrap_psa_verify_message(arg0_key, arg1_alg, arg2_input, arg3_input_length, arg4_signature, arg5_signature_length) -#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) */ +#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) && \ + !defined(RECORD_PSA_STATUS_COVERAGE_LOG) */ #ifdef __cplusplus } diff --git a/tests/scripts/generate_psa_wrappers.py b/tests/scripts/generate_psa_wrappers.py index 85c9278552..e5b4256f5e 100755 --- a/tests/scripts/generate_psa_wrappers.py +++ b/tests/scripts/generate_psa_wrappers.py @@ -45,7 +45,9 @@ class BufferParameter: class PSAWrapperGenerator(c_wrapper_generator.Base): """Generate a C source file containing wrapper functions for PSA Crypto API calls.""" - _CPP_GUARDS = 'defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS)' + _CPP_GUARDS = ('defined(MBEDTLS_PSA_CRYPTO_C) && ' + + 'defined(MBEDTLS_TEST_HOOKS) && \\\n ' + + '!defined(RECORD_PSA_STATUS_COVERAGE_LOG)') _WRAPPER_NAME_PREFIX = 'mbedtls_test_wrap_' _WRAPPER_NAME_SUFFIX = '' diff --git a/tests/src/psa_test_wrappers.c b/tests/src/psa_test_wrappers.c index f3fb852e16..3a3aaade9a 100644 --- a/tests/src/psa_test_wrappers.c +++ b/tests/src/psa_test_wrappers.c @@ -6,7 +6,8 @@ #include -#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) +#if defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) && \ + !defined(RECORD_PSA_STATUS_COVERAGE_LOG) #include @@ -977,6 +978,7 @@ psa_status_t mbedtls_test_wrap_psa_verify_message( return status; } -#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) */ +#endif /* defined(MBEDTLS_PSA_CRYPTO_C) && defined(MBEDTLS_TEST_HOOKS) && \ + !defined(RECORD_PSA_STATUS_COVERAGE_LOG) */ /* End of automatically generated file. */ From 89b50a7cb4b488f8045667b2e64002e89a228621 Mon Sep 17 00:00:00 2001 From: Gilles Peskine Date: Mon, 8 Jan 2024 21:05:42 +0100 Subject: [PATCH 180/184] Fix parsing of C line comments Fix // comments stopping on 'n' instead of newlines. Also allow backslash-newline in // comments. Signed-off-by: Gilles Peskine --- scripts/mbedtls_dev/c_parsing_helper.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/mbedtls_dev/c_parsing_helper.py b/scripts/mbedtls_dev/c_parsing_helper.py index 72214d51c9..2657b7d230 100644 --- a/scripts/mbedtls_dev/c_parsing_helper.py +++ b/scripts/mbedtls_dev/c_parsing_helper.py @@ -76,7 +76,7 @@ class FunctionInfo: # Match one C comment. # Note that we match both comment types, so things like // in a /*...*/ # comment are handled correctly. -_C_COMMENT_RE = re.compile(r'//[^n]*|/\*.*?\*/', re.S) +_C_COMMENT_RE = re.compile(r'//(?:[^\n]|\\\n)*|/\*.*?\*/', re.S) _NOT_NEWLINES_RE = re.compile(r'[^\n]+') def read_logical_lines(filename: str) -> Iterator[Tuple[int, str]]: From c2ab398d01cf7bec9fda5796c08cc3043c71a570 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 17 Jan 2024 12:25:19 +0000 Subject: [PATCH 181/184] Request C11 in CMake (but only for tests) Set the C_STANDARD property on the mbedtls_test target to 11. This requests C11 for the tests only. If C11 is not supported the build will not fail, since C_STANDARD_REQUIRED is not set, and memory poisoning will be disabled by a preprocessor check on __STDC_VERSION__. Additionally, reintroduce previous C99 enforcement on the rest of the library. Signed-off-by: David Horstmann --- CMakeLists.txt | 5 +++++ programs/test/CMakeLists.txt | 3 +++ tests/CMakeLists.txt | 2 ++ 3 files changed, 10 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 210cc38f34..0cb208e988 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -177,6 +177,9 @@ string(REGEX MATCH "Clang" CMAKE_COMPILER_IS_CLANG "${CMAKE_C_COMPILER_ID}") include(CheckCCompilerFlag) +set(CMAKE_C_EXTENSIONS OFF) +set(CMAKE_C_STANDARD 99) + if(CMAKE_COMPILER_IS_GNU) # some warnings we want are not available with old GCC versions # note: starting with CMake 2.8 we could use CMAKE_C_COMPILER_VERSION @@ -294,6 +297,8 @@ if(ENABLE_TESTING OR ENABLE_PROGRAMS) PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/tests/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/library) + # Request C11, needed for memory poisoning tests + set_target_properties(mbedtls_test PROPERTIES C_STANDARD 11) file(GLOB MBEDTLS_TEST_HELPER_FILES ${CMAKE_CURRENT_SOURCE_DIR}/tests/src/test_helpers/*.c) diff --git a/programs/test/CMakeLists.txt b/programs/test/CMakeLists.txt index 0778731125..5a26821b51 100644 --- a/programs/test/CMakeLists.txt +++ b/programs/test/CMakeLists.txt @@ -78,6 +78,9 @@ foreach(exe IN LISTS executables_libs executables_mbedcrypto) target_include_directories(${exe} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) endif() + # Request C11, required for memory poisoning + set_target_properties(${exe} PROPERTIES C_STANDARD 11) + # This emulates "if ( ... IN_LIST ... )" which becomes available in CMake 3.3 list(FIND executables_libs ${exe} exe_index) if (${exe_index} GREATER -1) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0869aaa018..188d5d595f 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -251,6 +251,8 @@ function(add_test_suite suite_name) target_include_directories(test_suite_${data_name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/include PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/../library) + # Request C11, which is needed for memory poisoning tests + set_target_properties(test_suite_${data_name} PROPERTIES C_STANDARD 11) if(${data_name} MATCHES ${SKIP_TEST_SUITES_REGEX}) message(STATUS "The test suite ${data_name} will not be executed.") From fad038c5015b4667fd343361702b6ff675a132b1 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 17 Jan 2024 14:23:20 +0000 Subject: [PATCH 182/184] Change memory poisoning flag to a count This allows unusually-nested memory poisoning to work correctly, since it keeps track of whether any buffers are still poisoned, rather than just disabling poisoning at the first call to the UNPOISON() macro. Signed-off-by: David Horstmann --- tests/include/test/memory.h | 6 +++--- tests/src/test_memory.c | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index e0437517da..181280f265 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -69,7 +69,7 @@ * unset in the test wrappers so that calls to PSA functions from the library * do not poison memory. */ -extern _Thread_local int mbedtls_test_memory_poisoning_enabled; +extern _Thread_local unsigned int mbedtls_test_memory_poisoning_count; /** Poison a memory area so that any attempt to read or write from it will * cause a runtime failure. @@ -79,7 +79,7 @@ extern _Thread_local int mbedtls_test_memory_poisoning_enabled; void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size); #define MBEDTLS_TEST_MEMORY_POISON(ptr, size) \ do { \ - mbedtls_test_memory_poisoning_enabled = 1; \ + mbedtls_test_memory_poisoning_count++; \ mbedtls_test_memory_poison(ptr, size); \ } while (0) @@ -94,7 +94,7 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); #define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) \ do { \ mbedtls_test_memory_unpoison(ptr, size); \ - mbedtls_test_memory_poisoning_enabled = 0; \ + mbedtls_test_memory_poisoning_count--; \ } while (0) #else /* MBEDTLS_TEST_MEMORY_CAN_POISON */ diff --git a/tests/src/test_memory.c b/tests/src/test_memory.c index dbb7b20de2..ac9dde6163 100644 --- a/tests/src/test_memory.c +++ b/tests/src/test_memory.c @@ -20,7 +20,7 @@ #if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) -_Thread_local int mbedtls_test_memory_poisoning_enabled = 0; +_Thread_local unsigned int mbedtls_test_memory_poisoning_count = 0; static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) { @@ -39,7 +39,7 @@ static void align_for_asan(const unsigned char **p_ptr, size_t *p_size) void mbedtls_test_memory_poison(const unsigned char *ptr, size_t size) { - if (!mbedtls_test_memory_poisoning_enabled) { + if (mbedtls_test_memory_poisoning_count == 0) { return; } if (size == 0) { From 9de6edd46246dbf2e42f449ad3a6fa37d1b6155e Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 17 Jan 2024 14:53:08 +0000 Subject: [PATCH 183/184] Separate memory poisoning tests from generic ASan Some platforms may support ASan but be C99-only (no C11 support). These platforms will support ASan metatests but not memory poisoning, which requires C11 features. To allow for this, create a separate platform requirement, "poison", in metatest.c to distinguish generic ASan metatests from ones that require suppport for memory poisoning. In practice our platforms support both, so run "poison" tests in the same all.sh components where we run "asan" ones. Signed-off-by: David Horstmann --- programs/test/metatest.c | 34 ++++++++++++++++------------------ tests/scripts/all.sh | 4 ++-- 2 files changed, 18 insertions(+), 20 deletions(-) diff --git a/programs/test/metatest.c b/programs/test/metatest.c index bdcb1df9af..79c57d1746 100644 --- a/programs/test/metatest.c +++ b/programs/test/metatest.c @@ -362,24 +362,22 @@ metatest_t metatests[] = { { "double_free", "asan", double_free }, { "read_uninitialized_stack", "msan", read_uninitialized_stack }, { "memory_leak", "asan", memory_leak }, -#if defined(MBEDTLS_TEST_MEMORY_CAN_POISON) - { "test_memory_poison_0_0_8_r", "asan", test_memory_poison }, - { "test_memory_poison_0_0_8_w", "asan", test_memory_poison }, - { "test_memory_poison_0_7_8_r", "asan", test_memory_poison }, - { "test_memory_poison_0_7_8_w", "asan", test_memory_poison }, - { "test_memory_poison_0_0_1_r", "asan", test_memory_poison }, - { "test_memory_poison_0_0_1_w", "asan", test_memory_poison }, - { "test_memory_poison_0_1_2_r", "asan", test_memory_poison }, - { "test_memory_poison_0_1_2_w", "asan", test_memory_poison }, - { "test_memory_poison_7_0_8_r", "asan", test_memory_poison }, - { "test_memory_poison_7_0_8_w", "asan", test_memory_poison }, - { "test_memory_poison_7_7_8_r", "asan", test_memory_poison }, - { "test_memory_poison_7_7_8_w", "asan", test_memory_poison }, - { "test_memory_poison_7_0_1_r", "asan", test_memory_poison }, - { "test_memory_poison_7_0_1_w", "asan", test_memory_poison }, - { "test_memory_poison_7_1_2_r", "asan", test_memory_poison }, - { "test_memory_poison_7_1_2_w", "asan", test_memory_poison }, -#endif /* MBEDTLS_TEST_MEMORY_CAN_POISON */ + { "test_memory_poison_0_0_8_r", "poison", test_memory_poison }, + { "test_memory_poison_0_0_8_w", "poison", test_memory_poison }, + { "test_memory_poison_0_7_8_r", "poison", test_memory_poison }, + { "test_memory_poison_0_7_8_w", "poison", test_memory_poison }, + { "test_memory_poison_0_0_1_r", "poison", test_memory_poison }, + { "test_memory_poison_0_0_1_w", "poison", test_memory_poison }, + { "test_memory_poison_0_1_2_r", "poison", test_memory_poison }, + { "test_memory_poison_0_1_2_w", "poison", test_memory_poison }, + { "test_memory_poison_7_0_8_r", "poison", test_memory_poison }, + { "test_memory_poison_7_0_8_w", "poison", test_memory_poison }, + { "test_memory_poison_7_7_8_r", "poison", test_memory_poison }, + { "test_memory_poison_7_7_8_w", "poison", test_memory_poison }, + { "test_memory_poison_7_0_1_r", "poison", test_memory_poison }, + { "test_memory_poison_7_0_1_w", "poison", test_memory_poison }, + { "test_memory_poison_7_1_2_r", "poison", test_memory_poison }, + { "test_memory_poison_7_1_2_w", "poison", test_memory_poison }, { "mutex_lock_not_initialized", "pthread", mutex_lock_not_initialized }, { "mutex_unlock_not_initialized", "pthread", mutex_unlock_not_initialized }, { "mutex_free_not_initialized", "pthread", mutex_free_not_initialized }, diff --git a/tests/scripts/all.sh b/tests/scripts/all.sh index 4465d05068..0daab0918d 100755 --- a/tests/scripts/all.sh +++ b/tests/scripts/all.sh @@ -1125,7 +1125,7 @@ component_test_default_cmake_gcc_asan () { programs/test/selftest msg "test: metatests (GCC, ASan build)" - tests/scripts/run-metatests.sh any asan + tests/scripts/run-metatests.sh any asan poison msg "test: ssl-opt.sh (ASan build)" # ~ 1 min tests/ssl-opt.sh @@ -1944,7 +1944,7 @@ component_test_everest () { make test msg "test: metatests (clang, ASan)" - tests/scripts/run-metatests.sh any asan + tests/scripts/run-metatests.sh any asan poison msg "test: Everest ECDH context - ECDH-related part of ssl-opt.sh (ASan build)" # ~ 5s tests/ssl-opt.sh -f ECDH From d3efb92922e4e897563b028974ee40818fdeee99 Mon Sep 17 00:00:00 2001 From: David Horstmann Date: Wed, 17 Jan 2024 15:27:50 +0000 Subject: [PATCH 184/184] Add underflow check to UNPOISON counter decrement Make sure that extra UNPOISON calls do not cause the poisoning counter to underflow and wrap around. Memory that is unpoisoned multiple times should remain unpoisoned. Signed-off-by: David Horstmann --- tests/include/test/memory.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/include/test/memory.h b/tests/include/test/memory.h index 181280f265..20fd8d30a5 100644 --- a/tests/include/test/memory.h +++ b/tests/include/test/memory.h @@ -94,7 +94,9 @@ void mbedtls_test_memory_unpoison(const unsigned char *ptr, size_t size); #define MBEDTLS_TEST_MEMORY_UNPOISON(ptr, size) \ do { \ mbedtls_test_memory_unpoison(ptr, size); \ - mbedtls_test_memory_poisoning_count--; \ + if (mbedtls_test_memory_poisoning_count != 0) { \ + mbedtls_test_memory_poisoning_count--; \ + } \ } while (0) #else /* MBEDTLS_TEST_MEMORY_CAN_POISON */