Ubuntu Core FDE

FDE workflow

During Ubuntu Core's install mode,
  • the gadget, kernel, core20 and snapd will be installed temporarily in the tmpfs
  • The enryption of writable partitions( ubuntu-save and ubuntu-data ) is reached by a fde-setup hook
  •  This hook is located at meta/hooks/fde-setup inside the kernel snap.
After install mode completes, UC20 boots into run mode with a chain of trust through secure boot:
  • ARM
    • ARM TrustZone with BL1-BL33 stages
    • the initramfs from kernel executes a binary called fde-reveal-key to retrieve the key and unlock the writable partitions( ubuntu-save and ubuntu-data ).
  • x86
    • UEFI BIOS with TPM and SecureBoot enabled

Measured boot

The Trusted Platform Module (TPM) is a tamper-proof, cryptographically secure auditing component with firmware supplied by a trusted third party.
The boot configuration log contains hash-chained measurements recorded in its Platform Configuration Registers (PCR) when the host last underwent the bootstrapping sequence. The following figure shows this recording process: Incrementally adding a previously hashed measurement to the next measurement’s hash and running the hashing algorithm on the union accomplishes hash-chaining.
Attestation is accomplished when a host provides proof of its configuration state using its boot configuration log (TCGLog). This log is used to determine whether boot components are trustworthy.
Forgery of a boot log is difficult because the TPM doesn't expose its PCR values other than the read and extend operations.
Furthermore, the credentials supplied by the Host Attestation Service are sealed to specific PCR values.

GRUB's Measuring boot components

Measured boot is currently only supported on EFI platforms.

If the tpm module is loaded and the platform has a Trusted Platform Module installed, GRUB will :

  • log each command executed and each file loaded into the TPM event log
  • extend the PCR values in the TPM correspondingly
All events will be logged into the PCR described below with a type of EV_IPL and an event description as described below.
Event typePCRDescription
Command8All executed commands (including those from configuration files) will be logged and measured as entered with a prefix of “grub_cmd: “
Kernel command line8Any command line passed to a kernel will be logged and measured as entered with a prefix of “kernel_cmdline: ”
Module command line8Any command line passed to a kernel module will be logged and measured as entered with a prefix of “module_cmdline: “
Files9Any file read by GRUB will be logged and measured with a descriptive text corresponding to the filename.
GRUB will not measure its own core.img - it is expected that firmware will carry this out.
GRUB will also not perform any measurements until the tpm module is loaded.
As such it is recommended that the tpm module be built into core.img in order to avoid a potential gap in measurement between core.img being loaded and the tpm module being loaded.

UC20 Full Disk Encryption (FDE)

Ubuntu Core 20 (UC20) uses full disk encryption (FDE) whenever the hardware allows.

Built-in FDE support requires both UEFI Secure Boot and TPM (Trusted Platform Module) support, but its implementation in Ubuntu Core is generic and widely compatible to help support a range of hardware.

The layout of the generated image used to install Ubuntu Core:

  • ubuntu-seed (role: system-seed; read-only, ext4 or typically vfat)
  • ubuntu-boot (role: system-boot; read-only, ext4 or vfat)
  • ubuntu-save (role: system-save; writable, ext4, encrypted)
  • ubuntu-data (role: system-data; writable, ext4, encrypted)
The initramfs bootstrapped from ubuntu-boot is responsible for decrypting both the ubuntu-save and ubuntu-data partitions.

The unsealing of the disk encryption key(s) is tied to the correct TPM boot measurements.

FDE can be disabled by the “storage-safety” setting in the model assertion used to build the installable image:

  • prefer-unencrypted
  • do not encrypt by default, even if the device supports encryption.
  • prefer-encrypted
  • do encrypt if the hardware supports it
  • encrypted
  • ensure encryption is used and fail if the device does not support it

Boot process

The system boot process:
  • Verify the bootloaders and kernel signatures
  • A chain of trust is enforced (SecureBoot) from a hardware root-of-trust to the software that recovers the keys from the TPM.
    • The platform hardware verifies the signature of the firmware’s initial boot block from Flash, using a public key burned into the chipset by the device manufacturer.
    • The firmware measures
      • the secure boot policy (including SecureBoot, PK, KEK, db and dbx variables) to the TPM
      • the CA certificates used to verify the drivers
      • the certificate used to verify shim
      • the hash of shim
    • Shim measures MOK policy and the MOK signature databases to the TPM.
  • Measures the kernel command line with the TPM
  • Platform characteristics and events are cryptographically recorded during the boot process to the TPM using one-way hash-extend operations.
    The properties that are measured are defined in the TCG PC Client Specific Platform Firmware Profile Specification: 2.3.4 - “Host Platform Roots of Trust Requirements - Integrity Collection and Reporting - PCR Usage”
  • the snapd initrd code measures the snap device model
  • snapd then separately verifies other snaps with their assertions as needed
