xref: /aosp_15_r20/external/mbedtls/docs/architecture/psa-storage-resilience.md (revision 62c56f9862f102b96d72393aff6076c951fb8148)
1*62c56f98SSadaf Ebrahimi# PSA storage resilience design
2*62c56f98SSadaf Ebrahimi
3*62c56f98SSadaf Ebrahimi## Introduction
4*62c56f98SSadaf Ebrahimi
5*62c56f98SSadaf EbrahimiThe PSA crypto subsystem includes a persistent key store. It is possible to create a persistent key and read it back later. This must work even if the underlying storage exhibits non-nominal behavior. In this document, _resilience_ means correct behavior of the key store even under if the underlying platform behaves in a non-nominal, but still partially controlled way.
6*62c56f98SSadaf Ebrahimi
7*62c56f98SSadaf EbrahimiAt this point, we are only concerned about one specific form of resilience: to a system crash or power loss. That is, we assume that the underlying platform behaves nominally, except that occasionally it may restart. In the field, this can happen due to a sudden loss of power.
8*62c56f98SSadaf Ebrahimi
9*62c56f98SSadaf EbrahimiThis document explores the problem space, defines a library design and a test design.
10*62c56f98SSadaf Ebrahimi
11*62c56f98SSadaf Ebrahimi## Resilience goals for API functions
12*62c56f98SSadaf Ebrahimi
13*62c56f98SSadaf Ebrahimi**Goal: PSA Crypto API functions are atomic and committing.**
14*62c56f98SSadaf Ebrahimi
15*62c56f98SSadaf Ebrahimi_Atomic_ means that when an application calls an API function, as far as the application is concerned, at any given point in time, the system is either in a state where the function has not started yet, or in a state where the function has returned. The application never needs to worry about an intermediate state.
16*62c56f98SSadaf Ebrahimi
17*62c56f98SSadaf Ebrahimi_Committing_ means that when a function returns, the data has been written to the persistent storage. As a consequence, if the system restarts during a sequence of storage modifications $M_1, M_2, \ldots, M_n$, we know that when the system restarts, a prefix of the sequence has been performed. For example, there will never be a situation where $M_2$ has been performed but not $M_1$.
18*62c56f98SSadaf Ebrahimi
19*62c56f98SSadaf EbrahimiThe committing property is important not only for sequences of operations, but also when reporting the result of an operation to an external system. For example, if a key creation function in the PSA Crypto API reports to the application that a key has been created, and the application reports to a server that the key has been created, it is guaranteed that the key exists even if the system restarts.
20*62c56f98SSadaf Ebrahimi
21*62c56f98SSadaf Ebrahimi## Assumptions on the underlying file storage
22*62c56f98SSadaf Ebrahimi
23*62c56f98SSadaf EbrahimiPSA relies on a PSA ITS (Internal Trusted Storage) interface, which exposes a simple API. There are two functions to modify files:
24*62c56f98SSadaf Ebrahimi
25*62c56f98SSadaf Ebrahimi* `set()` writes a whole file (either creating it, or replacing the previous content).
26*62c56f98SSadaf Ebrahimi* `remove()` removes a file (returning a specific error code if the file does not exist).
27*62c56f98SSadaf Ebrahimi
28*62c56f98SSadaf Ebrahimi**Assumption: the underlying ITS functions are atomic and committing.**
29*62c56f98SSadaf Ebrahimi
30*62c56f98SSadaf EbrahimiSince the underlying functions are atomic, the content of a file is always a version that was previously passed to `set()`. We do not try to handle the case where a file might be partially written.
31*62c56f98SSadaf Ebrahimi
32*62c56f98SSadaf Ebrahimi## Overview of API functions
33*62c56f98SSadaf Ebrahimi
34*62c56f98SSadaf EbrahimiFor a transparent key, all key management operations (creation or destruction) on persistent keys rely on a single call to the underlying storage (`set()` for a key creation, `remove()` for a key destruction). This also holds for an opaque key stored in a secure element that does not have its own key store: in this case, the core stores a wrapped (i.e. encrypted) copy of the key material, but this does not impact how the core interacts with the storage. Other API functions do not modify the storage.
35*62c56f98SSadaf Ebrahimi
36*62c56f98SSadaf EbrahimiThe following case requires extra work related to resilience:
37*62c56f98SSadaf Ebrahimi
38*62c56f98SSadaf Ebrahimi* [Key management for stateful secure element keys](#designing-key-management-for-secure-element-keys).
39*62c56f98SSadaf Ebrahimi
40*62c56f98SSadaf EbrahimiAs a consequence, apart from the listed cases, the API calls inherit directly from the [resilience properties of the underyling storage](#assumptions-on-the-underlying-file-storage). We do not need to take any special precautions in the library design, and we do not need to perform any testing of resilience for transparent keys.
41*62c56f98SSadaf Ebrahimi
42*62c56f98SSadaf Ebrahimi(This section was last updated for Mbed TLS 3.4.0 implementing PSA Crypto API 1.1.)
43*62c56f98SSadaf Ebrahimi
44*62c56f98SSadaf Ebrahimi## Designing key management for secure element keys
45*62c56f98SSadaf Ebrahimi
46*62c56f98SSadaf EbrahimiIn this section, we use “(stateful) secure element key” to mean a key stored in a stateful secure element, i.e. a secure element that stores keys. This excludes keys in a stateleess secure element for which the core stores a wrapped copy of the key. We study the problem of how key management in stateful secure elements interacts with storage and explore the design space.
47*62c56f98SSadaf Ebrahimi
48*62c56f98SSadaf Ebrahimi### Assumptions on stateful secure elements
49*62c56f98SSadaf Ebrahimi
50*62c56f98SSadaf Ebrahimi**Assumption: driver calls for key management in stateful secure elements are atomic and committing.**
51*62c56f98SSadaf Ebrahimi
52*62c56f98SSadaf Ebrahimi(For stateless secure elements, this assumption is vacuously true.)
53*62c56f98SSadaf Ebrahimi
54*62c56f98SSadaf Ebrahimi### Dual management of keys: the problem
55*62c56f98SSadaf Ebrahimi
56*62c56f98SSadaf EbrahimiFor a secure element key, key management requires a commitment on both sites. For example, consider a successful key creation operation:
57*62c56f98SSadaf Ebrahimi
58*62c56f98SSadaf Ebrahimi1. The core sends a request to the secure element to create a key.
59*62c56f98SSadaf Ebrahimi2. The secure element modifies its key store to create the key.
60*62c56f98SSadaf Ebrahimi3. The secure element reports to the core that the key has been created.
61*62c56f98SSadaf Ebrahimi4. The core reports to the application that the key has been created.
62*62c56f98SSadaf Ebrahimi
63*62c56f98SSadaf EbrahimiIf the core loses power between steps 1 and 2, the key does not exist yet. This is fine from an application's perspective since the core has not committed to the key's existence, but the core needs to take care not to leave resources in storage that are related to the non-existent key. If the core loses power between steps 2 and 3, the key exists in the secure element. From an application's perspective, the core may either report that the key exists or that it does not exist, but in the latter case, the core needs to free the key in the secure element, to avoid leaving behind inaccessible resources.
64*62c56f98SSadaf Ebrahimi
65*62c56f98SSadaf EbrahimiAs a consequence, the content of the storage cannot remain the same between the end of step 1 and the end of step 3, since the core must behave differently depending on whether step 2 has taken place.
66*62c56f98SSadaf Ebrahimi
67*62c56f98SSadaf EbrahimiAccomplishing a transaction across system boundaries is a well-known problem in database management, with a well-known solution: two-phase commit.
68*62c56f98SSadaf Ebrahimi
69*62c56f98SSadaf Ebrahimi### Overview of two-phase commit with stateful secure elements
70*62c56f98SSadaf Ebrahimi
71*62c56f98SSadaf EbrahimiWith a key in a stateful secure element, a successful creation process goes as follows (see [“Key management in a secure element with storage” in the driver interface specification](../../proposed/psa-driver-interface.html#key-management-in-a-secure-element-with-storage)):
72*62c56f98SSadaf Ebrahimi
73*62c56f98SSadaf Ebrahimi1. The core calls the driver's `"allocate_key"` entry point.
74*62c56f98SSadaf Ebrahimi2. The driver allocates a unique identifier _D_ for the key. This is unrelated to the key identifier _A_ used by the application interface. This step must not modify the state of the secure element.
75*62c56f98SSadaf Ebrahimi3. The core updates the storage to indicate that key identifier _A_ has the identifier _D_ in the driver, and that _A_ is in a half-created state.
76*62c56f98SSadaf Ebrahimi4. The core calls the driver's key creation entry point, passing it the driver's chosen identifier _D_.
77*62c56f98SSadaf Ebrahimi5. The driver creates the key in the secure element. When this happens, it concludes the voting phase of the two-phase commit: effectively, the secure element decides to commit. (It is however possible to revert this commitment by giving the secure element the order to destroy the key.)
78*62c56f98SSadaf Ebrahimi6. The core updates the storage to indicate that _A_ is now in a fully created state. This concludes the commit phase of the two-phase commit.
79*62c56f98SSadaf Ebrahimi
80*62c56f98SSadaf EbrahimiIf there is a loss of power:
81*62c56f98SSadaf Ebrahimi
82*62c56f98SSadaf Ebrahimi* Before step 3: the system state has not changed at all. As far as the world is concerned, the key creation attempt never happened.
83*62c56f98SSadaf Ebrahimi* Between step 3 and step 6: upon restart, the core needs to find out whether the secure element completed step 5 or not, and reconcile the state of the storage with the state of the secure element.
84*62c56f98SSadaf Ebrahimi* After step 6: the key has been created successfully.
85*62c56f98SSadaf Ebrahimi
86*62c56f98SSadaf EbrahimiKey destruction goes as follows:
87*62c56f98SSadaf Ebrahimi
88*62c56f98SSadaf Ebrahimi1. The core updates the storage indicating that the key is being destroyed.
89*62c56f98SSadaf Ebrahimi2. The core calls the driver's `"destroy_key"` entry point.
90*62c56f98SSadaf Ebrahimi3. The secure element destroys the key.
91*62c56f98SSadaf Ebrahimi4. The core updates the storage to indicate that the key has been destroyed.
92*62c56f98SSadaf Ebrahimi
93*62c56f98SSadaf EbrahimiIf there is a loss of power:
94*62c56f98SSadaf Ebrahimi
95*62c56f98SSadaf Ebrahimi* Before step 1: the system state has not changed at all. As far as the world is concerned, the key destruction attempt never happened.
96*62c56f98SSadaf Ebrahimi* Between step 1 and step 4: upon restart, the core needs to find out whether the secure element completed step 3 or not, and reconcile the state of the storage with the state of the secure element.
97*62c56f98SSadaf Ebrahimi* After step 4: the key has been destroyed successfully.
98*62c56f98SSadaf Ebrahimi
99*62c56f98SSadaf EbrahimiIn both cases, upon restart, the core needs to perform a transaction recovery. When a power loss happens, the core decides whether to commit or abort the transaction.
100*62c56f98SSadaf Ebrahimi
101*62c56f98SSadaf EbrahimiNote that the analysis in this section assumes that the driver does not update its persistent state during a key management operation (or at least not in a way that is influences the key management process — for example, it might renew an authorization token).
102*62c56f98SSadaf Ebrahimi
103*62c56f98SSadaf Ebrahimi### Optimization considerations for transactions
104*62c56f98SSadaf Ebrahimi
105*62c56f98SSadaf EbrahimiWe assume that power failures are rare. Therefore we will primarily optimize for the normal case. Transaction recovery needs to be practical, but does not have to be fully optimized.
106*62c56f98SSadaf Ebrahimi
107*62c56f98SSadaf EbrahimiThe main quantity we will optimize for is the number of storage updates in the nominal case. This is good for performance because storage writes are likely to dominate the runtime in some hardware configurations where storage writes are slow and communication with the secure element is fast, for key management operations that require a small amount of computation. In addition, minimizing the number of storage updates is good for the longevity of flash media.
108*62c56f98SSadaf Ebrahimi
109*62c56f98SSadaf Ebrahimi#### Information available during recovery
110*62c56f98SSadaf Ebrahimi
111*62c56f98SSadaf EbrahimiThe PSA ITS API does not support enumerating files in storage: an ITS call can only access one file identifier. Therefore transaction recovery cannot be done by traversing files whose name is or encodes the key identifier. It must start by traversing a small number of files whose names are independent of the key identifiers involved.
112*62c56f98SSadaf Ebrahimi
113*62c56f98SSadaf Ebrahimi#### Minimum effort for a transaction
114*62c56f98SSadaf Ebrahimi
115*62c56f98SSadaf EbrahimiPer the [assumptions on the underlying file storage](#assumptions-on-the-underlying-file-storage), each atomic operation in the internal storage concerns a single file: either removing it, or setting its content. Furthermore there is no way to enumerate the files in storage.
116*62c56f98SSadaf Ebrahimi
117*62c56f98SSadaf EbrahimiA key creation function must transform the internal storage from a state where file `id` does not exist, to a state where file `id` exists and has its desired final content (containing the key attributes and the driver's key identifier). The situation is similar with key destruction, except that the initial and final states are exchanged. Neither the initial state nor the final state reference `id` otherwise.
118*62c56f98SSadaf Ebrahimi
119*62c56f98SSadaf EbrahimiFor a key that is not in a stateful element, the transaction consists of a single write operation. As discussed previously, this is not possible with a stateful secure element because the state of the internal storage needs to change both before and after the state change in the secure element. No other single-write algorithm works.
120*62c56f98SSadaf Ebrahimi
121*62c56f98SSadaf EbrahimiIf there is a power failure around the time of changing the state of the secure element, there must be information in the internal storage that indicates that key `id` has a transaction in progress. The file `id` cannot be used for this purpose because there is no way to enumerate all keys (and even if there was, it would not be practical). Therefore the transaction will need to modify some other file `t` with a fixed name (a name that doesn't depend on the key). Since the final system state should be identical to the initial state except for the file `id`, the minimum number of storage operations for a transaction is 3:
122*62c56f98SSadaf Ebrahimi
123*62c56f98SSadaf Ebrahimi* Write (create or update) a file `t` referencing `id`.
124*62c56f98SSadaf Ebrahimi* Write the final state of `id`.
125*62c56f98SSadaf Ebrahimi* Restore `t` to its initial state.
126*62c56f98SSadaf Ebrahimi
127*62c56f98SSadaf EbrahimiThe strategies discussed in the [overview above](#overview-of-two-phase-commit-with-stateful-secure-elements) follow this pattern, with `t` being the file containing the transaction list that the recovery consults. We have just proved that this pattern is optimal.
128*62c56f98SSadaf Ebrahimi
129*62c56f98SSadaf EbrahimiNote that this pattern requires the state of `id` to be modified only once. In particular, if a key management involves writing an intermediate state for `id` before modifying the secure element state and writing a different state after that, this will require a total of 4 updates to internal storage. Since we want to minimize the number of storage updates, we will not explore designs that involved updating `id` twice or more.
130*62c56f98SSadaf Ebrahimi
131*62c56f98SSadaf Ebrahimi### Recovery strategies
132*62c56f98SSadaf Ebrahimi
133*62c56f98SSadaf EbrahimiWhen the core starts, it needs to know about transaction(s) that need to be resumed. This information will be stored in a persistent “transaction list”, with one entry per key. In this section, we explore recovery strategies, and we determine what the transaction list needs to contain as well as when it needs to be updated. Other sections will explore the format of the transaction list, as well as how many keys it needs to contain.
134*62c56f98SSadaf Ebrahimi
135*62c56f98SSadaf Ebrahimi#### Exploring the recovery decision tree
136*62c56f98SSadaf Ebrahimi
137*62c56f98SSadaf EbrahimiThere are four cases for recovery when a transaction is in progress. In each case, the core can either decide to commit the transaction (which may require replaying the interrupted part) or abort it (which may require a rewind in the secure element). It may call the secure element driver's `"get_key_attributes"` entry point to find out whether the key is present.
138*62c56f98SSadaf Ebrahimi
139*62c56f98SSadaf Ebrahimi* Key creation, key not present in the secure element:
140*62c56f98SSadaf Ebrahimi    * Committing means replaying the driver call in the key creation. This requires all the input, for example the data to import. This seems impractical in general. Also, the second driver call require a new call to `"allocate_key"` which will in general changing the key's driver identifier, which complicates state management in the core. Given the likely complexity, we exclude this strategy.
141*62c56f98SSadaf Ebrahimi    * Aborting means removing any trace of the key creation.
142*62c56f98SSadaf Ebrahimi* Key creation, key present in the secure element:
143*62c56f98SSadaf Ebrahimi    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted.
144*62c56f98SSadaf Ebrahimi    * Aborting means destroying the key in the secure element and removing any local storage used for that key.
145*62c56f98SSadaf Ebrahimi* Key destruction, key not present in the secure element:
146*62c56f98SSadaf Ebrahimi    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted, by removing any remaining local storage used for that key.
147*62c56f98SSadaf Ebrahimi    * Aborting would mean re-creating the key in the secure element, which is impossible in general since the key material is no longer present.
148*62c56f98SSadaf Ebrahimi* Key destruction, key present in the secure element:
149*62c56f98SSadaf Ebrahimi    * Committing means finishing the update of the core's persistent state, as would have been done if the transaction had not been interrupted, by removing any remaining local storage used for that key and destroying the key in the secure element.
150*62c56f98SSadaf Ebrahimi    * Aborting means keeping the key. This requires no action on the secure element, and is only practical locally if the local storage is intact.
151*62c56f98SSadaf Ebrahimi
152*62c56f98SSadaf Ebrahimi#### Comparing recovery strategies
153*62c56f98SSadaf Ebrahimi
154*62c56f98SSadaf EbrahimiFrom the analysis above, assuming that all keys are treated in the same way, there are 4 possible strategies.
155*62c56f98SSadaf Ebrahimi
156*62c56f98SSadaf Ebrahimi* [Always follow the state of the secure element](#exploring-the-follow-the-secure-element-strategy). This requires the secure element driver to have a `"get_key_attributes"` entry point. Recovery means resuming the operation where it left off. For key creation, this means that the key metadata needs to be saved before calling the secure element's key creation entry point.
157*62c56f98SSadaf Ebrahimi* Minimize the information processing: [always destroy the key](#exploring-the-always-destroy-strategy), i.e. abort all key creations and commit all key destructions. This does not require querying the state of the secure element. This does not require any special precautions to preserve information about the key during the transaction. It simplifies recovery in that the recovery process might not even need to know whether it's recovering a key creation or a key destruction.
158*62c56f98SSadaf Ebrahimi* Follow the state of the secure element for key creation, but always go ahead with key destruction. This requires the secure element driver to have a `"get_key_attributes"` entry point. Compared to always following the state of the secure element, this has the advantage of maximizing the chance that a command to destroy key material is effective. Compared to always destroying the key, this has a performance advantage if a key creation is interrupted. These do not seem like decisive advantages, so we will not consider this strategy further.
159*62c56f98SSadaf Ebrahimi* Always abort key creation, but follow the state of the secure element for key destruction. I can't think of a good reason to choose this strategy.
160*62c56f98SSadaf Ebrahimi
161*62c56f98SSadaf EbrahimiRequiring the driver to have a `"get_key_attributes"` entry point is potentially problematic because some secure elements don't have room to store key attributes: a key slot always exists, and it's up to the user to remember what, if anything, they put in it. The driver has to remember anyway, so that it can find a free slot when creating a key. But with a recovery strategy that doesn't involve a `"get_key_attributes"` entry point, the driver design is easier: the driver doesn't need to protect the information about slots in use against a power failure, the core takes care of that.
162*62c56f98SSadaf Ebrahimi
163*62c56f98SSadaf Ebrahimi#### Exploring the follow-the-secure-element strategy
164*62c56f98SSadaf Ebrahimi
165*62c56f98SSadaf EbrahimiEach entry in the transaction list contains the API key identifier, the key lifetime (or at least the location), the driver key identifier (not constant-size), and an indication of whether the key is being created or destroyed.
166*62c56f98SSadaf Ebrahimi
167*62c56f98SSadaf EbrahimiFor key creation, we have all the information to store in the key file once the `"allocate_key"` call returns. We must store all the information that will go in the key file before calling the driver's key creation entry point. Therefore the normal sequence of operations is:
168*62c56f98SSadaf Ebrahimi
169*62c56f98SSadaf Ebrahimi1. Call the driver's `"allocate_key"` entry point.
170*62c56f98SSadaf Ebrahimi2. Add the key to the transaction list, indicating that it is being created.
171*62c56f98SSadaf Ebrahimi3. Write the key file.
172*62c56f98SSadaf Ebrahimi4. Call the driver's key creation entry point.
173*62c56f98SSadaf Ebrahimi5. Remove the key from the transaction list.
174*62c56f98SSadaf Ebrahimi
175*62c56f98SSadaf EbrahimiDuring recovery, for each key in the transaction list that was being created:
176*62c56f98SSadaf Ebrahimi
177*62c56f98SSadaf Ebrahimi* If the key exists in the secure element, just remove it from the transaction list.
178*62c56f98SSadaf Ebrahimi* If the key does not exist in the secure element, first remove the key file if it is present, then remove the key from the transaction list.
179*62c56f98SSadaf Ebrahimi
180*62c56f98SSadaf EbrahimiFor key destruction, we need to preserve the key file until after the key has been destroyed. Therefore the normal sequence of operations is:
181*62c56f98SSadaf Ebrahimi
182*62c56f98SSadaf Ebrahimi1. Add the key to the transaction list, indicating that it is being destroyed.
183*62c56f98SSadaf Ebrahimi2. Call the driver's `"destroy_key"` entry point.
184*62c56f98SSadaf Ebrahimi3. Remove the key file.
185*62c56f98SSadaf Ebrahimi4. Remove the key from the transaction list.
186*62c56f98SSadaf Ebrahimi
187*62c56f98SSadaf EbrahimiDuring recovery, for each key in the transaction list that was being created:
188*62c56f98SSadaf Ebrahimi
189*62c56f98SSadaf Ebrahimi* If the key exists in the secure element, call the driver's `"destroy_key"` entry point, then remove the key file, and finally remote the key from the transaction lits.
190*62c56f98SSadaf Ebrahimi* If the key does not exist in the secure element, remove the key file if it is still present, then remove the key from the transaction list.
191*62c56f98SSadaf Ebrahimi
192*62c56f98SSadaf Ebrahimi#### Exploring the always-destroy strategy
193*62c56f98SSadaf Ebrahimi
194*62c56f98SSadaf EbrahimiEach entry in the transaction list contains the API key identifier, the key lifetime (or at least the location), and the driver key identifier (not constant-size).
195*62c56f98SSadaf Ebrahimi
196*62c56f98SSadaf EbrahimiFor key creation, we do not need to store the key's metadata until it has been created in the secure element. Therefore the normal sequence of operations is:
197*62c56f98SSadaf Ebrahimi
198*62c56f98SSadaf Ebrahimi1. Call the driver's `"allocate_key"` entry point.
199*62c56f98SSadaf Ebrahimi2. Add the key to the transaction list.
200*62c56f98SSadaf Ebrahimi3. Call the driver's key creation entry point.
201*62c56f98SSadaf Ebrahimi4. Write the key file.
202*62c56f98SSadaf Ebrahimi5. Remove the key from the transaction list.
203*62c56f98SSadaf Ebrahimi
204*62c56f98SSadaf EbrahimiFor key destruction, we can remove the key file before contacting the secure element. Therefore the normal sequence of operations is:
205*62c56f98SSadaf Ebrahimi
206*62c56f98SSadaf Ebrahimi1. Add the key to the transaction list.
207*62c56f98SSadaf Ebrahimi2. Remove the key file.
208*62c56f98SSadaf Ebrahimi3. Call the driver's `"destroy_key"` entry point.
209*62c56f98SSadaf Ebrahimi4. Remove the key from the transaction list.
210*62c56f98SSadaf Ebrahimi
211*62c56f98SSadaf EbrahimiRecovery means removing all traces of all keys on the transaction list. This means following the destruction process, starting after the point where the key has been added to the transaction list, and ignoring any failure of a removal action if the item to remove does not exist:
212*62c56f98SSadaf Ebrahimi
213*62c56f98SSadaf Ebrahimi1. Remove the key file, treating `DOES_NOT_EXIST` as a success.
214*62c56f98SSadaf Ebrahimi2. Call the driver's `"destroy_key"` entry point, treating `DOES_NOT_EXIST` as a success.
215*62c56f98SSadaf Ebrahimi3. Remove the key from the transaction list.
216*62c56f98SSadaf Ebrahimi
217*62c56f98SSadaf Ebrahimi#### Always-destroy strategy with a simpler transaction file
218*62c56f98SSadaf Ebrahimi
219*62c56f98SSadaf EbrahimiWe can modify the [always-destroy strategy](#exploring-the-always-destroy-strategy) to make the transaction file simpler: if we ensure that the key file always exists if the key exists in the secure element, then the transaction list does not need to include the driver key identifier: it can be read from the key file.
220*62c56f98SSadaf Ebrahimi
221*62c56f98SSadaf EbrahimiFor key creation, we need to store the key's metadata before creating in the secure element. Therefore the normal sequence of operations is:
222*62c56f98SSadaf Ebrahimi
223*62c56f98SSadaf Ebrahimi1. Call the driver's `"allocate_key"` entry point.
224*62c56f98SSadaf Ebrahimi2. Add the key to the transaction list.
225*62c56f98SSadaf Ebrahimi3. Write the key file.
226*62c56f98SSadaf Ebrahimi4. Call the driver's key creation entry point.
227*62c56f98SSadaf Ebrahimi5. Remove the key from the transaction list.
228*62c56f98SSadaf Ebrahimi
229*62c56f98SSadaf EbrahimiFor key destruction, we need to contact the secure element before removing the key file. Therefore the normal sequence of operations is:
230*62c56f98SSadaf Ebrahimi
231*62c56f98SSadaf Ebrahimi1. Add the key to the transaction list.
232*62c56f98SSadaf Ebrahimi2. Call the driver's `"destroy_key"` entry point.
233*62c56f98SSadaf Ebrahimi3. Remove the key file.
234*62c56f98SSadaf Ebrahimi4. Remove the key from the transaction list.
235*62c56f98SSadaf Ebrahimi
236*62c56f98SSadaf EbrahimiRecovery means removing all traces of all keys on the transaction list. This means following the destruction process, starting after the point where the key has been added to the transaction list, and ignoring any failure of a removal action if the item to remove does not exist:
237*62c56f98SSadaf Ebrahimi
238*62c56f98SSadaf Ebrahimi1. Load the driver key identifier from the key file. If the key file does not exist, skip to step 4.
239*62c56f98SSadaf Ebrahimi2. Call the driver's `"destroy_key"` entry point, treating `DOES_NOT_EXIST` as a success.
240*62c56f98SSadaf Ebrahimi3. Remove the key file, treating `DOES_NOT_EXIST` as a success.
241*62c56f98SSadaf Ebrahimi4. Remove the key from the transaction list.
242*62c56f98SSadaf Ebrahimi
243*62c56f98SSadaf EbrahimiCompared with the basic always-destroy strategy:
244*62c56f98SSadaf Ebrahimi
245*62c56f98SSadaf Ebrahimi* The transaction file handling is simpler since its entries have a fixed size.
246*62c56f98SSadaf Ebrahimi* The flow of information is somewhat different from transparent keys and keys in stateless secure elements: we aren't just replacing “create the key material” by “tell the secure element to create the key material”, those happen at different times. But there's a different flow for stateful secure elements anyway, since the call to `"allocate_key"` has no analog in the stateless secure element or transparent cases.
247*62c56f98SSadaf Ebrahimi
248*62c56f98SSadaf Ebrahimi#### Assisting secure element drivers with recovery
249*62c56f98SSadaf Ebrahimi
250*62c56f98SSadaf EbrahimiThe actions of the secure element driver may themselves be non-atomic. So the driver must be given a chance to perform recovery.
251*62c56f98SSadaf Ebrahimi
252*62c56f98SSadaf EbrahimiTo simplify the design of the driver, the core should guarantee that the driver will know if a transaction was in progress and the core cannot be sure about the state of the secure element. Merely calling a read-only entry point such as `"get_key_attributes"` does not provide enough information to the driver for it to know that it should actively perform recovery related to that key.
253*62c56f98SSadaf Ebrahimi
254*62c56f98SSadaf EbrahimiThis gives an advantage to the “always destroy” strategy. Under this strategy, if the key might be in a transitional state, the core will request a key destruction from the driver. This means that, if the driver has per-key auxiliary data to clean up, it can bundle that as part of the key's destruction.
255*62c56f98SSadaf Ebrahimi
256*62c56f98SSadaf Ebrahimi### Testing non-atomic processes
257*62c56f98SSadaf Ebrahimi
258*62c56f98SSadaf EbrahimiIn this section, we discuss how to test non-atomic processes that must implement an atomic and committing interface. As discussed in [“Overview of API functions”](#overview-of-api-functions), this concerns key management in stateful secure elements.
259*62c56f98SSadaf Ebrahimi
260*62c56f98SSadaf Ebrahimi#### Naive test strategy for non-atomic processes
261*62c56f98SSadaf Ebrahimi
262*62c56f98SSadaf EbrahimiNon-atomic processes consist of a series of atomic, committing steps.
263*62c56f98SSadaf Ebrahimi
264*62c56f98SSadaf EbrahimiOur general strategy to test them is as follows: every time there is a modification of persistent state, either in storage or in the (simulated) secure element, try both the nominal case and simulating a power loss. If a power loss occurs, restart the system (i.e. clean up and call `psa_crypto_init()`), and check that the system ends up in a consistent state.
265*62c56f98SSadaf Ebrahimi
266*62c56f98SSadaf EbrahimiNote that this creates a binary tree of possibilities: after each state modification, there may or may not be a restart, and after that different state modifications may occur, each of which may or may not be followed by a restart.
267*62c56f98SSadaf Ebrahimi
268*62c56f98SSadaf EbrahimiFor example, consider testing of one key creation operation (see [“Overview of two-phase commit with stateful secure elements”](#overview-of-two-phase-commit-with-stateful-secure-elements), under the simplifying assumption that each storage update step, as well as the recovery after a restart, each make a single (atomic) storage modification and no secure element access. The nominal case consists of three state modifications: storage modification (start transaction), creation on the secure element, storage modification (commit transaction). We need to test the following sequences:
269*62c56f98SSadaf Ebrahimi
270*62c56f98SSadaf Ebrahimi* Start transaction, restart, recovery.
271*62c56f98SSadaf Ebrahimi* Start transaction, secure element operation, restart, recovery.
272*62c56f98SSadaf Ebrahimi* Start transaction, secure element operation, commit transaction.
273*62c56f98SSadaf Ebrahimi
274*62c56f98SSadaf EbrahimiIf, for example, recovery consists of two atomic steps, the tree of possibilities expands and may be infinite:
275*62c56f98SSadaf Ebrahimi
276*62c56f98SSadaf Ebrahimi* Start transaction, restart, recovery step 1, restart, recovery step 1, recovery step 2.
277*62c56f98SSadaf Ebrahimi* Start transaction, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, recovery step 2.
278*62c56f98SSadaf Ebrahimi* Start transaction, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, restart, recovery step 1, recovery step 2.
279*62c56f98SSadaf Ebrahimi* etc.
280*62c56f98SSadaf Ebrahimi* Start transaction, secure element operation, restart, ...
281*62c56f98SSadaf Ebrahimi* Start transaction, secure element operation, commit transaction.
282*62c56f98SSadaf Ebrahimi
283*62c56f98SSadaf EbrahimiIn order to limit the possibilities, we need to make some assumptions about the recovery step. For example, if we have confidence that recovery step 1 is idempotent (i.e. doing it twice is the same as doing it once), we don't need to test what happens in execution sequences that take recovery step 1 more than twice in a row.
284*62c56f98SSadaf Ebrahimi
285*62c56f98SSadaf Ebrahimi### Splitting normal behavior and transaction recovery
286*62c56f98SSadaf Ebrahimi
287*62c56f98SSadaf EbrahimiWe introduce an abstraction level in transaction recovery:
288*62c56f98SSadaf Ebrahimi
289*62c56f98SSadaf Ebrahimi* Normal operation must maintain a certain invariant on the state of the world (internal storage and secure element).
290*62c56f98SSadaf Ebrahimi* Transaction recovery is defined over all states of the world that satisfy this invariant.
291*62c56f98SSadaf Ebrahimi
292*62c56f98SSadaf EbrahimiThis separation of concerns greatly facilitates testing, since it is now split into two parts:
293*62c56f98SSadaf Ebrahimi
294*62c56f98SSadaf Ebrahimi* During the testing of normal operation, we can use read-only invasive testing to ensure that the invariant is maintained. No modification of normal behavior (such as simulated power failures) is necessary.
295*62c56f98SSadaf Ebrahimi* Testing of transaction recovery is independent of how the system state was reached. We only need to artificially construct a representative sample of system states that match the invariant. Transaction recovery is itself an operation that must respect the invariant, and so we do not need any special testing for the case of an interrupted recovery.
296*62c56f98SSadaf Ebrahimi
297*62c56f98SSadaf EbrahimiAnother benefit of this approach is that it is easier to specify and test what happens if the library is updated on a device with leftovers from an interrupted transaction. We will require and test that the new version of the library supports recovery of the old library's states, without worrying how those states were reached.
298*62c56f98SSadaf Ebrahimi
299*62c56f98SSadaf Ebrahimi#### Towards an invariant for transactions
300*62c56f98SSadaf Ebrahimi
301*62c56f98SSadaf EbrahimiAs discussed in the section [“Recovery strategies”](#recovery-strategies), the information about active transactions is stored in a transaction list file. The name of the transaction list file does not depend on the identifiers of the keys in the list, but there may be more than one transaction list, for example one per secure element. If so, each transaction list can be considered independently.
302*62c56f98SSadaf Ebrahimi
303*62c56f98SSadaf EbrahimiWhen no transaction is in progress, the transaction list does not exist, or is empty. The empty case must be supported because this is the initial state of the filesystem. When no transaction is in progress, the state of the secure element must be consistent with references to keys in that secure element contained in key files. More generally, if a key is not in the transaction list, then the key must be present in the secure element if and only if the key file is in the internal storage.
304*62c56f98SSadaf Ebrahimi
305*62c56f98SSadaf EbrahimiFor the purposes of the state invariant, it matters whether the transaction list file contains the driver key identifier, or if the driver key identifier is only stored in the key file. This is because the core needs to know the driver key id in order to access the secure element. If the transaction list does not contain the driver key identifier, and the key file does not exist, the key must not be present in the secure element.
306*62c56f98SSadaf Ebrahimi
307*62c56f98SSadaf EbrahimiWe thus have two scenarios, each with their own invariant: one where the transaction list contains only key identifiers, and one where it also contains the secure element's key identifier (as well as the location of the secure element if this is not encoded in the name of the transaction list file).
308*62c56f98SSadaf Ebrahimi
309*62c56f98SSadaf Ebrahimi#### Storage invariant if the transaction list contains application key identifiers only
310*62c56f98SSadaf Ebrahimi
311*62c56f98SSadaf EbrahimiInvariants:
312*62c56f98SSadaf Ebrahimi
313*62c56f98SSadaf Ebrahimi* If the file `id` does not exist, then no resources corresponding to that key are in a secure element. This holds whether `id` is in the transaction list or not.
314*62c56f98SSadaf Ebrahimi* If `id` is not in the transaction list and the file `id` exists and references a key in a stateful secure element, then the key is present in the secure element.
315*62c56f98SSadaf Ebrahimi
316*62c56f98SSadaf EbrahimiIf `id` is in the transaction list and the file `id` exists, the key may or may not be present in the secure element.
317*62c56f98SSadaf Ebrahimi
318*62c56f98SSadaf EbrahimiThe invariant imposes constraints on the [order of operations for the two-phase commit](#overview-of-two-phase-commit-with-stateful-secure-elements): key creation must create `id` before calling the secure element's key creation entry point, and key destruction must remove `id` after calling the secure element's key destruction entry point.
319*62c56f98SSadaf Ebrahimi
320*62c56f98SSadaf EbrahimiFor recovery:
321*62c56f98SSadaf Ebrahimi
322*62c56f98SSadaf Ebrahimi* If the file `id` does not exist, then nothing needs to be done for recovery, other than removing `id` from the transaction list.
323*62c56f98SSadaf Ebrahimi* If the file `id` exists:
324*62c56f98SSadaf Ebrahimi    * It is correct to destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
325*62c56f98SSadaf Ebrahimi    * It is correct to check whether the key exists in the secure element, and if it does, keep it and keep `id`. If not, remove `id` from the internal storage.
326*62c56f98SSadaf Ebrahimi
327*62c56f98SSadaf Ebrahimi#### Storage invariant if the transaction list contains driver key identifiers
328*62c56f98SSadaf Ebrahimi
329*62c56f98SSadaf EbrahimiInvariants:
330*62c56f98SSadaf Ebrahimi
331*62c56f98SSadaf Ebrahimi* If `id` is not in the transaction list and the file `id` does not exist, then no resources corresponding to that key are in a secure element.
332*62c56f98SSadaf Ebrahimi* If `id` is not in the transaction list and the file `id` exists, then the key is present in the secure element.
333*62c56f98SSadaf Ebrahimi
334*62c56f98SSadaf EbrahimiIf `id` is in the transaction list, neither the state of `id` in the internal storage nor the state of the key in the secure element is known.
335*62c56f98SSadaf Ebrahimi
336*62c56f98SSadaf EbrahimiFor recovery:
337*62c56f98SSadaf Ebrahimi
338*62c56f98SSadaf Ebrahimi* If the file `id` does not exist, then destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success).
339*62c56f98SSadaf Ebrahimi* If the file `id` exists:
340*62c56f98SSadaf Ebrahimi    * It is correct to destroy the key in the secure element (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
341*62c56f98SSadaf Ebrahimi    * It is correct to check whether the key exists in the secure element, and if it does, keep it and keep `id`. If not, remove `id` from the internal storage.
342*62c56f98SSadaf Ebrahimi
343*62c56f98SSadaf Ebrahimi#### Coverage of states that respect the invariant
344*62c56f98SSadaf Ebrahimi
345*62c56f98SSadaf EbrahimiFor a given key, we have to consider three a priori independent boolean states:
346*62c56f98SSadaf Ebrahimi
347*62c56f98SSadaf Ebrahimi* Whether the key file exists.
348*62c56f98SSadaf Ebrahimi* Whether the key is in the secure element.
349*62c56f98SSadaf Ebrahimi* Whether the key is in the transaction list.
350*62c56f98SSadaf Ebrahimi
351*62c56f98SSadaf EbrahimiThere is full coverage for one key if we have tests of recovery for the states among these $2^3 = 8$ possibilities that satisfy the storage invariant.
352*62c56f98SSadaf Ebrahimi
353*62c56f98SSadaf EbrahimiIn addition, testing should adequately cover the case of multiple keys in the transaction list. How much coverage is adequate depends on the layout of the list as well as white-box considerations of how the list is manipulated.
354*62c56f98SSadaf Ebrahimi
355*62c56f98SSadaf Ebrahimi### Choice of a transaction design
356*62c56f98SSadaf Ebrahimi
357*62c56f98SSadaf Ebrahimi#### Chosen transaction algorithm
358*62c56f98SSadaf Ebrahimi
359*62c56f98SSadaf EbrahimiBased on [“Optimization considerations for transactions”](#optimization-considerations-for-transactions), we choose a transaction algorithm that consists in the following operations:
360*62c56f98SSadaf Ebrahimi
361*62c56f98SSadaf Ebrahimi1. Add the key identifier to the transaction list.
362*62c56f98SSadaf Ebrahimi2. Call the secure element's key creation or destruction entry point.
363*62c56f98SSadaf Ebrahimi3. Remove the key identifier from the transaction list.
364*62c56f98SSadaf Ebrahimi
365*62c56f98SSadaf EbrahimiIn addition, before or after step 2, create or remove the key file in the internal storage.
366*62c56f98SSadaf Ebrahimi
367*62c56f98SSadaf EbrahimiIn order to conveniently support multiple transactions at the same time, we pick the simplest possible layout for the transaction list: a simple array of key identifiers. Since the transaction list does not contain the driver key identifier:
368*62c56f98SSadaf Ebrahimi
369*62c56f98SSadaf Ebrahimi* During key creation, create the key file in internal storage in the internal storage before calling the secure element's key creation entry point.
370*62c56f98SSadaf Ebrahimi* During key destruction, call the secure element's key destruction entry point before removing the key file in internal storage.
371*62c56f98SSadaf Ebrahimi
372*62c56f98SSadaf EbrahimiThis choice of algorithm does not require the secure element driver to have a `"get_key_attributes"` entry point.
373*62c56f98SSadaf Ebrahimi
374*62c56f98SSadaf Ebrahimi#### Chosen storage invariant
375*62c56f98SSadaf Ebrahimi
376*62c56f98SSadaf EbrahimiThe [storage invariant](#storage-invariant-if-the-transaction-list-contains-application-key-identifiers-only) is as follows:
377*62c56f98SSadaf Ebrahimi
378*62c56f98SSadaf Ebrahimi* If the file `id` does not exist, then no resources corresponding to that key are in a secure element. This holds whether `id` is in the transaction list or not.
379*62c56f98SSadaf Ebrahimi* If `id` is not in the transaction list and the file `id` exists and references a key in a stateful secure element, then the key is present in the secure element.
380*62c56f98SSadaf Ebrahimi* If `id` is in the transaction list and a key exists by that identifier, the key's location is a stateful secure element.
381*62c56f98SSadaf Ebrahimi
382*62c56f98SSadaf Ebrahimi#### Chosen recovery process
383*62c56f98SSadaf Ebrahimi
384*62c56f98SSadaf EbrahimiTo [assist secure element drivers with recovery](#assisting-secure-element-drivers-with-recovery), we pick the [always-destroy recovery strategy with a simple transaction file](#always-destroy-strategy-with-a-simpler-transaction-file). The the recovery process is as follows:
385*62c56f98SSadaf Ebrahimi
386*62c56f98SSadaf Ebrahimi* If the file `id` does not exist, then nothing needs to be done for recovery, other than removing `id` from the transaction list.
387*62c56f98SSadaf Ebrahimi* If the file `id` exists, call the secure element's key destruction entry point (treating a `DOES_NOT_EXIST` error as a success), then remove `id`.
388*62c56f98SSadaf Ebrahimi
389*62c56f98SSadaf Ebrahimi## Specification of key management in stateful secure elements
390*62c56f98SSadaf Ebrahimi
391*62c56f98SSadaf EbrahimiThis section only concerns stateful secure elements as discussed in [“Designing key management for secure element keys”](#designing-key-management-for-secure-element-keys), i.e. secure elements with an `"allocate_key"` entry point. The design follows the general principle described in [“Overview of two-phase commit with stateful secure elements”](#overview-of-two-phase-commit-with-stateful-secure-elements) and the specific choices justified in [“Choice of a transaction design”](choice-of-a-transaction-design).
392*62c56f98SSadaf Ebrahimi
393*62c56f98SSadaf Ebrahimi### Transaction list file manipulation
394*62c56f98SSadaf Ebrahimi
395*62c56f98SSadaf EbrahimiThe transaction list is a simple array of key identifiers.
396*62c56f98SSadaf Ebrahimi
397*62c56f98SSadaf EbrahimiTo add a key identifier to the list:
398*62c56f98SSadaf Ebrahimi
399*62c56f98SSadaf Ebrahimi1. Load the current list from the transaction list if it exists and it is not already cached in memory.
400*62c56f98SSadaf Ebrahimi2. Append the key identifier to the array.
401*62c56f98SSadaf Ebrahimi3. Write the updated list file.
402*62c56f98SSadaf Ebrahimi
403*62c56f98SSadaf EbrahimiTo remove a key identifier from the list:
404*62c56f98SSadaf Ebrahimi
405*62c56f98SSadaf Ebrahimi1. Load the current list if it is not already cached in memory. It is an error if the file does not exist since it must contain this identifier.
406*62c56f98SSadaf Ebrahimi2. Remove the key identifier from the array. If it wasn't the last element in array, move array elements to fill the hole.
407*62c56f98SSadaf Ebrahimi3. If the list is now empty, remove the transaction list file. Otherwise write the updated list to the file.
408*62c56f98SSadaf Ebrahimi
409*62c56f98SSadaf Ebrahimi### Key creation process in the core
410*62c56f98SSadaf Ebrahimi
411*62c56f98SSadaf EbrahimiLet _A_ be the application key identifier.
412*62c56f98SSadaf Ebrahimi
413*62c56f98SSadaf Ebrahimi1. Call the driver's `"allocate_key"` entry point, obtaining the driver key identifier _D_ chosen by the driver.
414*62c56f98SSadaf Ebrahimi2. Add _A_ [to the transaction list file](#transaction-list-file-manipulation).
415*62c56f98SSadaf Ebrahimi3. Create the key file _A_ in the internal storage. Note that this is done at a different time from what happens when creating a transparent key or a key in a stateless secure element: in those cases, creating the key file happens after the actual creation of the key material.
416*62c56f98SSadaf Ebrahimi4. Call the secure element's key creation entry point.
417*62c56f98SSadaf Ebrahimi5. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
418*62c56f98SSadaf Ebrahimi
419*62c56f98SSadaf EbrahimiIf any step fails:
420*62c56f98SSadaf Ebrahimi
421*62c56f98SSadaf Ebrahimi* If the secure element's key creation entry point has been called and succeeded, call the secure element's destroy entry point.
422*62c56f98SSadaf Ebrahimi* If the key file has been created in the internal storage, remove it.
423*62c56f98SSadaf Ebrahimi* Remove the key from the transaction list.
424*62c56f98SSadaf Ebrahimi
425*62c56f98SSadaf EbrahimiNote that this process is identical to key destruction, except that the key is already in the transaction list.
426*62c56f98SSadaf Ebrahimi
427*62c56f98SSadaf Ebrahimi### Key destruction process in the core
428*62c56f98SSadaf Ebrahimi
429*62c56f98SSadaf EbrahimiLet _A_ be the application key identifier.
430*62c56f98SSadaf Ebrahimi
431*62c56f98SSadaf EbrahimiWe assume that the key is loaded in a key slot in memory: the core needs to know the key's location in order to determine whether the key is in a stateful secure element, and if so to know the driver key identifier. A possible optimization would be to load only that information in local variables, without occupying a key store; this has the advantage that key destruction works even if the key store is full.
432*62c56f98SSadaf Ebrahimi
433*62c56f98SSadaf Ebrahimi1. Add _A_ [to the transaction list file](#transaction-list-file-manipulation).
434*62c56f98SSadaf Ebrahimi2. Call the secure element's `"destroy_key"` entry point.
435*62c56f98SSadaf Ebrahimi3. Remove the key file _A_ from the internal storage.
436*62c56f98SSadaf Ebrahimi4. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
437*62c56f98SSadaf Ebrahimi5. Free the corresponding key slot in memory.
438*62c56f98SSadaf Ebrahimi
439*62c56f98SSadaf EbrahimiIf any step fails, remember the error but continue the process, to destroy the resources associated with the key as much as is practical.
440*62c56f98SSadaf Ebrahimi
441*62c56f98SSadaf Ebrahimi### Transaction recovery
442*62c56f98SSadaf Ebrahimi
443*62c56f98SSadaf EbrahimiFor each key _A_ in the transaction list file, if the file _A_ exists in the internal storage:
444*62c56f98SSadaf Ebrahimi
445*62c56f98SSadaf Ebrahimi1. Load the key into a key slot in memory (to get its location and the driver key identifier, although we could get the location from the transaction list).
446*62c56f98SSadaf Ebrahimi2. Call the secure element's `"destroy_key"` entry point.
447*62c56f98SSadaf Ebrahimi3. Remove the key file _A_ from the internal storage.
448*62c56f98SSadaf Ebrahimi4. Remove _A_ [from the transaction list file](#transaction-list-file-manipulation).
449*62c56f98SSadaf Ebrahimi5. Free the corresponding key slot in memory.
450*62c56f98SSadaf Ebrahimi
451*62c56f98SSadaf EbrahimiThe transaction list file can be processed in any order.
452*62c56f98SSadaf Ebrahimi
453*62c56f98SSadaf EbrahimiIt is correct to update the transaction list after recovering each key, or to only delete the transaction list file once the recovery is over.
454*62c56f98SSadaf Ebrahimi
455*62c56f98SSadaf Ebrahimi### Concrete format of the transaction list file
456*62c56f98SSadaf Ebrahimi
457*62c56f98SSadaf EbrahimiThe transaction list file contains a [fixed header](#transaction-list-header-format) followed by a list of [fixed-size elements](#transaction-list-element-format).
458*62c56f98SSadaf Ebrahimi
459*62c56f98SSadaf EbrahimiThe file uid is `PSA_CRYPTO_ITS_TRANSACTION_LIST_UID` = 0xffffff53.
460*62c56f98SSadaf Ebrahimi
461*62c56f98SSadaf Ebrahimi#### Transaction list header format
462*62c56f98SSadaf Ebrahimi
463*62c56f98SSadaf Ebrahimi* Version (2 bytes): 0x0003. (Chosen to differ from the first two bytes of a [dynamic secure element transaction file](#dynamic-secure-element-transaction-file), to reduce the risk of a mix-up.)
464*62c56f98SSadaf Ebrahimi* Key name size (2 bytes): `sizeof(psa_storage_uid_t)`. Storing this size avoids reading bad data if Mbed TLS is upgraded to a different integration that names keys differently.
465*62c56f98SSadaf Ebrahimi
466*62c56f98SSadaf Ebrahimi#### Transaction list element format
467*62c56f98SSadaf Ebrahimi
468*62c56f98SSadaf EbrahimiIn practice, there will rarely be more than one active transaction at a time, so the size of an element is not critical for efficiency. Therefore, in addition to the key identifier which is required, we add some potentially useful information in case it becomes useful later. We do not put the driver key identifier because its size is not a constant.
469*62c56f98SSadaf Ebrahimi
470*62c56f98SSadaf Ebrahimi* Key id: `sizeof(psa_storage_uid_t)` bytes.
471*62c56f98SSadaf Ebrahimi* Key lifetime: 4 bytes (`sizeof(psa_key_lifetime_t)`). Currently unused during recovery.
472*62c56f98SSadaf Ebrahimi* Operation type: 1 byte. Currently unused during recovery.
473*62c56f98SSadaf Ebrahimi    * 0: destroy key.
474*62c56f98SSadaf Ebrahimi    * 1: import key.
475*62c56f98SSadaf Ebrahimi    * 2: generate key.
476*62c56f98SSadaf Ebrahimi    * 3: derive key.
477*62c56f98SSadaf Ebrahimi    * 4: import key.
478*62c56f98SSadaf Ebrahimi* Padding: 3 bytes. Reserved for future use. Currently unused during recovery.
479*62c56f98SSadaf Ebrahimi
480*62c56f98SSadaf Ebrahimi#### Dynamic secure element transaction file
481*62c56f98SSadaf Ebrahimi
482*62c56f98SSadaf EbrahimiNote that the code base already references a “transaction file” (`PSA_CRYPTO_ITS_TRANSACTION_UID` = 0xffffff54), used by dynamic secure elements (feature enabled with `MBEDTLS_PSA_CRYPTO_SE_C`). This is a deprecated feature that has not been fully implemented: when this feature is enabled, the transaction file gets written during transactions, but if it exists when PSA crypto starts, `psa_crypto_init()` fails because [recovery has never been implemented](https://github.com/ARMmbed/mbed-crypto/issues/218).
483*62c56f98SSadaf Ebrahimi
484*62c56f98SSadaf EbrahimiFor the new kind of secure element driver, we pick a different file name to avoid any mixup.
485*62c56f98SSadaf Ebrahimi
486*62c56f98SSadaf Ebrahimi## Testing key management in secure elements
487*62c56f98SSadaf Ebrahimi
488*62c56f98SSadaf Ebrahimi### Instrumentation for checking the storage invariant
489*62c56f98SSadaf Ebrahimi
490*62c56f98SSadaf Ebrahimi#### Test hook locations
491*62c56f98SSadaf Ebrahimi
492*62c56f98SSadaf EbrahimiWhen `MBEDTLS_TEST_HOOKS` is enabled, each call to `psa_its_set()` or `psa_its_remove()` also calls a test hook, passing the file UID as an argument to the hook.
493*62c56f98SSadaf Ebrahimi
494*62c56f98SSadaf EbrahimiWhen a stateful secure element driver is present in the build, we use this hook to verify that the storage respects the [storage invariant](#chosen-storage-invariant). In addition, if there is some information about key ongoing operation (set explicitly by the test function as a global variable in the test framework), the hook tests that the content of the storage is compatible with the ongoing operation.
495*62c56f98SSadaf Ebrahimi
496*62c56f98SSadaf Ebrahimi#### Test hook behavior
497*62c56f98SSadaf Ebrahimi
498*62c56f98SSadaf EbrahimiThe storage invariant check cannot check all keys in storage, and does not need to (for example, it would be pointless to check anything about transparent keys). It checks the following keys:
499*62c56f98SSadaf Ebrahimi
500*62c56f98SSadaf Ebrahimi* When invoked from the test hook on a key file: on that key.
501*62c56f98SSadaf Ebrahimi* When invoked from the test hook on the transaction file: on all the keys listed in the transaction file.
502*62c56f98SSadaf Ebrahimi* When invoked from a test secure element: on the specified key.
503*62c56f98SSadaf Ebrahimi
504*62c56f98SSadaf Ebrahimi#### Test hook extra data
505*62c56f98SSadaf Ebrahimi
506*62c56f98SSadaf EbrahimiSome tests set global variables to indicate which persistent keys they manipulate. We instrument at least some of these tests to also indicate what operation is in progress on the key. See the GitHub issues or the source code for details.
507*62c56f98SSadaf Ebrahimi
508*62c56f98SSadaf Ebrahimi### Testing of transaction recovery
509*62c56f98SSadaf Ebrahimi
510*62c56f98SSadaf EbrahimiWhen no secure element driver is present in the build, the presence of a transaction list file during initialization is an error.
511*62c56f98SSadaf Ebrahimi
512*62c56f98SSadaf Ebrahimi#### Recovery testing process
513*62c56f98SSadaf Ebrahimi
514*62c56f98SSadaf EbrahimiWhen the stateful test secure element driver is present in the build, we run test cases on a representative selection of states of the internal storage and the test secure element. Each test case for transaction recovery has the following form:
515*62c56f98SSadaf Ebrahimi
516*62c56f98SSadaf Ebrahimi1. Create the initial state:
517*62c56f98SSadaf Ebrahimi    * Create a transaction list file with a certain content.
518*62c56f98SSadaf Ebrahimi    * Create key files that we want to have in the test.
519*62c56f98SSadaf Ebrahimi    * Call the secure element test driver to create keys without going throught the PSA API.
520*62c56f98SSadaf Ebrahimi2. Call `psa_crypto_init()`. Expect success if the initial state satisfies the [storage invariant](#chosen-storage-invariant) and failure otherwise.
521*62c56f98SSadaf Ebrahimi3. On success, check that the expected keys exist, and that keys that are expected to have been destroyed by recovery do not exist.
522*62c56f98SSadaf Ebrahimi4. Clean up the storage and the secure element test driver's state.
523*62c56f98SSadaf Ebrahimi
524*62c56f98SSadaf Ebrahimi#### States to test recovery on
525*62c56f98SSadaf Ebrahimi
526*62c56f98SSadaf EbrahimiFor a given key located in a secure element, the following combination of states are possible:
527*62c56f98SSadaf Ebrahimi
528*62c56f98SSadaf Ebrahimi* Key file: present, absent.
529*62c56f98SSadaf Ebrahimi* Key in secure element: present, absent.
530*62c56f98SSadaf Ebrahimi* Key in the transaction file: no, creation (import), destruction.
531*62c56f98SSadaf Ebrahimi
532*62c56f98SSadaf EbrahimiWe test all $2 \times 2 \times 3 = 12$ possibilities, each in its own test case. In each case, call the test function that checks the storage invariant and check that its result is as expected. Then, if the storage invariant is met, follow the [recovery testing process](#recovery-testing-process).
533*62c56f98SSadaf Ebrahimi
534*62c56f98SSadaf EbrahimiIn addition, have at least one positive test case for each creation method other than import, to ensure that we don't reject a valid value.
535*62c56f98SSadaf Ebrahimi
536*62c56f98SSadaf EbrahimiNote: testing of a damaged filesystem (including a filesystem that doesn't meet the invariant) is out of scope of the present document.
537