Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

PSA unified driver prototype #3313

Conversation

gilles-peskine-arm
Copy link
Contributor

@gilles-peskine-arm gilles-peskine-arm commented May 6, 2020

Experiment with a unified driver interface and generated code.

Design overview

As a driver writer, you write a JSON file that describes which functions your driver provides and which algorithms and key types they support.

The function prototypes are mostly the same for transparent drivers (which take a cleartext key) and opaque drivers (which take a wrapped key or a key identifier). The key is represented as a byte buffer. Note that the interpretation of this byte buffer is different for transparent and opaque drivers.

Each opaque driver defines a location. If a key's lifetime indicates that its location is not the default location, the core dispatches operations to the opaque driver for that location. If a key is in the default location (transparent key), the core dispatches to the transparent driver.

Not implemented yet: a transparent driver should be able to indicate that it handles certain key types and algorithms, so that the corresponding built-in code is not included. Or some equivalent mechanism.

Status

  • Documentation: https://github.com/gilles-peskine-arm/mbedtls/blob/psa-unified-driver-prototype/docs/architecture/psa-driver-interface.md (incomplete but readable)
  • Stability: expect rebases, complete rewrites, abandonment. The general design of the interface is approved, but the interface still needs work in the particulars. The code is prototype-quality only and may be abandoned.
  • Completeness: one function for one driver type (psa_sign_hash for transparent drivers). Doesn't actually pass the key data correctly. No dispatch based on key type or algorithm. Some infrastructure missing for opaque drivers. Transparent drivers always fall back to the built-in code.
  • Code quality: quick-and-dirty
  • Robustness: are you kidding?
  • Tests: smoke

* Lower 8 bits: persistence level
    * 0: volatile
    * 1: persistent (default)
    * 2-127: persistent (reserved for future PSA specifications)
    * 128-254: persistent (reserved for vendors)
    * 255: read-only
* Upper 24 bits: location indicator
    * 0: built-in
    * 1: primary secure element
    * 2-0x7fffff: reserved for future PSA specifications
    * 0x800000-0xffffff: vendor-specific

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Drop lifetime_ or LIFETIME_ to make the names shorter. They're still
unambiguous.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Call persistence "default" because that is genuinely the default that
applications should use if they don't know better. It's slightly
misleading in that the default persistence when you create a key is
volatile, not this: "default" is the default persistence for
persistent keys, not the default persistence for keys in general. But
we haven't found a better name.

Introduce the term "primary local storage" to designate the default
storage location.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Applications need this to combine implementation-specific values of
persistence levels and location indicators.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
The experimental unified cryptoprocessor driver interface will be
gated by this configuration option.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Make it possible for other library functions to obtain a key's
attributes from a key slot pointer.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
New Python module to parse a driver description in JSON format. There
is a JSON schema in the Python code.

New Python script to generate a C header file with prototypes for the
functions that the driver needs to implement.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
New Python script that takes driver description files and generates C
code that calls the correct driver based on the key location, with
potential fallback on PSA_ERROR_NOT_SUPPORTED.

Only psa_sign_hash is implemented for now. Only function capabilites
are recognized for now, without taking algorithms and key types into
account. Both transparent and opaque drivers are supported.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Only supports deterministic ECDSA with SHA-256.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Pass the variable PSA_DRIVERS to indicate the JSON driver description
files to parse.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Document a partially unified interface for accelerator drivers and
secure element drivers.

Document how to use this interface in Mbed TLS.

Work in progress

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Implement PSA_EXPORT_KEY_PAIR_MAX_SIZE and
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE for RSA and ECC.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
Test PSA_EXPORT_KEY_PAIR_MAX_SIZE and
PSA_EXPORT_PUBLIC_KEY_MAX_SIZE for RSA and ECC.

Signed-off-by: Gilles Peskine <Gilles.Peskine@arm.com>
@kotkcy
Copy link

kotkcy commented Jun 14, 2020

My next open question:
how the kernel / this driver will process the events of the beginning (completion) of the use of crypto hardware to switch the crypto power domain?


If there are multiple available transparent drivers, the core tries them in turn until one is declared without a true `"fallback"` property or returns a status other than `PSA_ERROR_NOT_SUPPORTED`.

If a transparent driver function is part of a capability where the `"fallback"` property is false or omitted, the core should not include any other code for this capability, whether built in or in another transparent driver.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is fallback really a property of the accelerator? I'm thinking it would be more natural to have a global fallback option per feature set, for which it is the library end-user's responsibility to turn it on or off. It doesn't make sense that one accelerator plugin can ship with the fallback property set, and will pull in a whole bunch of fallback code for everyone using the plugin. Simultaneously, it doesn't make sense to ship a plugin without the fallback property set, since then the user cannot add the fallback bits he/she needs.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No fallback means “I can handle everything I declare”. Fallback means “I can't handle everything I declare”. The application has no way to know whether falling back is needed. So yes, it's a property of the driver.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Are the declarations not granular enough such that a driver should be able to define strictly what it supports?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hope they mostly are, but I'm sure a few exceptions will come up. Maybe a secure element with a limit on input size. Or an RSA accelerator that only works with e=65537.


#### Driver initialization