snap-bootstrap is the main executable that is run during the early initramfs booting stage of UC20:
  1. Mounting selected partitions from the disk that UC20 is on. 
  2. snap-bootstrap may perform the necessary steps to unlock any encrypted partitions such as ubuntu-data and ubuntu-save.
  3. The keys used to unlock these partitions will be protected by a TPM2 device, which will permit recovery of the keys when the device boots software that is authorized to access the encrypted partitions.
    TPMs have the ability to seal arbitrary data inside a TPM object and they provide a command that can be used to unseal that data - TPM2_Unseal.
    Requesting the TPM to reveal the unlock key is only successful if the properties that were measured into the TPM during the boot process match those encoded in the authorization policy for the key.
  4. After unlocking and mounting all such partitions, snap-bootstrap mounts base snap files.
  5. snap-bootstrap then chooses which kernel snap is to be used to mount and find additional kernel modules that are not compiled into the kernel or shipped as modules inside the initramfs or otherwise loaded as DTBs, etc.
  6. snap-bootstrap will then mount the ubuntu-data partition such that either the writable components of the root filesystem come from this actual partition, or if the mode the system is booting into is an ephemeral system such as install or recover, will mount a temporary filesystem for this.
  7. snap-bootstrap on kernel and base snap upgrades will also handle updating bootloader environment variables to implement A/B or try-boot functionality.
  8. snap-bootstrap then finally may do some additional setup of the root filesystem such as copying some default files for ephemeral system modes such as recover.

PCR4 and PCR7

Measurements to the TPM by the firmware via the EFI_TCG2_PROTOCOL.HashLogExtendEvent API result in entries being appended to the TCG event log, unless the EFI_TCG2_EXTEND_ONLY flag is defined.

All measurements to PCR4 and PCR7 are logged, and this event log is provided by the firmware to the kernel which retains it for inspection by userspace.
All you need is a buffer holding data, the size of the buffer, and a data structure describing the extend event.
The event log structure is defined in the TCG EFI Protocol Specification: "5 Event Log Structure".

On a standard (UEFI+TPM platform) FDE platform

TPM-based FDE seals the FDE secret key to the full EFI state, including the kernel command line, which is subsequently unsealed by the initrd code in the secure-boot protected kernel.efi at boot time.

On a non-standard (non-UEFI+TPM platform) FDE platform

Creating a verifiable boot process on a non-standard FDE platform, such as ARM, is board-specific and will typically involve creating custom gadget and kernel snaps.
UC20 provides a helper mechanism, via a hook interface, to ensure the integrity of any subsequently executed or accessed data.

Supported snap hooks

A hook is an executable file that runs within a snap’s confined environment when a certain action occurs.
Examples requiring hooks:
  • notifying a snap that something has happened
  • If a snap has been upgraded, the snap may need to trigger a scripted migration process to port an old data format to the new one.
  • notifying a snap that a specific operation is in progress
  • A snap may need to know when a specific interface connects or disconnects.
A hook is defined as an executable within a snap’s meta/hooks/ directory, and consequently, also within snap/hooks/ when building with snapcraft.
snapd will execute the hook when required by that hook’s action.

There are 2 ways for Snapcraft to integrate hooks into a snap:

  • Using a project-wide snap/hooks directory
  • Hook executable can be placed in a snap/hooks directory and be automatically copied into the snap during Snapcraft’s prime step.
    Hooks copied from snap/hooks cannot access a part’s environment.
  • Defined in snapcraft.yaml
  • A part within snapcraft.yaml can generate or install hook executables into $SNAPCRAFT_PART_INSTALL/snap/hooks/.
    These are automatically copied into the snap during the prime step.
    
    configure-hook:
        plugin: dump
        source: configure-again/
        organize:
            configure: snap/hooks/configure
        
    Hooks that are generated from a part will include that part’s environment.
By default, hooks run with no plugs.
If a hook needs more privileges(access to system resources outside of a snap’s confined environment), it one can use the top-level hooks attribute in snapcraft.yaml to request plugs.
For example, the following excerpt registers an configure hook making use of a network plug:

hooks: # Top-level YAML attribute, parallel to `apps`
  configure: # Hook name, corresponds to executable name
    plugs: [network] # Or any other plugs required by this hook
Hooks are called with no parameters.
A hook needs to request or modify information via the snapctl tool.

The following hooks are defined(actions) and implemented:

  • The configure hook
  • The full-disk-encryption hook
  • 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.
    The current spec does not include boot chains or a method of tacking what specific boot assets are measured.
  • The gate-auto-refresh hook
  • The install hook
  • The install-device hook
  • The interface hooks
  • The pre-refresh hook
  • The post-refresh hook
  • The remove hook
  • The prepare-device hook
  • This hook is only supported in gadget snaps.

