Side meeting: PSA as an API @ Summit

There as a side meeting this morning on using PSA as an API; here are notes taken from it (sorry for the gaps):

RIOT summit 2022: Security break-out

MCR taking the moderation goggles.

MCR: Care about ubiquitous cryptography, that makes maximum use of available hardware. (It’s faster, it’s more power efficient, and security advantages from storing private key deeply in hardware). Includes secure and measured boot, firmware updates, onboarding, remote attestation. All works from private keys provisioned in factory, and public keys being provisioned. Want this on RIOT independent of whether or not secure element is available. (There’s always a secure element, sometimes it’s just the whole module.) Thus, should reference private keys by reference and not value, but also load them in such a way they persist across flashes / firmware upgrades etc. I’ve noticed if people don’t use security regularly it becomes “security last”, and it stays off. Lena has done awesome job with PSA API, should accept it and go a couple steps further with it. All crypto users should use that, want cipher modules disappear. This might also simplify things.

(MCR: Also have second half, but let’s go ahead)

Lena: PR is pusehd since Friday, some more to do. Ciphers, hashes and ECs are in. […] and also nRF hardware accelerator is available, and one secure element (partially). PSA has integrated key management, identifier-based.

chrysn: Conversion APIs? Lena: Weierstrass and Montgomery are both supported, not sure about the conversion. Göran: We’re using the same keys for static-static derivation and signature. With EdDSA, these use different representations. Lena: Could store them independently. chrysn: So there’s a way; thanks, so it works by storing to keys. MCR: I understood it works even when the hardware does convert, so even better – provided the hardware can do the conversion.

MCR: For with no secure element, we need to access the secret keys to persist them.
Lena: PSA also has specs for secure storage API. Maybe look at that?
Koen: Think MCR is talking about nitty-gritty flash reservation stuff.
MCR: Yes. Describe where in flash things are, and tools we use for flashing need to work with that. Fork or upstream …, but need to mess with that.

chrysn: What does the PSA storage API need of the nitty-gritty details? Just store keys, or also signature counters / other counters?
MCR: CFG page (making EEPROM? out of interface?), but make that storing cheap. Carsten tells me, choice of CBOR indefinite map is coincidental.
Koen: There are some that erase to 0.
chrysn: It’s more complex; let’s not try not to solve it here.

Koen: back to PSA. What’s experience around porting things to PSA? Lot of code? Lena: Depends. For one module, just had to convert some drives. For hardware driver, it was more. MCR: Somewhere I saw switch statement mapping types. Lena: Design decision, so we can combine multiple backends, and different algorithms wind up in different ones. MCR: Micro-optimization, can we just make them all the same value? Koen: Yes it’s a micro-optimization.

Koen: I think we can deprecate the rest.
MCR: Do we have anything that is not crypto-agile?
chrysn: These could still go to PSA.
MCR: Where it is happening, would we inadvertently pull in a bigger chunk of code that would have been acceptable?
Koen: Maybe FIDO. MCR: That should be agile.

MCR: What’d be the impact of having at least one verification method around?
Koen: Signature would suck.

Minimal for OTA update? A hash?
Koen: If we want to go non-standard crypto way, libhydrogen is an option.

chrysn: Is per-device encrypted manifests an option? That way, asymmetric would not be necesary any more.
MCR: I’s not 2010 any more.
chrysn: We’re talking about the options we have for devices that can’t afford generic asymmetric algorithms, so…

michel: want to make sure that the decision for PSA is no lock-in to hardware manufacturers features

Next steps?

Koen: Write down requirements from this meeting. Plan a meeting 1month from now.
Check ARM PSA secure key storage API
?: Align with IETF hackathon in November(?)?
?: Key storage is big topic … key manager??
Thomas: Asymmetric crypto is the narrow waist here …?

1 Like
  1. One task is to sort out the nitty-gritty for getting an “env” (to use the uboot term) stored in flash.
  2. review and merge the current PSA patch.
  3. consider what the path towards having the PSA code be enabled by default.
  4. consider what the minimum set of algorithms (hash, cipher, signature) could be.
  5. what other parts of PSA do we want?
  6. can we move all crypto/hash/cipher users in RIOT-OS to use PSA? Can we then eliminate some glue code inside PSA?

What are the requirements on the “env” with respect to flashing outside the bootloader? Would it be OK if all this only works when there is a cooperating bootloader around? (I think that’d simplify things greatly, and also open avenues for evolving the env).

11 posts were split to a new topic: Using storage across firmware revisions. (A Security API spin-off)

By “flashing outside the bootloader” I mean situations when a debugger is connected to the board and updates the firmware. If we can, at least to some extent, tolerate that the keys might not be persisted in such a situation (which we can’t guarantee anyway: A programmer can always be used to erase the flash), then I have a concrete proposal.

Ad “control over the bootloader”: If we have no control over the bootloader, there is little we can do to persist data; I think it’s a sane assumption to rely on support from the bootloader.

Proposal “red light, green light”

… named thusly just because I think it’s easiest to talk about concrete proposals when they also have concrete names, and this gives the application great flexibility in one phase (green), and requires it to “freeze” in other situations (red).