An opaque driver may declare an `"init"` function in a capability with no algorithm, key type or key size. If so, the driver calls this function once during the initialization of the PSA Crypto subsystem. If the init function of any driver fails, the initialization of the PSA Crypto subsystem fails.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Further down this section it states that For transparent drivers, the init function does not take any parameter., indicating transparent drivers would also be able to define an init function. Please update this paragraph accordingly.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.


For transparent drivers, the key buffer contains the key material, in the same format as defined for `psa_export_key()` and `psa_export_public_key()` in the PSA Cryptography API. For opaque drivers, the content of the key buffer is entirely up to the driver.

* For functions that involve a multi-part operation, the operation state type (`psa_XXX_operation_t`) is replaced by a driver-specific operation state type (*prefix*`_XXX_operation_t`).
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This means the definition for this type must be available at compilation time for the PSA Crypto core. Wouldn't it be better to have the driver define the expected size of the operation context, and allocate/pass an opaque byte buffer from the crypto core to the accelerator?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That would make it easier to write unsafe code where the only thing that ensures that the size is correct is a comment in the source code /*Remember to update acme.json when the size changes*/. And the size might be different on different systems.

If you really don't want to expose the type in a public header, expose a struct {uint8_t[N];} and you're on your own to ensure that N is correct, which would be the case anyway.

@nazrchorn
Copy link

nazrchorn commented Jun 18, 2020

Hi, I have proposition to change following behavior:
"Public key not stored
ECC key pairs are stored as the private key value only. The public key needs to be calculated from that."

In our design, ECC key pairs are stored encrypted in flash and during secure boot they are decrypted and imported to mbed crypto key storage.

Standard psa_import_key() function, always re-calculate public ECC key from the private one, that take too much time(150ms per key @ 50Mhz CM0 core). Taking into account that user already has a public key data - we just need quickly to put it to the key storage along with private part and significantly reduce boot time.

So I propose to support ECC key pair without re-calculation and add APIs for export/import of full key pair.

* `"hash_compute"` (transparent drivers only): calculation of a hash. Called by `psa_hash_compute()` and `psa_hash_compare()`. To verify a hash with `psa_hash_compare()`, the core calls the driver's `"hash_compute"` function and compares the result with the reference hash value.
* `"mac_compute"`: calculation of a MAC. Called by `psa_mac_compute()` and possibly `psa_mac_verify()`. To verify a mac with `psa_mac_verify()`, the core calls an applicable driver's `"mac_verify"` function if there is one, otherwise the core calls an applicable driver's `"mac_compute"` function and compares the result with the reference MAC value.
* `"mac_verify"`: verification of a MAC. Called by `psa_mac_verify()`. This function is mainly useful for drivers of secure elements that verify a MAC without revealing the correct MAC. Although transparent drivers may implement this function in addition to `"mac_compute"`, it is generally not useful because the core can call the `"mac_compute"` function and compare with the expected MAC value.
* `"cipher_encrypt"`: unauthenticated symmetric cipher encryption. Called by `psa_cipher_encrypt()`.
Copy link
Contributor

@stevew817 stevew817 Jun 18, 2020

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What about IV for single-op cipher calls?
Edit: I know it says "This will encrypt using a random IV" in the definition of psa_cipher_encrypt, but what about accelerators that don't have context, and require all data in one operation? Do they need to provide a multipart driver that returns an error if you call update on it more than once? That also breaks the API...


#### Operation family `"cipher_encrypt_multipart"`

TODO
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One thing I'd like to see 'fixed' when you get to the next specification, is the mixing in of padding modes, e.g. PSA_ALG_CBC_PKCS7. It doesn't make sense that the accelerator has to care about padding when doing encryption (i.e. insert padding up to next block boundary and output this last block), when the 'inverse' function cannot be made to care about padding in the current API design, meaning the layer above needs to be padding-aware either way.

Either the multipart_cipher_finish() API needs to be redesigned in order to support the accelerator 'subtracting' bytes from already-decrypted output, and padding becomes solely the accelerator's responsibility, or the accelerator API gets redefined such that the accelerator doesn't have to care about padding, and the layer above it will.

@gilles-peskine-arm
Copy link
Contributor Author

Superseded by:

  • PSA unified driver specification #3493 for the specification. There are still comments here that the currently posted version doesn't address, I'll make further commits in the coming hours or days.
  • Yet-to-come separate pull requests for the implementation.

@gilles-peskine-arm
Copy link
Contributor Author

Hi @nazrchorn

The current draft makes it an implementation for the core as to when to calculate the public key. It's optional whether psa_generate_key and psa_import_key calculate the public key and store it, or they calculate the public key on demand, or use some hybrid mechanism such as calculate-and-store-on-first-use.

I think that's the best choice given that it's generally the implementer or porter of the core who knows best how much storage is available and what the performance bottlenecks are on a given platform. But I'm open to improvements.

@bensze01 bensze01 modified the milestones: July 2020 Sprint, Driver interface: API design and prototype Jul 28, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
component-crypto Crypto primitives and low-level interfaces DO-NOT-MERGE needs-work
Projects
None yet
Development

Successfully merging this pull request may close these issues.

8 participants