UC20 full-disk-encryption hook interface

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 does provide a helper mechanism, via a hook interface, to ensure the integrity.

Snapd uses the hook mechanism in the kernel snap to run 2 hook-specific executable binaries(fde-setup/fde-reveal-key).

  • The 1st binary is called on installation/re-installation of the kernel snap to ensure the key is sealed.
  • It is run as a normal hook called meta/hooks/fde-setup.
    In the install mode, this hook uses "snapctl fde-setup-request" and "snapctl fde-setup-result".
    The kernel snap used the key to encrypt the writable partitions and ensure that the key that can decrypt the partitions will be sealed safely.
  • The 2nd binary retrieves the encryption key and unlocks the disk
  • The 2nd binary is executed from the initramfs of the kernel snap in the run mode.
    It 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.
If fde-reveal-key and meta/hooks/fde-setup exist, they’re used for all FDE operations instead of the built-in secboot code.

The hook fde-setup returns a “handle” consisting of any valid JSON which could then be used as auxiliary data to help unseal/decrypt the protected key by fde-reveal-key.
snapd stores and handles this data completely opaquely.

  • Data flow for fde-setup
  • The fde-setup is a normal snapd hook which is located at meta/hooks/fde-setup inside the kernel snap.
    It uses snapctl to communicate with snapd.
    The workflow for this hook is :
    1. it calls "snapctl fde-setup-request" and reads the JSON returned from stdout.
    2. The JSON will contain an op field that tells the hook what JSON format is used.
      • op: features
      • 
        $ snapctl fde-setup-request
        {"op":"features"}
        
        $ echo '{"features": []}' | snapctl fde-setup-result
                    
        Initially this will just be an empty list returned: [].
      • op: initial-setup
      • 
        $ snapctl fde-setup-request
        {
            "op": "initial-setup",      // valid: "initial-setup","update"
            "key": "base64 encoded payload that should be encrypted/sealed",
        }
        			
        A base64 encoded key is delivered from snapd.
    3. Use CA to ask TA to encrypt the key
    4. Decode the base64 encoded key then passes the plaintext key to TA(fde_key_handler).
      TEE internal API (TEE_AEEncryptFinal) is called to encrypt the key.
      The encrypted key will be returned to CA.
    5. Provide the sealed key
    6. When the the hook has completed the encryption and sealing, it calls fde-setup-result with "sealed-key" and handle to snapd.
      
      $ echo '{"sealed-key": "<base64-sealed-or-encrypted-key>", "handle": <opaque-handle-valid-JSON> }' | snapctl fde-setup-result        
              
      The sealed keys for ubuntu-save and ubuntu-data can be found by the following paths:
      • /run/mnt/ubuntu-seed/device/fde/ubuntu-save.recovery.sealed-key
      • /run/mnt/ubuntu-seed/device/fde/ubuntu-data.recovery.sealed-key
  • Data flow for fde-reveal-key
  • The fde-reveal-key binary is executed from $initrd/usr/bin in the initramfs stage.
    It :
    • gets JSON data via stdin
    • 
      {
          "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
      }      
      		
    • Ask TA to decrypt the sealed key
    • The decrypted operation will be performed by TEE internal API (TEE_AEDecryptFinal) to retrieve the plaintext key.
      Then encode the plaintest key with base64 encoding.
    • Output the data to stdout
    • Output the following:
      
            	{"key": "base64 encoded key"} 
              
  • TA lock
  • When the hook is called with:
    
          	{"op": "lock"} 
      
    access to the keys should be locked using a method appropriate for the underlying hardware.
    TA has provided a mechanism to set a locked state, once lock is engaged, only encryption operations are available.

UC20 FDE with ARM TrustZone

The hook interface fde-setup and fde-reveal-key binaries will be CAs (Client Application).
They go through tee (Trusted Execution Environment) driver in the kernel space to communicate with TAs (Trusted Application) run in the secure world (OP-TEE OS core).

The popular solution is to use ARM TrustZone + OP-TEE as the backend of secure world for data protection.

TA which is responsible for encryption/decryption must be loaded prior to initramfs when fde-reveal-key is called.
Therefore, TA binary was embedded to OP-TEE core image when they’re built inside the gadget snap, and this approach is so-called Early TA.
Early TA are linked into a special data section in the TEE core blob.
Therefore, in the senario of FDE, there is no need to use tee-supplicant to load TA from user space into the secure memory managed by OP-TEE core.

TEE based Trusted Keys in Linux

Kernel Keys

