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

Provisioning roadmap #446

Open
chrysn opened this issue Sep 27, 2024 · 2 comments
Open

Provisioning roadmap #446

chrysn opened this issue Sep 27, 2024 · 2 comments

Comments

@chrysn
Copy link
Collaborator

chrysn commented Sep 27, 2024

Description

Around the flashing step we should have an option to customize what is on the device.

Whether that is part of the built source code (eg. through environment variables), linked in, flashed to a dedicated memory address, flashed through some code placed in RAM and executed there or done by the starting application while it communicates with the debugger is an implementation aspect that will need to be figured out, but may not be the same across all platforms.

Motivation

To make the usability aspects of CoAP (road map on #226) fly, we need some better provisioning.

Requirements

Concrete requirements are that

  1. Devices get unique keys (or key properties such as usage counters / sender IDs).
  2. When a device gets a key, its previous keys are revoked.
  3. Optionally, devices when re-flashed get the same keys again.

Item 1 is just security essentials1, item 2 is for practical reasons: If we want to express "All my devices may access this resource" without checking signatures, the public keys of all those devices need to be in some list, ideally distributed to other devices – which is practical for the 20-or-so distinct boards a typical developer ever flashes, but not for the 20k different boards those appear as if every flashing creates a new participant without revoking the old key. Item 3 is convenient if you don't want to limit "All my devices" to "All my devices so far", but update a device without revoking its permissions. (Using this is a trade-off, and I think that by that time one would rather use signed identities, but 1+2 enable it to the point where it is cheap enough).

Open questions

  • Will probe-rs or other debuggers help us?

    Not immediately: Unlike in debuggers specialized to concrete series (eg. those on EFM32 devices where I've just used that for years and took it for granted), there is no concept of a device identity that a flashing or debugging process can be aimed at – some chips have registers that could be read through a debug interface, some chips have an area in their in-SoC QSPI flash for that, some have nothing but could have an identifying chip on board (eg. the MAC of the soldered-on Ethernet module).

  • How does such customization fit in the laze process? Will the run target commonly used now, instead of being flash-and-attach, become flash-and-provision-and-attach? When (which I expect can be a step on the way) binaries are customized, how does the pre-flash provisioning preparation feed data back into the build process?

Are you planning to do it yourself in a pull request?

Yes

Footnotes

  1. Protocols nowadays sometimes have mitigation in place, so eg. in EDHOC (unlike in similar protocols like earlier versions of Tox) when someone else has your key they can't impersonate anyone else to the owner that key, but that is supposed to be an extra layer of security and not relied on.

@chrysn
Copy link
Collaborator Author

chrysn commented Sep 27, 2024

Some updates from a recent voice chat with @kaspar030:

  • As for acquiring device IDs, there we discussed the alternative of providing per-device names at provisioning time. We agree that those are tedious – but we won't need them if devices provide us with a device ID that can be communicated at least through some channel. So manual naming of devices is off the table. (Doesn't keep anyone from assigning nick names, but it's not a workflow we'd rely on).

  • Configuration storage will be available soon, so we can rely on that.

  • What we can or can't do at flash time will vary by platform, and we won't have a consistent programmer or communication channel. We'll have to rely on per-platform means of getting data out again.

    Which those channels are may need further discussion. We talked about how in an end user situation USB might be a means of getting out the data (the end user will not have a debugger when they receive a programmed device), but here we have an issue of trust:

    • Only provisioning done "by the programmer" (while the device is in a trusted environment) can be trusted by the programmer to be authentic. Over a later USB connection you can learn who the device claims to be, but would need some kind of remote attestation to be sure it runs the right firmware.

    • Provisioning may easily be a multi-stage process. A first step is flashing, then the firmware starts up and generates its key (discussed so far). But then later on, a second phase of provisioning can still happen over USB or whatever the user can use, and that may factor in data from an earlier stage.

    So I don't think we should necessarily drop some "provisioning via GDB / something RTTish" from the plan, especially because provisioning steps may need to be bidirectional (flash in program including root-of-trust, generate key, receive certificate). The early-provisioning may still need multiple channels (dunno, an ATMega doesn't have a debug port, so it's flash and then UART), but keeping that open to end-user needs may be excessive because the end user would need some earlier provisioning to have happened anyway.

@chrysn
Copy link
Collaborator Author

chrysn commented Sep 27, 2024

Concrete sketch of a plan:

  • When an application needs a per-device key, it is built to store that in its config storage, but no key is flashed (sure, could later be some hardware key store, but point is that it's empty).
  • When the application starts up and an asymmetric key is missing, it generates it. Should be a one-time thing.
  • The application reports its device ID (see Device IDs #444) and public key through some commissioning interface. As long as that's only needed in one direction, that can be stdout / defmt. The local key management (see feat(docs): Add section on CoAP #448) can then update itself from those. (In particular, it can record that that key is "one of ours", and revoke any older keys from the same device ID).
  • For any "certificates" (signed CWTs) based on the key we need a back-channel; that's probably also convenient for anything with symmetric keys (as blurting them out through stdout is probably not a great idea, having them overwritten from outside sounds safer). Depending on what they key is for precisely, that backchannel might only allow one write.

This does not need any support from the programmers in that all binaries we flash are safe to flash onto any device, and then the key-management part will "just" overwrite the keys we had for the device (OK, revoke, we don't delete data just so -- but in this case, we just accidentally erased the device, so it's OK that its keys are revoked too :-) ).

I think that that kind of provisioning should be run once at programming time. Next stage provisioning can then run from those keys unless it's the simplest case where only a public key is reported publicly.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant