Skip to content

The PSA driver layer sometimes makes untested assumptions on the API layer #7801

@gilles-peskine-arm

Description

@gilles-peskine-arm

The structure of the implementation of the PSA crypto API is:

  • API layer (mostly in psa_crypto.c): does the keystore lookup and some validation, then calls the dispatch layer.
  • Dispatch layer (in psa_crypto_driver_wrappers.c): a simple autogenerated thing that calls lower-level functions based on the available drivers and the choice of key and algorithm.
  • Driver layer (in psa_crypto_{hash,mac,ecp,rsa,...}.c): either a third-party driver or our built-in implementation.

We test the whole stack through test_suite_psa_crypto and other test suites. We test the API+dispatch layers with test drivers. We test our implementation of the driver layer via the whole stack, and we also test the underlying primitives that are implemented in low-level modules (aes.c, rsa.c, etc.), but we don't test the driver layer as such. So we don't know if the driver layer works correctly when it isn't invoked by our API dispatch layer.

We don't plan on making the dispatch layer available directly from application code, or in a third-party API layer implementation. But we do plan on making the dispatch layer available to other drivers, when a primitive needs another “lower-level” primitive — e.g. using a hash to implement a signature, or a MAC to implement a KDF. So we should test that.

Example of something that can go wrong:

  • Our API layer for MAC verifies that the output buffer is large enough, and passes the size of the MAC to the lower layer. This is perfectly fine since the size of the MAC is both large enough for the output (it's exactly the size of the output) and small enough to fit the allocated memory (the API layer verifies that). It allows the lower layer to not have to repeat the verification check.
  • Because the driver layer was not written with independent checks in mind, it relies on the fact that the output size parameter is exactly the length of the MAC. In particular, it sets the output parameter *mac_length to the value that it receives as mac_size. This is correct when invoked via the API layer, but not when invoked merely based on the assumptions of the driver interface, which only guarantee that the mac_size parameter is large enough for the hash.
  • We accidentally ran into this in the PBKDF2 code. PBKDF2 uses a MAC internally, and it calls the dispatch layer directly. This is exactly the sort of use cases we were thinking of for invoking the dispatch layer directly.

The goal of this issue is to design and implement a test plan for our driver implementations, to validate that they behave correctly when invoked from the dispatch layer. More precisely, the definition of done for this issue is:

  1. Write a specification that clearly indicates the responsibility of the generic layer vs drivers for argument checks. Note that this is overdue anyway for the sake of driver implementers!
  2. Design a test plan for our built-in driver implementations with respect to this specification. More generally, the tests could apply to any driver. Tooling to run the tests on third-party drivers are outside the scope of this issue, but we'll probably want to design the tests in such a way that this can be added easily.
  3. File issues to implement this test plan.

Metadata

Metadata

Assignees

No one assigned

    Labels

    component-cryptoCrypto primitives and low-level interfacescomponent-psaPSA keystore/dispatch layer (storage, drivers, …)size-lEstimated task size: large (2w+)

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions