Issues with `PSA_STORAGE_FLAG_WRITE_ONCE`
There are a couple of concerns with the PSA_STORAGE_FLAG_WRITE_ONCE data item flag, which have led me to wonder if this should be deprecated in a future version of the API.
Is there a good use case?
What is the security benefit of using PSA_STORAGE_FLAG_WRITE_ONCE. What is the use case for which this is the answer? — does it provided any benefit over "stops me accidentally erasing data that I must not delete"?
The API itself guarantees that all of your stored data cannot be deleted by anyone other than the application/client that created it. So this flag only stops you from deleting your own data.
Optional, but undiscoverable?
The specification hints that this flag might not be supported, in §2.3 The Protected Storage API:
However, it MUST treat the
PSA_STORAGE_FLAG_WRITE_ONCEflag as definitive if it is supported.
but in §2.4 The Internal TrustedStorage API:
However, it must honor the
PSA_STORAGE_FLAG_WRITE_ONCEflag.
However, the specification does not indicate exactly how the caller can determine if this flag is supported or not. It is not clear if the implementation will respond to psa_its_set() with PSA_ERROR_NOT_SUPPORTED,or by not setting the PSA_STORAGE_FLAG_WRITE_ONCE flag in the item info returned by psa_its_get_info():
in §2.3 again:
When reporting meta data,
psa_ps_get_info()should report the actual protection level applied and not the requested level.
in §5.3.3. psa_its_set:
PSA_ERROR_NOT_SUPPORTED: The operation failed because one or more of the flags provided increate_flagsis not supported or is not valid.
Hard to test
When an implementation supports this flag, then a test case will create undeletable data items. This will prevent the test case from running again, or results in slowly filling up storage, unless the product provides a mechanism to delete the write-once data items. Such a mechanism defeats the purpose of this API flag
Given the cost for an implementation to support this, and without a clearly identified use case (threat) that requires this as a mitigation; would it be better to plan to remove this from the API in a future update, and deprecate it (recommending non-implementation) in the next version of the API?
I see that section 2.2 has the following text "13. The PSA_STORAGE_FLAG_WRITE_ONCE must be enforced when the PSA RoT Lifecycle of the device is SECURED or NON_PSA_ROT_DEBUG. It must not be enforced when the device is in the PSA_ROT_PROVISIONING state."
This suggests to me the intent was for the provisioning state to be able to provision and re-provision a value and make it immutable to any SECURED state service that might, in that state, be considered the owner. This means that there is the notion of properties changing between provisioning state (where the data is mutable) and secured state (where it's immutable). So it is a way of preventing "... me accidentally erasing data that I must not delete", which seems useful to me.
On the subject of testing, then write-once is a problem, but the same problem arises when the underlying storage is OTP. In such cases it must be recognised that such testing should be carried out solely on test parts.
This suggests to me the intent was for the provisioning state to be able to provision and re-provision a value and make it immutable to any SECURED state service that might, in that state, be considered the owner.
Perhaps this was why it was originally requested. However, I don't think that use case is still in scope, and this API is not a very suitable way to address that use case.
The PSA_ROT_PROVISIONING lifecycle state is specified for factory-provisioning of the "PRoT parameters" - in particular the ROTPK, IAK, HUK, device identity, etc. It was not envisaged that these would be natural candidates to expose via the ITS API:
- Most of them are secret keys, and the obvious access mechanism for runtime usage (where needed) is to provide them as 'built-in' or 'platform' keys accessed via the Crypto API from the specific Secure Service that requires them.
- The Crypto API does not have the concept of write-only keys. (This does not prevent an implementation from presenting some keys as 'already present' and 'read/use-only'.)
- Provisioning these via the Crypto API or ITS API requires that the owner of the key/data-item creates the key/data-item. The APIs have no concept of an owner, if the platform provides isolated execution environments, then the implementation is expected to provide each one with a private view of the stored keys/data.
- The expectation when developing the APIs, was that provisioning of these parameters is done using platform-specific service APIs, or special provisioning-state-only firmware. This would enable writing to storage locations that are not managed by the Crypto or ITS services for application-writable items.
If this is usable for RoT provisioning, then the documentation should state this explicitly, and perhaps it should be invalid to use the flag to create a data item once the device has left ROT_PROVISIONING state. Testing would be challenging/destructive as it requires starting with a device in provisioning state, and then locking it down to SECURED state, which might be irreversible for some device platforms.
I found just the one mention in the API document of ROT_PROVISIONING_STATE, but did not find a mention of PRoT Parameters, nor any of ROTPK, IAK, etc., examples. Thus the API, as I read it, would allow me to store any stuff I choose in secure storage. Using the same API and underlying implementation to provision (and make write once) has many code re-use benefits (more so if re-provisioning in a deployed device is supported), which are effectively denied by the expectation expressed in fourth bullet point. My reading of point 13 in section 2.2 suggests that this API has applicability when in some provisioning state.
Testing is a separate topic from the use of the API. Testing, if it is permanent (e.g. use of OTP) requires test parts but such testing will not be carried out on every production instance.
One of the principles with the API design is that the caller stores and retrieves its data, expecting that the implementation secures the data from entities outside of the caller's security boundary. This can be protecting it from other applications on the device, as well as from an attacker trying to read the data from storage.
With provisioning of data, there is often a provisioning component/service storing the data, and the operational component that is using the data. The current API requires that these are 'the same caller' from the perspective of the implementation of the API, otherwise the operational component has no visibility/access to the data stored by the provisioning component. If the provisioning component is supplying data for multiple runtime services, this effectively means all services have visibility of all data items. This is not an approach that supports security designs that isolate services from each other, or isolates the provisioning service from the operational services.
If the use case for provisioning data includes the ability to separate the provisioning service from the operational service(s), then the Storage implementation has to provide a privileged API to store data on behalf of another caller. This API would need to identify both the identity of the user of the data item, as well as the data item UID. The way a caller is identified is specific to the system, and thus there is no obvious way to create a simple generic API to provision data this way. With this use case, 'WRITE ONCE' is not the necessary attribute, as the write-protection can be based on which caller stored the provisioned data item.
If re-provisioning is part of this use case, then 'WRITE ONCE' is not a usable property, unless 're-provisioning' means returning the device to ROT_PROVISIONING state, when the 'WRITE ONCE' attribute could be ignored. But the intention of ROT_PROVISIONING is that this occurs within a secure [factory] environment, and was designed for PRoT parameter provisioning, not general application provisioning.
If we need neither isolation, nor re-provisioning, in the device, then 'WRITE ONCE' may provide a minimal protection against accidental (caused by software bugs) changes to provisioned data items, but has minimal effect on security of the device against a software-based attack. Without isolation, there are better ways to achieve the attack objective than tampering with provisioned data.
I tend to agree. If I am remembering correctly, the flag was added at the insistence of the team in Israel - who I believe are no loner with us.
I do not believe it is any less onerous for someone assessing the security to check that the WRITE ONCE flag is observed, than it is to check that the calling code never updates a value.
In general, anyone with the ability to update code that stores variables that must not be updated, and wanted to do so maliciously, would also be ale to update the ITS with a version that ignored it.
The only possible use case I can come up with is if you have a hypothetical ITS implementation that could access both fuse and mutable non-volatile storage, when the flag could act as a hint that a value was to be written to fuse. However, I do not believe anyone has implemented such a system, and I do not see it as compelling.