Open athoelke opened 1 year ago
Working through the proposal to construct a draft PR, I notice a handful of issues that are worth resolving:
The statements about what functionality does, and does not, required PSA_CRYPTO_SUBSYSTEM_RANDOM
seem particularly MbedTLS-based (not surprising, this is where the above text was taken from):
Only the following operations are guaranteed not to require this subsystem:
- Hash operations;
- Signature verification operations.
Currently, symmetric decryption for ciphers and AEAD, and MAC operations do not require the random generator. This may change in future versions of the library or when the operations are performed by a driver.
Is there anything we could or should state categorically about this subsystem for the API specification?
The definition of psa_crypto_init_subsystem()
includes error codes that are not currently listed against psa_crypto_init()
in the specification (although I see these are listed there in the mbedtls doxygen comments):
PSA_ERROR_INSUFFICIENT_STORAGE
PSA_ERROR_HARDWARE_FAILURE
PSA_ERROR_STORAGE_FAILURE
PSA_ERROR_DATA_INVALID
PSA_ERROR_DATA_CORRUPT
Are these all useful? - if so, I presume they should be added to psa_crypto_init()
in the spec?
What behavior should be specified for calls to psa_crypto_init_subsystem()
when the subsystem
parameter includes bits that are not recognized/supported by the implementation? We could do either:
a) Ignore unrecognized bits - in effect these unknown subsystems are 'already initialized', as they do not need further initialization. b) Report an INVALID_ARGUMENT or NOT_SUPPORTED error if unrecognized/unsupported bits are set.
I think I prefer (a), as this fits a model of usage where a caller does not have to know implementation details (which subsystems are implemented) to use the API. But there might be a good argument for (b) that I haven't considered....
Almost all function calls can return PSA_ERROR_BAD_STATE
with the description:
The library requires initializing by a call to
psa_crypto_init()
.
With the addition of psa_crypto_init_subsystem()
, should we:
Leave this text as is, even if a call to psa_crypto_init_subsystem()
with a suitable parameter value would also have the required effect.
Generalize the statement to something like the following with a section reference:
The library requires initializing. See Library initialization.
Enhance each instance to describe both psa_crypto_init()
and psa_crypto_init_subsystem()
(without providing subsystem details - as those may be implementation defined).
My current thinking is to do (2).
The statements about what functionality does, and does not, required PSA_CRYPTO_SUBSYSTEM_RANDOM seem particularly MbedTLS-based
I think there are two plausible choices here. One is to make it entirely implementation-defined. The other is what I made a guarantee in Mbed TLS, which is that you can verify a signature before randomness is available. The second choice removes doubt that in a bootloader, you can verify the signature of the next bit (which may include drivers for other mechanisms).
The definition of psa_crypto_init_subsystem() includes error codes that are not currently listed against psa_crypto_init() in the specification
These are storage-related failures. They can happen if a driver stores persistent data which it reads during initialization, or if there is a secure element with key storage that requires transaction recovery during startup. I think they should be added to psa_crypto_init
(but even if they aren't, implementations are allowed to use these error codes since these are implementation-specific concerns, as long as drivers aren't an official PSA feature).
What behavior should be specified for calls to psa_crypto_init_subsystem() when the subsystem parameter includes bits that are not recognized/supported by the implementation?
My thinking was: if you're requesting a specific feature, you really need the implementation to support it. But that's in terms of what's available to the application, not in terms of what the implementation does internally. For example, MBEDTLS_PSA_CRYPTO_SUBSYSTEM_ACCELERATORS_INDEX
means “I can do operations with transparent keys”, and an implementation that doesn't support accelerator drivers would just make it a successful no-op.
To avoid requesting features that are not supported by the implementation, you can request one feature at a time and ignore failures (a failure of partial initialization leaves the previously-initialized subsystems available). Also, we could standardize MBEDTLS_PSA_CRYPTO_ALL_SUBSYSTEMS
as a standard name having an implementation-defined value.
(TL,DR: leaning towards b.)
Almost all function calls can return PSA_ERROR_BAD_STATE with the description:
The library requires initializing by a call to psa_crypto_init().
We need to describe somewhere what you can do with various subsystems initialized. I don't have a strong opinion as to where. To some extent, this will be implementation-defined: for example only algorithms that require randomness are guaranteed to fail if RANDOM isn't initialized (and even then, an implementation may initialize RANDOM implicitly, for example a client in a client-server implementation will typically have everything available as soon as COMMUNICATION is established).
The statements about what functionality does, and does not, required PSA_CRYPTO_SUBSYSTEM_RANDOM seem particularly MbedTLS-based
I think there are two plausible choices here. One is to make it entirely implementation-defined. The other is what I made a guarantee in Mbed TLS, which is that you can verify a signature before randomness is available. The second choice removes doubt that in a bootloader, you can verify the signature of the next bit (which may include drivers for other mechanisms).
I see that, and this is already reflected in the existing specification text in the 'Note' here regarding initialization of an RNG. I think this use case is important enough to keep a specification-level requirement for hashes and signature verification not requiring the RANDOM subsystem.
What behavior should be specified for calls to psa_crypto_init_subsystem() when the subsystem parameter includes bits that are not recognized/supported by the implementation?
... (TL,DR: leaning towards b.)
Do we want applications that want to use this facility to need to write code like this:
psa_status_t r = psa_crypto_init_subsystem(PSA_CRYPTO_SUBSYSTEM_COMMUNICATION);
if (r != PSA_SUCCESS && r !=PSA_ERROR_INVALID_ARGUMENT && r !=PSA_ERROR_NOT_SUPPORTED) {
return ERR_INIT_FAILED;
}
... just in case they are compiled against a client-server implementation, and a non-client-server implementation is permitted to report any of SUCCESS, INVALID_ARGUMENT, or NOT_SUPPORTED in this situation?
I think I am leaning toward an in-between approach:
We need to describe somewhere what you can do with various subsystems initialized. I don't have a strong opinion as to where. To some extent, this will be implementation-defined: for example only algorithms that require randomness are guaranteed to fail if RANDOM isn't initialized (and even then, an implementation may initialize RANDOM implicitly, for example a client in a client-server implementation will typically have everything available as soon as COMMUNICATION is established).
I have been reworking the text that is currently in the description of psa_crypto_init()
as an introduction to the Library initialization section, but expanded to describe both one-shot and partial initialization. This previously warned that what can be done prior to calling psa_crypto_init()
is implementation-defined. I am adjusting that to describe the implementation-defined nature of the relationship between subsystem initialization and functionality.
- For defined (in the spec) subsystem values, the implementation returns SUCCESS if either (a) it has no such subsystem to initialize, or (b) it has successfully initialized that subsystem.
- For undefined subsystem values, the implementation should return INVALID_ARGUMENT.
Yes, that was my intent. For example COMMUNICATION is effectively always enabled in a non-client-server implementation, and init(COMMUNICATION) should always succeed.
psa_crypto_init()
is allowed to, and even encouraged to, initialize the RNG. This allows many implementations to provide a RNG that will never fail. (Implementations that comply to some security standards will still need to fail RNG calls if the entropy source fails, but a CSPRNG that has been seeded once can be good forever under reasonable security requirements.)This is a problem on systems that don't have an entropy source available, or where the entropy source is not yet available at the time the application wants to call
psa_crypto_init()
. The typical scenario would be a device or an application that only wants to calculate hashes and verify signatures, for example a bootloader.An application that only wants to calculate hashes might get away with insisting that the implementation accepts
psa_hash_xxx()
calls beforepsa_crypto_init()
, although this is strict non-portable. An application that wants to verify signatures needs the keystore to be available, and it's less reasonable to require this to happen withoutpsa_crypto_init()
. And even hash drivers may need some initialization. And client-server implementations may needpsa_crypto_init()
to establish the communication between the client and the server.There are use cases for doing a partial initialization of the PSA crypto subsystem: initialize basic functionality, initialize drivers, maybe initialize the key store, but do not initialize the RNG. This behavior is currently implementation-specific, but it makes sense to standardize it.
The following API definition is based on the current PR proposal for Mbed-TLS: https://github.com/Mbed-TLS/mbedtls/pull/6636.
Proposed API
psa_crypto_init
(function)Amend the description to also refer to the fine-grained initialization control provided by
psa_crypto_init_subsystem()
.psa_crypto_subsystem_t
(type)The designation of a subsystem of the PSA Crypto implementation.
Value of this type are masks of
PSA_CRYPTO_SUBSYSTEM_xxx
constants.PSA_CRYPTO_SUBSYSTEM_COMMUNICATION
(macro)Crypto subsystem identifier for the communication with the server, if this is a client that communicates with a server where the key store is located.
In a client-server implementation, this subsystem is necessary before any API function other than library initialization, deinitialization and functions accessing local data structures such as key attributes.
In a library implementation, initializing this subsystem does nothing and succeeds.
PSA_CRYPTO_SUBSYSTEM_KEYS
(macro)Crypto subsystem identifier for the key store in memory.
Initializing this subsystem allows creating, accessing and destroying volatile keys in the default location, i.e. keys with the lifetime
PSA_KEY_LIFETIME_VOLATILE
.Persistent keys also require
PSA_CRYPTO_SUBSYSTEM_STORAGE
. Keys in other locations also requirePSA_CRYPTO_SUBSYSTEM_SECURE_ELEMENTS
.PSA_CRYPTO_SUBSYSTEM_STORAGE
(macro)Crypto subsystem identifier for access to keys in storage.
Initializing this subsystem as well as
PSA_CRYPTO_SUBSYSTEM_KEYS
allows creating, accessing and destroying persistent keys.Persistent keys in secure elements also require
PSA_CRYPTO_SUBSYSTEM_SECURE_ELEMENTS
.PSA_CRYPTO_SUBSYSTEM_ACCELERATORS
(macro)Crypto subsystem identifier for accelerator drivers.
Initializing this subsystem calls the initialization entry points of all registered accelerator drivers.
Initializing this subsystem allows cryptographic operations that are implemented via an accelerator driver.
PSA_CRYPTO_SUBSYSTEM_SECURE_ELEMENTS
(macro)Crypto subsystem identifier for secure element drivers.
Initializing this subsystem calls the initialization entry points of all registered secure element drivers.
Initializing this subsystem as well as
PSA_CRYPTO_SUBSYSTEM_KEYS
allows creating, accessing and destroying keys in a secure element (i.e. keys whose location is notPSA_KEY_LOCATION_LOCAL_STORAGE
).PSA_CRYPTO_SUBSYSTEM_RANDOM
(macro)Crypto subsystem identifier for the random generator.
Initializing this subsystem initializes all registered entropy drivers and accesses the registered entropy sources.
Initializing this subsystem is necessary for
psa_generate_random()
,psa_generate_key()
, as well as some operations using private or secret keys. Only the following operations are guaranteed not to require this subsystem:Currently, symmetric decryption (authenticated or not) and MAC operations do not require the random generator. This may change in future versions of the library or when the operations are performed by a driver.
PSA_CRYPTO_SUBSYSTEM_BUILTIN_KEYS
(macro)Crypto subsystem identifier for access to built-in keys.
Initializing this subsystem as well as
PSA_CRYPTO_SUBSYSTEM_KEYS
allows access to built-in keys.psa_crypto_init_subsystem
(function)Partial library initialization.
subsystem
PSA_CRYPTO_SUBSYSTEM_xxx
values, or a bitwise-or of them.psa_status_t
PSA_SUCCESS
PSA_ERROR_INSUFFICIENT_MEMORY
PSA_ERROR_INSUFFICIENT_STORAGE
PSA_ERROR_COMMUNICATION_FAILURE
PSA_ERROR_HARDWARE_FAILURE
PSA_ERROR_CORRUPTION_DETECTED
PSA_ERROR_INSUFFICIENT_ENTROPY
PSA_ERROR_STORAGE_FAILURE
PSA_ERROR_DATA_INVALID
PSA_ERROR_DATA_CORRUPT
Description
Applications may call this function on the same subsystem more than once. Once a call succeeds, subsequent calls with the same subsystem are guaranteed to succeed.
Initializing a subsystem may initialize other subsystems if the implementations needs them internally. For example, in a typical client-server implementation,
PSA_CRYPTO_SUBSYSTEM_COMMUNICATION
is required for all other subsystems, and therefore initializing any other subsystem also initializesPSA_CRYPTO_SUBSYSTEM_COMMUNICATION
.Calling
psa_crypto_init()
is equivalent to callingpsa_crypto_init_subsystem()
on all the available subsystems.PSA_CRYPTO_SUBSYSTEM_xxx
values. If the initialization of one subsystem fails, it is unspecified whether other requested subsystems are initialized or not.