raspberrypi / rpi-sb-provisioner

A minimal-input automatic secure boot provisioning system for Raspberry Pi devices.
Apache License 2.0
18 stars 6 forks source link

Help me understand the device unique key? #82

Closed torntrousers closed 3 days ago

torntrousers commented 4 days ago

Following the PR comment - https://github.com/raspberrypi/rpi-sb-provisioner/pull/80#discussion_r1848576286 - The device unique key is indeed asymmetric - but only the private component is retained by the device. This component is used as the unlock passphrase for LUKS - but can also be used for other purposes by trusted software, where having the public key stored may allow you to authenticate a given device.

Can you explain in any more detail about the device unique key?

By "This component is used as the unlock passphrase for LUKS" how does that work - am i mistaken in thinking LUKS uses symmetric not asymmetric keys? Or is it that the private part of the device unique key is just used as a symmetric key for LUKS?

The bit about " the public key stored may allow you to authenticate a given device" sounds interesting can you say more about how that could work?

roliver-rpi commented 3 days ago

During the provisioning of each individual device, we generate a 32-byte ed25519 private key openssl genpkey -algorithm ed25519 and save it to OTP.

When this key is first-provisioned, it may retrieved using fastboot as described by @tdewey-rpi in https://github.com/raspberrypi/rpi-sb-provisioner/issues/79#issuecomment-2473157119

The corresponding public key is derived and can be retrieved using fastboot as follows:

fastboot getvar public-key

The presence of this key-pair allows for customer software running on-device to sign messages with the private key (retrieved from OTP) and later verify the authenticity of those signed messages on their internet infrastructure (using the per-device public key retrieved during provisioning). In short, using this mechanism allows customer server software to determine the provenance of messages received (i.e. they're from provisioned devices).

There is, however, an additional use of the 32-byte private-key stored in OTP; it is converted to a human-friendly form (the version you see from fastboot getvar private-key) and used as LUKS-passphrase for the encrypted root partition. As such, if the individual-device private key is saved during provisioning it can later be used to recover the plaintext contents of EMMC should OTP be unavailable for any reason (i.e. device failure / removal of EMMC from PCB).

In terms of how LUKS works, it makes use of a randomly generated master key to encrypt data (this is never made directly available to the user). Instead, the user is able to make use of multiple keyslots (allowing for multiple passphrases to be used) to gain access to the master key and thus decrypt the parition. LUKS derives the keys for these keyslots using a Password-Based Key Derivation Function (PBKDF). In our case, the passphrase/key derived from the OTP-private key (see previous paragraph) is used in the first keyslot. This allows for the encrypted root partition to be automatically unlocked at boot time without requiring user intervention.

torntrousers commented 3 days ago

The presence of this key-pair allows for customer software running on-device to sign messages with the private key (retrieved from OTP)

this is interesting to me, could you point to any doc on how to do that, or code or api examples?

tdewey-rpi commented 3 days ago

The presence of this key-pair allows for customer software running on-device to sign messages with the private key (retrieved from OTP)

this is interesting to me, could you point to any doc on how to do that, or code or api examples?

We're looking at expanding this area in terms of worked examples and enhanced software support.

With BCM2712 and BCM2711, the ARM-side code can request the key at any point. We have a wrapper for this, but I'd encourage anyone deploying this system to extremely tightly constrain any equivalent code.

Use the cryptroot OTP fetcher to get a BASE64-encoded private key https://github.com/raspberrypi/pi-gen-micro/blob/main/cryptroot/cryptkey-fetch

Apply the ed25519 header to create a DER key: cat <(echo -n MC4CAQAwBQYDK2VwBCIEIA== | base64 -d -) <(base64 -d /tmp/keyfile) > /tmp/key.der

Use standard openssl commands with this key:

openssl pkeyutl -sign -inform der -inkey /tmp/key.der -out signature.bin -rawin -in message.bin

# verify signature
openssl pkeyutl -verify -pubin -inkey public.pem -rawin -in message.bin -sigfile signature.bin

(from https://stackoverflow.com/questions/75925284/how-do-openssl-commands-sign-messages-using-ed25519).

Note that I wouldn't explicitly recommend driving this through shell - and we are considering implementing a service to allow connecting this key storage to OpenSSL and other cryptography libraries by exposing a PKCS11 interface.

torntrousers commented 3 days ago

Thanks, really interesting.

In the cryptkey-fetch script it mentions

"Raspberry Pi 5 and earlier revisions do not have a hardware secure key store. These OTP rows are visible to any user in the 'video' group via vcmailbox. Therefore this functionality is only suitable for key storage if the OS has already been restricted using the signed boot functionality.",

Is it that the user needing to be in the 'video' group the main thing that makes this more secure than just a plain text key stored in a file in the LUKS encrypted partition, or are there additional benefits that using OTP brings?

tdewey-rpi commented 3 days ago

Thanks, really interesting.

In the cryptkey-fetch script it mentions

"Raspberry Pi 5 and earlier revisions do not have a hardware secure key store. These OTP rows are visible to any user in the 'video' group via vcmailbox. Therefore this functionality is only suitable for key storage if the OS has already been restricted using the signed boot functionality.",

Is it that the user needing to be in the 'video' group the main thing that makes this more secure than just a plain text key stored in a file in the LUKS encrypted partition, or are there additional benefits that using OTP brings?

There are lifecycle and offline attack benefits: a) OTP is, by definition, one-time programmable. There is no ability to repurpose the key store, so your device cannot be trivially co-opted by another software payload b) OTP presents a higher bar to secret extraction - assuming you sufficiently secure your userspace using DAC & MAC, acquiring the key requires a (likely destructive) physical attack. Depending on your threat model, anomaly detection gains an opportunity to detect stale heartbeats from a device allowing you to flag the OTP key of the device as suspicious. c) The only other storage location available without additional hardware is the storage media itself - which would have to store the material outside the encrypted container. Storage media cloning, particularly for SD cards, is hard to detect, so any material stored outside of the encrypted container should be considered trivially discoverable by an actor with physical access. d) OTP, as it is outside of the storage media entirely, can present a key storage option regardless of downstream software's choices. It is invariant to your selection of kernel, userspace, filesystem, etc.

torntrousers commented 3 days ago

Thanks, great! One last question, with "c) ...which would have to store the material outside the encrypted container" is that the LUKSv2 container? why couldn't it be within that?

tdewey-rpi commented 3 days ago

So the thing to recall at this stage is that a device could have multiple unique secrets. In the case of the OTP-stored key, it must be stored outside of the LUKSv2 container as it is used to unlock the LUKSv2 container. You could, however, have additional secrets that you choose to keep inside the LUKSv2 container - indeed, this is how a system running Raspberry Pi Connect on a Secure Boot device would operate.

torntrousers commented 3 days ago

oh yes, is see. Thanks so much for all the detailed answers here @tdewey-rpi and @roliver-rpi