Kernel's Key Retention Service(CONFIG_KEYS) allows cryptographic keys, authentication tokens, cross-domain user mappings, and similar to be cached in the kernel.
Keyrings are permitted; these are a special type of key that can hold links to other keys.

Keys are represented in the kernel by

  
  struct key
Each key has a number of attributes:
  • A serial number.
  • A type.
  • A description (for matching a key in a search).
  • Access control information.
  • An expiry time.
  • A payload.
  • State.
Userspace can manipulate keys directly through three new syscalls:
  • add_key
  • request_key
  • keyctl

Trusted and Encrypted Keys are two new key types added to the existing kernel key ring service.
All Trusted and Encrypted keys are created in the kernel, and user space sees, stores, and loads only encrypted blobs.

New keys are created from kernel generated random numbers, and are encrypted/decrypted using a specified ‘master’ key.
The ‘master’ key can either be a trusted-key or user-key type.

A trust source provides the source of security for Trusted Keys.

Trusted Key: TPM as a trust source

Computers that incorporate a TPM can create cryptographic keys and encrypt them so that they can only be decrypted by the TPM.
  • wrapping or binding a key
  • Each TPM has a master wrapping key, called the storage root key, which is stored within the TPM itself.
  • sealing the key to the TPM
  • A TPM can also create a key that is wrapped and tied to certain platform measurements(PCR).(sealing)
    The sealed key is returned to the user at the time of sealing.
    This sealed key can be unwrapped only when those platform measurements have the same values that they had when the key was created.(unsealing).
    1. the current PCR values will be compared to the PCR values recorded at the time of sealing
    2. if they match, the key will be decrypted and sent back.
    Nothing will happen to the sealed key as the sealed key is not stored in the TPM,

                         +----------------+
+------------+           |   TPM          |
| sealed key |------>    +                |
| PCR        |           |   PCR(current) |
+------------+           +----------------+
When you seal some data, the TPM returns the encrypted data to you to do as you please.
If you wanted to "override" it, you would simply delete that encrypted data and seal the original data again, with the new PCR values.
Management of the encrypted data is up to you and the TPM has nothing to do with it.

Trusted Keys use a TPM to generate and to seal the keys:

  • Keys are sealed under a 2048 bit RSA key in the TPM, and optionally sealed to specified PCR (integrity measurement) values
  • Keys are only unsealed by the TPM, if PCRs and blob integrity verifications match.

Trusted Key: TEE as a trust source

A Trusted Execution Environment based on ARM TrustZone provides hardware based isolation to perform trusted operations.
Especially OP-TEE which offers a standardized API to exploit hardware unique key (HUK).
Hardware Unique Key (HUK) which is generally burnt in on-chip fuses(OTP) and is accessible to TEE only.
HUK can be utilized to perform seal/unseal operations for Trusted keys.
Sealed Trusted key blobs can be exported to user-space.
Trusted key blobs can be unsealed on a particular hardware only.

Production grade OP-TEE implementation written in C

  • sealing key
    • Random IV (Initialization Vector) and nonce is generated by random data generation function (TEE_GenerateRandom) for each key to be sealed
    • TA unique key is derived from HUK (Hardware Unique Key) by the following inputs
      • TA UUID
      • Random nonce of handle
      TA unique key is used as input of AES-GCM for each key so it can not be recreated from another TA.
    The sealing operation (TEE_AEEncryptFinal) returns:
    • sealed key
    • handle
    • Handle contains IV, nonce and tag.
  • unsealing key
  • The unsealing operation (TEE_AEDecryptFinal) returns:
    • unsealed key
    • handle
    • Handle contains IV, nonce and tag.

host/fde_key_manager

fde_key_manager.c:

#define FDE_SETUP "fde-setup"
#define FDE_REVEAL_KEY "fde-reveal-key"
#define SNAPCTL "snapctl"


This is a wrapper for the following binary:
  • fde-setup
  • This is invoked as kernel snap's hook.
    Its input is fetched as json by snapctl `fde-setup-request`.
    • request_str = get_fde_setup_request()
    • Get the result of "snapctl fde-setup-request" from STDIN and returned it.
    • handle_fde_operation(request_str);
    • If the "op" in the request_str is "initial-setup", call handle_operation_setup(request_json):
      • Get the key
      • Encrypt the key
  • fde-reveal-key
  • This is invoked within initrd, its input is passed as json through stdin.
    Supported operations:
    • reveal
    • 
      - request: { "op": "reveal", "sealed-key": "base64-encoded-bytes",
                   "handle": "base64-encoded-bytes"}
      - result: {"key": "base64-encoded-bytes"}
      		
    • lock
    • 
      - request: { "op": "lock" }
      - result:
      		
    • request_str = get_reveal_key_request()
    • handle_fde_operation(request_str);

ta

References

留言

熱門文章