blob: c894b92a14e8d50e0aa3e6ca01a69291a455b1e1 [file] [log] [blame] [view]
Gilles Peskinea42a8de2021-11-03 12:18:41 +01001Thread safety of the PSA key store
2==================================
3
4Analysis of the behavior of the PSA key store as of Mbed TLS 9202ba37b19d3ea25c8451fd8597fce69eaa6867.
5
6## Resources to protect
7
8### Global variables
9
10* `psa_crypto_slot_management::global_data.key_slots[i]`: see [“Key slots”](#key-slots).
11
12* `psa_crypto_slot_management::global_data.key_slots_initialized`:
13 * `psa_initialize_key_slots`: modification.
14 * `psa_wipe_all_key_slots`: modification.
15 * `psa_get_empty_key_slot`: read.
16 * `psa_get_and_lock_key_slot`: read.
17
18* `psa_crypto::global_data.rng`: depends on the RNG implementation. See [“Random generator”](#random-generator).
19 * `psa_generate_random`: query.
20 * `mbedtls_psa_crypto_configure_entropy_sources` (only if `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled): setup. Only called from `psa_crypto_init` via `mbedtls_psa_random_init`, or from test code.
21 * `mbedtls_psa_crypto_free`: deinit.
22 * `psa_crypto_init`: seed (via `mbedtls_psa_random_seed`); setup via `mbedtls_psa_crypto_configure_entropy_sources.
23
24* `psa_crypto::global_data.{initialized,rng_state}`: these are bit-fields and cannot be modified independently so they must be protected by the same mutex. The following functions access these fields:
25 * `mbedtls_psa_crypto_configure_entropy_sources` [`rng_state`] (only if `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled): read. Only called from `psa_crypto_init` via `mbedtls_psa_random_init`, or from test code.
26 * `mbedtls_psa_crypto_free`: modification.
27 * `psa_crypto_init`: modification.
28 * Many functions via `GUARD_MODULE_INITIALIZED`: read.
29
30### Key slots
31
32#### Key slot array traversal
33
34“Occupied key slot” is determined by `psa_is_key_slot_occupied` based on `slot->attr.type`.
35
36The following functions traverse the key slot array:
37
38* `psa_get_and_lock_key_slot_in_memory`: reads `slot->attr.id`.
39* `psa_get_and_lock_key_slot_in_memory`: calls `psa_lock_key_slot` on one occupied slot.
40* `psa_get_empty_key_slot`: calls `psa_is_key_slot_occupied`.
41* `psa_get_empty_key_slot`: calls `psa_wipe_key_slot` and more modifications on one occupied slot with no active user.
42* `psa_get_empty_key_slot`: calls `psa_lock_key_slot` and more modification on one unoccupied slot.
43* `psa_wipe_all_key_slots`: writes to all slots.
44* `mbedtls_psa_get_stats`: reads from all slots.
45
46#### Key slot state
47
48The following functions modify a slot's usage state:
49
50* `psa_lock_key_slot`: writes to `slot->lock_count`.
51* `psa_unlock_key_slot`: writes to `slot->lock_count`.
52* `psa_wipe_key_slot`: writes to `slot->lock_count`.
53* `psa_destroy_key`: reads `slot->lock_count`, calls `psa_lock_key_slot`.
54* `psa_wipe_all_key_slots`: writes to all slots.
55* `psa_get_empty_key_slot`: writes to `slot->lock_count` and calls `psa_wipe_key_slot` and `psa_lock_key_slot` on one occupied slot with no active user; calls `psa_lock_key_slot` on one unoccupied slot.
56* `psa_close_key`: reads `slot->lock_count`; calls `psa_get_and_lock_key_slot_in_memory`, `psa_wipe_key_slot` and `psa_unlock_key_slot`.
57* `psa_purge_key`: reads `slot->lock_count`; calls `psa_get_and_lock_key_slot_in_memory`, `psa_wipe_key_slot` and `psa_unlock_key_slot`.
58
Andrzej Kurekeec6b2c2021-11-08 14:09:29 +010059**slot->attr access:**
60`psa_crypto_core.h`:
61* `psa_key_slot_set_flags` - writes to attr.flags
62* `psa_key_slot_set_bits_in_flags` - writes to attr.flags
63* `psa_key_slot_clear_bits` - writes to attr.flags
64* `psa_is_key_slot_occupied` - reads attr.type
65* `psa_key_slot_get_flags` - reads attr.flags
66
67`psa_crypto_slot_management.c`:
68* `psa_get_and_lock_key_slot_in_memory` - reads attr.id
69* `psa_get_empty_key_slot` - reads attr.lifetime
70* `psa_load_persistent_key_into_slot` - passes attr pointer to psa_load_persistent_key
71* `psa_load_persistent_key` - reads attr.id and passes pointer to psa_parse_key_data_from_storage
72* `psa_parse_key_data_from_storage` - writes to many attributes
73* `psa_get_and_lock_key_slot` - writes to attr.id, attr.lifetime, and attr.policy.usage
74* `psa_purge_key` - reads attr.lifetime, calls psa_wipe_key_slot
75* `mbedtls_psa_get_stats` - reads attr.lifetime, attr.id
76
77`psa_crypto.c`:
78* `psa_get_and_lock_key_slot_with_policy` - reads attr.type, attr.policy.
79* `psa_get_and_lock_transparent_key_slot_with_policy` - reads attr.lifetime
80* `psa_destroy_key` - reads attr.lifetime, attr.id
81* `psa_get_key_attributes` - copies all publicly available attributes of a key
82* `psa_export_key` - copies attributes
83* `psa_export_public_key` - reads attr.type, copies attributes
84* `psa_start_key_creation` - writes to the whole attr structure
85* `psa_validate_optional_attributes` - reads attr.type, attr.bits
86* `psa_import_key` - reads attr.bits
87* `psa_copy_key` - reads attr.bits, attr.type, attr.lifetime, attr.policy
88* `psa_mac_setup` - copies whole attr structure
89* `psa_mac_compute_internal` - copies whole attr structure
90* `psa_verify_internal` - copies whole attr structure
91* `psa_sign_internal` - copies whole attr structure, reads attr.type
92* `psa_assymmetric_encrypt` - reads attr.type
93* `psa_assymetric_decrypt` - reads attr.type
94* `psa_cipher_setup` - copies whole attr structure, reads attr.type
95* `psa_cipher_encrypt` - copies whole attr structure, reads attr.type
96* `psa_cipher_decrypt` - copies whole attr structure, reads attr.type
97* `psa_aead_encrypt` - copies whole attr structure
98* `psa_aead_decrypt` - copies whole attr structure
99* `psa_aead_setup` - copies whole attr structure
100* `psa_generate_derived_key_internal` - reads attr.type, writes to and reads from attr.bits, copies whole attr structure
101* `psa_key_derivation_input_key` - reads attr.type
102* `psa_key_agreement_raw_internal` - reads attr.type and attr.bits
Gilles Peskinea42a8de2021-11-03 12:18:41 +0100103
104TODO: change `psa_is_key_slot_occupied` to checking the id?
105
106#### Key slot content
107
108Other than what is used to determine the [“key slot state”](#key-slot-state), the contents of a key slot are only accessed as follows:
109
110* Modification during key creation (between `psa_start_key_creation` and `psa_finish_key_creation` or `psa_fail_key_creation`).
111* Destruction in `psa_wipe_key_slot`.
112* Read in many functions, between calls to `psa_lock_key_slot` and `psa_unlock_key_slot`.
113
Andrzej Kurekeec6b2c2021-11-08 14:09:29 +0100114**slot->key access:**
115* `psa_allocate_buffer_to_slot` - allocates key.data, sets key.bytes;
116* `psa_copy_key_material_into_slot` - writes to key.data
117* `psa_remove_key_data_from_memory` - writes and reads to/from key data
118* `psa_get_key_attributes` - reads from key data
119* `psa_export_key` - passes key data to psa_driver_wrapper_export_key
120* `psa_export_public_key` - passes key data to psa_driver_wrapper_export_public_key
121* `psa_finish_key_creation` - passes key data to psa_save_persistent_key
122* `psa_validate_optional_attributes` - passes key data and bytes to mbedtls_psa_rsa_load_representation
123* `psa_import_key` - passes key data to psa_driver_wrapper_import_key
124* `psa_copy_key` - passes key data to psa_driver_wrapper_copy_key, psa_copy_key_material_into_slot
125* `psa_mac_setup` - passes key data to psa_driver_wrapper_mac_sign_setup, psa_driver_wrapper_mac_verify_setup
126* `psa_mac_compute_internal` - passes key data to psa_driver_wrapper_mac_compute
127* `psa_sign_internal` - passes key data to psa_driver_wrapper_sign_message, psa_driver_wrapper_sign_hash
128* `psa_verify_internal` - passes key data to psa_driver_wrapper_verify_message, psa_driver_wrapper_verify_hash
129* `psa_asymmetric_encrypt` - passes key data to mbedtls_psa_rsa_load_representation
130* `psa_asymmetric_decrypt` - passes key data to mbedtls_psa_rsa_load_representation
131* `psa_cipher_setup ` - passes key data to psa_driver_wrapper_cipher_encrypt_setup and psa_driver_wrapper_cipher_decrypt_setup
132* `psa_cipher_encrypt` - passes key data to psa_driver_wrapper_cipher_encrypt
133* `psa_cipher_decrypt` - passes key data to psa_driver_wrapper_cipher_decrypt
134* `psa_aead_encrypt` - passes key data to psa_driver_wrapper_aead_encrypt
135* `psa_aead_decrypt` - passes key data to psa_driver_wrapper_aead_decrypt
136* `psa_aead_setup` - passes key data to psa_driver_wrapper_aead_encrypt_setup and psa_driver_wrapper_aead_decrypt_setup
137* `psa_generate_derived_key_internal` - passes key data to psa_driver_wrapper_import_key
138* `psa_key_derivation_input_key` - passes key data to psa_key_derivation_input_internal
139* `psa_key_agreement_raw_internal` - passes key data to mbedtls_psa_ecp_load_representation
140* `psa_generate_key` - passes key data to psa_driver_wrapper_generate_key
141
Gilles Peskinea42a8de2021-11-03 12:18:41 +0100142### Random generator
143
144The PSA RNG can be accessed both from various PSA functions, and from application code via `mbedtls_psa_get_random`.
145
146With the built-in RNG implementations using `mbedtls_ctr_drbg_context` or `mbedtls_hmac_drbg_context`, querying the RNG with `mbedtls_xxx_drbg_random()` is thread-safe (protected by a mutex inside the RNG implementation), but other operations (init, free, seed) are not.
147
148When `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is enabled, thread safety depends on the implementation.
149
150### Driver resources
151
152Depends on the driver. The PSA driver interface specification does not discuss whether drivers must support concurrent calls.
153
154## Simple global lock strategy
155
156Have a single mutex protecting all accesses to the key store and other global variables. In practice, this means every PSA API function needs to take the lock on entry and release on exit, except for:
157
158* Hash function.
159* Accessors for key attributes and other local structures.
160
161Note that operation functions do need to take the lock, since they need to prevent the destruction of the key.
162
163Note that this does not protect access to the RNG via `mbedtls_psa_get_random`, which is guaranteed to be thread-safe when `MBEDTLS_PSA_CRYPTO_EXTERNAL_RNG` is disabled.
164
165This approach is conceptually simple, but requires extra instrumentation to every function and has bad performance in a multithreaded environment since a slow operation in one thread blocks unrelated operations on other threads.
166
167## Global lock excluding slot content
168
169Have a single mutex protecting all accesses to the key store and other global variables, except that it's ok to access the content of a key slot without taking the lock if one of the following conditions holds:
170
171* The key slot is in a state that guarantees that the thread has exclusive access.
172* The key slot is in a state that guarantees that no other thread can modify the slot content, and the accessing thread is only reading the slot.
173
174Note that a thread must hold the global mutex when it reads or changes a slot's state.
175
176### Slot states
177
178For concurrency purposes, a slot can be in one of three states:
179
180* UNUSED: no thread is currently accessing the slot. It may be occupied by a volatile key or a cached key.
181* WRITING: a thread has exclusive access to the slot. This can only happen in specific circumstances as detailed below.
182* READING: any thread may read from the slot.
183
184A high-level view of state transitions:
185
186* `psa_get_empty_key_slot`: UNUSED → WRITING.
187* `psa_get_and_lock_key_slot_in_memory`: UNUSED or READING → READING. This function only accepts slots in the UNUSED or READING state. A slot with the correct id but in the WRITING state is considered free.
188* `psa_unlock_key_slot`: READING → UNUSED or READING.
189* `psa_finish_key_creation`: WRITING → READING.
190* `psa_fail_key_creation`: WRITING → UNUSED.
191* `psa_wipe_key_slot`: any → UNUSED. If the slot is READING or WRITING on entry, this function must wait until the writer or all readers have finished. (By the way, the WRITING state is possible if `mbedtls_psa_crypto_free` is called while a key creation is in progress.) See [“Destruction of a key in use”](#destruction of a key in use).
192
193The current `state->lock_count` corresponds to the difference between UNUSED and READING: a slot is in use iff its lock count is nonzero, so `lock_count == 0` corresponds to UNUSED and `lock_count != 0` corresponds to READING.
194
195There is currently no indication of when a slot is in the WRITING state. This only happens between a call to `psa_start_key_creation` and a call to one of `psa_finish_key_creation` or `psa_fail_key_creation`. This new state can be conveyed by a new boolean flag, or by setting `lock_count` to `~0`.
196
197### Destruction of a key in use
198
199Problem: a key slot is destroyed (by `psa_wipe_key_slot`) while it's in use (READING or WRITING).
200
201TODO: how do we ensure that? This needs something more sophisticated than mutexes (concurrency number >2)! Even a per-slot mutex isn't enough (we'd need a reader-writer lock).