UC20/UC22 full-disk-encryption hook interface

Ubuntu Core 20 and 22 (UC20/UC22) use full disk encryption (FDE) whenever the hardware allows, protecting both the confidentiality and integrity of a device’s data when there’s physical access to a device, or after a device has been lost or stolen.

Creating a verifiable boot process on a non-standard (non-UEFI+TPM platform) FDE platform, such as on a Raspberry Pi or other ARM devices, is board-specific and will typically involve creating custom gadget and kernel snaps. UC20/UC22, however, do provide a helper mechanism, via a hook interface, to ensure the integrity of any subsequently executed or accessed data… This process is outlined below.

Get in touch to discuss bespoke requirements for non-standard FDE platforms.

Using the hook

Snapd uses the hook mechanism in the kernel snap to run hook-specific executable binaries. The first retrieves the encryption key and unlocks the disk while the second is called on installation/re-installation to ensure the key is sealed.

  • The first binary is executed from the initrd and is located inside the initrd $PATH, e.g. usr/bin/fde-reveal-key.

    This is used by snap-bootstrap initramfs-mounts to retrieve the key and unlock the disk. It is simply confined with systemd-run to ensure the helper is misused to change the ubuntu-data partition in uncontrolled ways.

  • The second binary is executed from the install/reinstall process to ensure the key is sealed.

    It is run as a normal hook called meta/hooks/fde-setup with corresponding snapctl fde-setup-request and snapctl fde-setup-result interfaces that can be used from the hook.

If fde-reveal-key and meta/hooks/fde-setup exist, they’re used for all FDE operations instead of the built-in secboot code.

Data handling

The helper mechanism is stateless, with snapd providing whatever data is necessary.

When protecting a key, for instance, fde-setup returns a “handle” consisting of any valid JSON, such as a string with base64-encoded binary data, which could then be used as auxiliary data to help unseal/decrypt the protected key.

Apart from ensuring the data is valid JSON, or when the data is passed back to fde-reveal-key (see below), snapd stores and handles this data completely opaquely.

Data for fde-reveal-key

As mentioned earlier, the fde-reveal-key binary is run from initrd. It gets data via stdin and outputs the data to stdout.

op: reveal

In this operation the revealed key is written to stdout. The data passed via stdin is JSON:

{
    "op": "reveal",                 // more may be added in the future
    "sealed-key": "<base64-encoded-bytes>",
    "handle": <handle-valid-json>,  // opaque handle (can be empty) 
                                    // as returned previously by fde-setup
}

It returns: {"key": "base64 encoded key"} to allow additional future capabilities.

Any exit code other than 0 is expected to return an error string to stderr.

op: lock

When the hook is called in this way, access to the keys should be locked using a method appropriate for the underlying hardware.

Data for fde-setup

fde-setup is a normal snapd hook and uses snapctl to communicate with snapd.

The workflow for the hook is that it calls snapctl fde-setup-request and reads the JSON from stdout. The JSON will contain an op field that tells the hook what to do.

The result is operation dependent and should be set via stdin to snapctl fde-setup-result.

op: features

If the fde-setup hook is run with "op": "features" (see above), the hook should report, via JSON, a JSON-formatted array containing the features it supports. Initially this will just be an empty list: [].

For example

$ snapctl fde-setup-request
{"op":"features"}

$ echo '{"features": []}' | snapctl fde-setup-result

Currently, this is mostly to future proof hooks/snapd.

op: initial-setup

If the fde-setup hook is run with "op": "initial-setup", the hook should call snapctl fde-setup-request, passing the following JSON to stdout:

{
    "op": "initial-setup",      // valid: "initial-setup","update"
    "key": "base64 encoded payload that should be encrypted/sealed",
}

When the hook has completed the encryption and sealing, it will call:

$ echo '{"sealed-key": "<base64-sealed-or-encrypted-key>", "handle": <opaque-handle-valid-JSON> }' | snapctl fde-setup-result

Any exit code other than 0 is expected to return an error string to stderr.

Implementation examples

Future development

The current spec does not include boot chains or a method of tacking what specific boot assets are measured. Consequently, these hooks cannot currently be used for systems that require measurements.

1 Like