This describes a scheme for persisting data between firmware updates, which should be reasonably independent of the precise bootloader used (and only needs some support from it).

  • We define a data structure that can easily be embedded in bootloader metadata (similar to “version of the application” and “address where to jump from after the bootloader”). That data structure is part of the constant data in the firmware, and represents a map (or multimap) from keys to memory areas in flash. A mandatory entry there is a pointer to a memory area that indicates whether the data there is usable (possibly in the form of a checksum; the precise indication may need to depend on the type of flash used. This could maybe need tristte semantics: “good”, “I have no data” and “I have data in the journal, please reboot into this image to recover”).

    That data structure may be CBOR, but it might be easier to just use an array of (type, pointer) or (type, length, pointer) words, because may of the pointers would be constructed at build time by the linker, and the linker can’t easily build valid CBOR. (It’s possible in a packed struct, but a bit awkward).

  • The application can, at run time, use any scheme it likes for updating, journaling, wear leveling, checksumming and what-so-not. While they do that (which is during regular operation), they keep the memory pointed to by the bootloader marked as “invalid”. When the application reboots in presence of a new firmware image (or when it reboots into a bootloader that may or may not select a new firmware image, or even when it writes a new firmware image anticipating it could be power-cycled), it updates its flash content to a valid state of what its bootloader list points to.

    This means that the new firmware doesn’t need to know how to read the wear leveling / journal of the old version, it just uses the simple structure everyone agrees on. It also means that if a new firmware version comes “as a surprise”, it won’t be able to use the old counters, keys etc – but firmware does not come as a surprise unless you were just hooked up to a debugger (in which case the memory could just as well have been erased in full).

  • An application that comes up without its own data (or its own journal) valid will look at the other image’s list, and pick any data it can use from there. The image may alternatively be configured with a full identity of its own (not looking at any old versions), or with an opportunistic identity (overwritten at first start with anything found in persisted data)

The main advantage over the alternative schemes of the bootloader partitioning things is that we don’t need to decide once-and-for-all how much memory in the partitioning is to be set aside for persisted data. Applications can opt in and out of this over the lifetime of the device, even when the bootloader is not modifed, even though opting out will destroy the data (but then again the application could always do that). We many need a little help from the bootloader in selecting the images, just in case writes get racy (think “I’ve prepared the other image but still need to do one more change to the metadata; if we’re power cycled now, please boot into me once more to recover”), but that’ll become evident when looking more deeply into how the bootloader selects the image.

Possible implementation approaches:

  • “I don’t care but I’m a good sport” (which might be a good default): The application doesn’t use persisted data, but it will do one thing to aid other applications that’d come back and want the old data: It reserves some of its flash for a one-time rescue operation, checks at startup if that flash is in a valid state (it won’t be after initial flashing), and if not stupidly copies all data over. (Enabling this would require one indirecion in the bootloader metadata, which should be easy to afford).

  • “One KISS on every cheek”: Simple double buffering. Data is stored in a struct on one page, and all bootloader metadata pointers point into that struct. When any property is changed, the second page is erased, new data written there, then the original page is erased (just briefly putting the device into the I-can’t-boot-into-new-firmware mode, but you won’t do that during a firmware update anyway) and rewritten with the proper data. This costs two erases per data change, but it’s simple.

  • Any kind of wear leveling, log structured data storage and what so not goes here. FlashDB might be a good candidate. If the application uses a file system somewhere, that might also be an option.

  • “The water belongs to the tribe”: If a firmware comes up and gets clearance from whatever would give the clearance that the old image is dead, the application could use the full second image space for wear leveling of its data changes. Only before a new firmware file is written (as it is necessary anyway), it will consolidate the data into the one area pointed to in the bootloader metadata again.

Concrete example:

  • Assign numbers 101 for the CoJP key of the device as a pledge (in self-determined COSE cnf format), 102 as the sender sequence counter of that key (in 5-byte sequence number form), 201 as the IDevID (with a 16-byte length prefix) and 1023 as this flash memory’s “data is valid” indicator (2x 32 bit, valid is represented by “00000000ffffffff” as is practical for nRF52 style flashes).
  • The application uses KISS-on-every-cheek style storage. It allocates two pages in its own flash (using FLASH_WRITABLE_INIT), both typed struct persisted { uint8_t cojp_key[128]; uint8_t cojp_seqno[5]; uint16_t idevid_len; uint8_t idevid[200]; uint32_t valid[2]; }. As the image relies on all that data being available already, none of these pages are flashed with initialization values (and thus come up empty / invalid).
  • For its bootloader it deposits metadata [101, first_page.cojp_key, 102, first_page.cojp_seqno, 201, &first_page.idevid_len, 1023, first_page.valid ].
  • At startup, the application finds both its flash pages in an invalid (ffffffffffffffff) state, and looks at the earlier image’s data, which it copies into its own pages and marks it as valid.
  • When the application loads a new firmware image into the other flash page, it doesn’t need to go into “red mode” because it always is; the next firmware coming up will just read data from the addresses indicated in the bootloader metadata.

I have a proposal about #6: There are some platform drivers that don’t support full cryptographic operations, but only basic primitives (e.g. AES encryption of one 16 byte block), and everything else has to be done in software (e.g. cipher modes). When we think about moving to PSA completely, maybe we can restructure the code in a way that allows us to reuse some software operations in these cases.

Lena via RIOT wrote: > I have a proposal about #6: There are some platform drivers that don’t > support full cryptographic operations, but only basic primitives > (e.g. AES encryption of one 16 byte block), and everything else has to > be done in software (e.g. cipher modes). When we think about moving to > PSA completely, maybe we can restructure the code in a way that allows > us to reuse some software operations in these cases.

Yes, this is important for many reasons!

I moved the posts here on “storage across firmware revisions” to Using storage across firmware revisions. (A Security API spin-off)