# Configure UEFI Secure Boot and disk encryption in Arm CCA Realms

## In this learning path

- [Introduction](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cca-bootsync/)
- [Understand Arm CCA BootSync and the Boot Injection protocol](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cca-bootsync/cca-bootsync/)
- [Configure UEFI Secure Boot and disk encryption in Arm CCA Realms](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cca-bootsync/flow/)
- [Next Steps](https://learn.arm.com/learning-paths/servers-and-cloud-computing/cca-bootsync/_next-steps/)

## What you will configure and validate
You’ll configure and validate both failure and success cases:
- First, you’ll launch a Realm without injecting any boot data to see that the firmware can run successful attestation and ask for BootSync data.
- Next, you’ll add the variable data file. BootSync completes, UEFI Secure Boot is enabled, and the unsigned kernel is rejected.
- Then, you’ll sign the Realm kernel and validate Secure Boot. The Realm boots with UEFI Secure Boot enabled, and the Secure Boot UEFI variable reports `1`.
- Finally, you’ll encrypt the Realm root file system and use BootSync secret data to provide UEFI variables and the unlock passphrase during early boot. You’ll unlock the encrypted file system.

You’ll run the User Context service in a Docker container and launch CCA Realms on an Arm Fixed Virtual Platform (FVP) in a separate Docker container.

You’ll use two terminals:
- In the User Context terminal, you’ll run the User Context service. This service represents the Realm initiator and decides whether to release BootSync data.
- In the FVP terminal, you’ll run the Arm CCA FVP and launch Realm virtual machines using `lkvm-bootsync`.

## Install dependencies
Set up Docker’s `apt` repository so you can pull and run the pre-built containers:
```
# Add Docker's official GPG key:
sudo apt-get update
sudo apt-get install -y ca-certificates curl
sudo install -m 0755 -d /etc/apt/keyrings
sudo curl -fsSL [https://download.docker.com/linux/ubuntu/gpg](https://download.docker.com/linux/ubuntu/gpg) -o /etc/apt/keyrings/docker.asc
sudo chmod a+r /etc/apt/keyrings/docker.asc

# Add the repository to APT sources:
echo \
  "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.asc] [https://download.docker.com/linux/ubuntu](https://download.docker.com/linux/ubuntu) \
  $(. /etc/os-release && echo "${UBUNTU_CODENAME:-$VERSION_CODENAME}") stable" | \
  sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
sudo apt-get update
```
Install Docker packages for the runtime used by both the User Context and FVP containers, and optionally install `git` for inspecting linked source files:
```
sudo apt-get install -y git docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
```
Add your user name to the Docker group so you can run Docker without `sudo`:
```
sudo usermod -aG docker $USER
newgrp docker
```
The group change applies to new shells; `newgrp docker` updates the current shell.

## Start the User Context service
In the User Context terminal, pull the Docker image with the pre-built User Context service. Then, run the container:
```
docker pull armlimited/cca-learning-path:cca-key-broker-v4
docker network create cca-trustee
docker run --rm -it --network cca-trustee --name user-context armlimited/cca-learning-path:cca-key-broker-v4
```
The `cca-trustee` Docker network is important. It lets the FVP container resolve the User Context service by container name, `user-context`, when `lkvm-bootsync` later uses `--service-ip user-context`.

In the User Context terminal, start the User Context service inside the running container using the `run-user-context-service.sh` script. The User Context service is part of the [EDK2](https://gitlab.arm.com/linux-arm/edk2-cca/-/tree/cca/4441_measured_boot_v1/ArmVirtPkg/ArmCcaBootSync/UserContext) project. Created specifically as a proof of concept, it’s intentionally small and not designed for production use.

When the script starts for the first time, it generates Secure Boot signing certificates and creates a Provisioning Data file. The Provisioning Data is a binary file generated by [GenPd.py](https://gitlab.arm.com/linux-arm/edk2-cca/-/blob/cca/4441_measured_boot_v1/ArmVirtPkg/ArmCcaBootSync/Scripts/GenPd.py) script. The file contains EFI variable definitions required for enabling UEFI Secure Boot.

You’ll be asked for a passphrase for the Secure Boot signing certificates. Remember it because you’ll use it again when signing the Realm kernel.
```
./run-user-context-service.sh
```

## Validate BootSync failure without boot data
With the User Context service running in the User Context terminal, open the FVP terminal.

Pull the Docker image with the pre-built FVP and CCA reference software stack. Then, run the container connected to the same Docker network:
```
docker pull armlimited/cca-learning-path:cca-simulation-v4
docker run --rm -it --network cca-trustee armlimited/cca-learning-path:cca-simulation-v4
```
Within your running container, launch the `run-cca-fvp.sh` script to run the Arm CCA pre-built binaries on the FVP:
```
./run-cca-fvp.sh
```
The `run-cca-fvp.sh` script uses the `screen` command to connect to the different UARTs in the FVP.

When the host Linux boots, log in:
Enter root as the username:
```
__output__
Welcome to the CCA host
__output__
host login: root
__output__
(host) #
```
Change directory to `/cca` and use `lkvm-bootsync` to launch a guest Linux in a Realm:
```
cd /cca
./lkvm-bootsync run --realm --disable-sve --irqchip=gicv3-its \
  --firmware KVMTOOL_EFI_SECUREBOOT.fd \
  -c 1 -m 512 --no-pvtime --pmu \
  --disk guest-disk.img --virtio-transport pci \
  --service-ip user-context \
  --service-port 1080 \
  --realm-pv ARMCCA01
```
The BootSync-specific parameters are:
- `--service-ip user-context` points the VMM to the User Context container on the Docker network.
- `--service-port 1080` uses the default User Context service port.
- `--realm-pv ARMCCA01` sets the Realm Personalization Value (RPV). The User Context uses this value as a file-name prefix when looking for BootSync data, such as `ARMCCA01_VAR.dat` and `ARMCCA01_SEC.dat`.

After running the command, you’ll see the Realm boot. The output is similar to:
```
__output__
Shell> bootaa64.efi root=/dev/vda2 acpi=force ip=on
__output__
EFI stub: Booting Linux Kernel...
__output__
EFI stub: Generating empty DTB
__output__
EFI stub: Exiting boot services...
```
UEFI Secure Boot isn’t mentioned in the EFI messages. This is expected because you’ve not yet provided the UEFI variable data that enables Secure Boot.

After the Realm boots, log in using the root again as the username:
```
__output__
Welcome to the CCA realm
__output__
realm login: root
__output__
(realm) #
```
Use the `efivar` utility to check the Secure Boot EFI variable. A value of `0` confirms that Secure Boot is not enabled:
```
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
efivar -p -d -n {global}-SecureBoot
__output__
0
```
Stop the Realm:
```
poweroff
```
In the User Context terminal, you can see that the Realm firmware requested a Variable Data file, but the file is missing:
```
__output__
INFO: BIB Variable Data Requested
__output__
FileName = ARMCCA01_VAR.dat
__output__
Error: ARMCCA01_VAR.dat Not found!
__output__
Error: Boot Sync failed.
__output__
Info: Session State: ConnectionEstablished
__output__
Info: Attestation State: AttSuccess
__output__
Info: BootSync State: BootSyncNotDone
```
This is an intentional failure: BootSync completed enough of the protocol to request boot information, but the User Context had no `ARMCCA01_VAR.dat` file to release.

Next, you’ll add the missing Boot Information Block data and repeat the launch.

## Pass UEFI variable data and validate unsigned kernel rejection
In the User Context terminal, stop the service by pressing `Ctrl-C`.

The RPV and file names used here are details of this Learning Path’s reference implementation. One User Context service can support multiple Realms by using the RPV as a file-name prefix. A Realm launched with `--realm-pv ARMCCA01` requests files that start with `ARMCCA01`. If the matching files are missing, BootSync can establish a session and complete attestation, but the User Context can’t provide the requested boot information.

Copy the generated Provisioning Data file to a Realm variable data file with the name `<RPV>_VAR.dat`:
```
cp SecureBoot/SecBootCert/pd.bin SecureBoot/ARMCCA01_VAR.dat
```
Because you launched the Realm with `--realm-pv ARMCCA01`, the User Context service looks for `ARMCCA01_VAR.dat`.

Create a Secret Data file with a test string that’ll be shared with a Realm using the same RPV:
```
echo "My Realm secret data" > SecureBoot/ARMCCA01_SEC.dat
```
In the User Context terminal, relaunch the User Context service:
```
./run-user-context-service.sh
```
In the FVP terminal, relaunch the Realm with the same RPV:
```
cd /cca
./lkvm-bootsync run --realm --disable-sve --irqchip=gicv3-its \
  --firmware KVMTOOL_EFI_SECUREBOOT.fd \
  -c 1 -m 512 --no-pvtime --pmu \
  --disk guest-disk.img --virtio-transport pci \
  --service-ip user-context \
  --service-port 1080 \
  --realm-pv ARMCCA01
```
Keeping the same `--realm-pv ARMCCA01` is what links the Realm request to the files you created.

In the User Context terminal, you can see that attestation succeeded and BootSync completed:
```
__output__
Info: Received FIN. Disconnecting.
__output__
Info: Session State: UnConnected
__output__
Info: Attestation State: AttSuccess
__output__
Info: BootSync State: BootSyncCompete
```
This means the User Context released the Boot Information Blocks to the Realm firmware.

The Realm now fails to boot the Linux kernel:
```
__output__
Shell> bootaa64.efi root=/dev/vda2 acpi=force ip=on
__output__
Script Error Status: Access Denied (line number 1)
```
This failure is expected. The same kernel booted before Secure Boot was enabled, but now the firmware enforces the Secure Boot variables injected by BootSync. The kernel image isn’t signed yet, so firmware rejects it.

Stop the realm by running `reset` in the EFI shell prompt:
```
reset
```
Next, you’ll launch a Realm with a signed kernel image.

## Sign the kernel and verify Secure Boot
In the FVP terminal, use the `sign_guest_kernel.sh` script to sign the Realm Linux kernel with the Secure Boot certificate generated earlier by the User Context setup script. When prompted, enter the passphrase you used for the Secure Boot signing certificates:
```
sign_guest_kernel.sh
__output__
Signing kernel image:
__output__
Enter PEM pass phrase:
Signing Unsigned original image
```
In the FVP terminal, relaunch the Realm. Use the same User Context service, service port, and RPV so the Realm receives the same Secure Boot variable data:
```
cd /cca
./lkvm-bootsync run --realm --disable-sve --irqchip=gicv3-its \
  --firmware KVMTOOL_EFI_SECUREBOOT.fd \
  -c 1 -m 512 --no-pvtime --pmu \
  --disk guest-disk.img --virtio-transport pci \
  --service-ip user-context \
  --service-port 1080 \
  --realm-pv ARMCCA01
```
The output is similar to:
```
__output__
Shell> bootaa64.efi root=/dev/vda2 acpi=force ip=on
__output__
EFI stub: Booting Linux Kernel...
__output__
EFI stub: UEFI Secure Boot is enabled.
__output__
EFI stub: Generating empty DTB
__output__
EFI stub: Exiting boot services...
```
The EFI messages show that UEFI Secure Boot is enabled.

After the Realm boots, log in using `root` as the username:
```
__output__
Welcome to the CCA realm
__output__
realm login: root
__output__
(realm) #
```
Use `efivar` to check the Secure Boot EFI variable. A value of `1` confirms that Secure Boot is enabled:
```
mount -t efivarfs efivarfs /sys/firmware/efi/efivars
efivar -p -d -n {global}-SecureBoot
__output__
1
```
You can also see that EFI SetupMode is disabled:
```
efivar -d -p -n {global}-SetupMode
__output__
0
```
Using [securityfs](https://www.kernel.org/doc/Documentation/ABI/testing/securityfs-secrets-coco), you can access the secret data shared with the Realm from under the `GRUB_EFI_DISKPASSWD_GUID` entry:
```
mount -t securityfs none /sys/kernel/security
cat /sys/kernel/security/secrets/coco/736869e5-84f0-4973-92ec-06879ce3da0b
__output__
My Realm secret data
```
Stop the Realm:
```
poweroff
```
You’ve successfully started a Realm with UEFI Secure Boot configured and enabled using Arm CCA BootSync.

## Encrypt the root file system and update secret data for disk unlock
You can use the secret data shared with a Realm using Arm CCA BootSync to provide access to encrypted file systems. You’ll now encrypt the root file system, observe a failed unlock when the wrong secret is available, and update the BootSync secret so the Realm can unlock the disk during boot.

### Encrypt the root partition
> Note: There are different ways to provide a Realm with access to encrypted partitions using Arm CCA BootSync. For example:
> - Use an updated version of [Grub](https://gitlab.arm.com/linux-arm/grub-cca/-/tree/cca/4441_measured_boot_v1) as a boot loader. This version of Grub uses the secret data to open an encrypted partition and load kernel and `initrd` images from it. The following is an example Grub config file:
```
__output__
echo 'Mounting encrypted disk...'
__output__
cryptomount -s efisecret (hd0,gpt2)
__output__

echo 'Loading Kernel...'
__output__
linux (crypto0)/bootaa64.efi
__output__

echo 'Loading InitRD...'
__output__
initrd (crypto0)/initrd.gz
__output__

echo 'Booting Linux...'
__output__
boot
```
> With the current Grub implementation of Arm CCA BootSync support, the booted operating system has access to the `initramfs` only.
> - Use an init script in an `initrd` image to unlock and mount the encrypted partition. You’ll use this approach in the next step.

In the FVP terminal, use the `encrypt_rootfs.sh` script to encrypt the root partition in the Realm disk image and add an `initrd` image to the kernel parameters.

You’ll be asked for a passphrase to encrypt the root partition. Use a memorable test passphrase and keep it available, because you’ll later write the same value into `ARMCCA01_SEC.dat` for BootSync to release.

You can use a maximum of 512 characters of uppercase or lowercase letters, numbers, and symbols. Ignore the warning message because you’re using small partitions.
```
encrypt_rootfs.sh
__output__
Resizing root file system to accommodate minimal LUKS2 header
__output__
resize2fs 1.47.3 (8-Jul-2025)
__output__
Resizing the filesystem on /dev/loop0p2 to 260096 (1k) blocks.
__output__
The filesystem on /dev/loop0p2 is now 260096 (1k) blocks long.
__output__

tune2fs 1.47.3 (8-Jul-2025)
__output__
Encrypting root partition
__output__
Enter passphrase for LUKS2-temp-4684d923-d8b7-4371-8bb2-f8667bfb0498.new:
__output__
WARNING: keyslots area (1015808 bytes) is very small, available LUKS2 keyslot count is very limited.
__output__
Enable Initrd image
```

### Validate failed disk unlock
In the FVP terminal, relaunch the Realm. At this point, `ARMCCA01_SEC.dat` still contains the test string from the previous Secure Boot step, not the disk encryption passphrase:
```
cd /cca
./lkvm-bootsync run --realm --disable-sve --irqchip=gicv3-its \
  --firmware KVMTOOL_EFI_SECUREBOOT.fd \
  -c 1 -m 512 --no-pvtime --pmu \
  --disk guest-disk.img --virtio-transport pci \
  --service-ip user-context \
  --service-port 1080 \
  --realm-pv ARMCCA01
```
In the Realm boot log, you can see that the init script failed to unlock the encrypted partition:
```
__output__
[   32.761651] Run /init as init process
__output__
Detecting LUKS containers in vda1-2
__output__
/dev/vda1 is not LUKS
__output__
/dev/vda2 is LUKS, attempting unlock.
__output__
No key available with this passphrase.
__output__
Failed to unlock LUKS partition. Has BootSync been completed?
__output__
FAIL finding root on LUKS, loading BusyBox...
__output__
can't run '/etc/init.d/rcS': No such file or directory
__output__

Please press Enter to activate this console.
```
Press `Enter` to get into the Realm shell. 

In the shell, check that only `initramfs` is mounted and that you can’t mount `/dev/vda2` directly:
```
df -h
__output__
Filesystem                Size      Used Available Use% Mounted on
devtmpfs                174.4M         0    174.4M   0% /dev
```
```
mount -t auto /dev/vda2 /cryptroot
__output__
mount: mounting /dev/vda2 on /cryptroot failed: Invalid argument
```
Stop the Realm:
```
poweroff
```
The init script failed to unlock the encrypted partition because the Secret Data file doesn’t contain the encryption passphrase yet.

### Update the BootSync secret and validate successful disk unlock
Now, update the BootSync secret.

In the User Context terminal, stop the service by pressing `Ctrl-C`.

Save the encryption passphrase into the Secret Data file. Use `echo -n` so the file contains only the passphrase and no trailing newline character:
```
ROOTFS_PASSPHRASE='replace-with-the-passphrase-you-used-for-encrypt_rootfs'
echo -n "$ROOTFS_PASSPHRASE" > SecureBoot/ARMCCA01_SEC.dat
```
In the User Context terminal, relaunch the User Context service:
```
./run-user-context-service.sh
```
In the FVP terminal, relaunch the Realm with the same RPV:
```
cd /cca
./lkvm-bootsync run --realm --disable-sve --irqchip=gicv3-its \
  --firmware KVMTOOL_EFI_SECUREBOOT.fd \
  -c 1 -m 512 --no-pvtime --pmu \
  --disk guest-disk.img --virtio-transport pci \
  --service-ip user-context \
  --service-port 1080 \
  --realm-pv ARMCCA01
```
In the Realm boot log, you can see that the init script successfully unlocked and mounted the encrypted partition:
```
__output__
[  233.501627] Run /init as init process
__output__
Detecting LUKS containers in vda1-2
__output__
/dev/vda1 is not LUKS
__output__
/dev/vda2 is LUKS, attempting unlock.
__output__
*** LUKS partition unlocked, switching root ***
```
After the Realm boots, log in using `root` as the username:
```
__output__
Welcome to the CCA realm
__output__
realm login: root
__output__
(realm) #
```
Check that the unlocked encrypted partition `/dev/mapper/cryptroot` is mounted as root:
```
df -h
__output__
Filesystem                Size      Used Available Use% Mounted on
devtmpfs                174.4M         0    174.4M   0% /dev
/dev/mapper/cryptroot
                        227.9M     93.0M    118.2M  44% /
tmpfs                   194.8M         0    194.8M   0% /dev/shm
tmpfs                   194.8M     40.0K    194.8M   0% /tmp
tmpfs                   194.8M     32.0K    194.8M   0% /run
cgroup                  194.8M         0    194.8M   0% /sys/fs/cgroup
```
Stop the Realm:
```
poweroff
```
Stop the FVP:
```
poweroff
```

## What you’ve accomplished
You’ve now used Arm CCA BootSync to enable UEFI Secure Boot and unlock an encrypted root file system for a Realm.

You can extend the workflows covered in this Learning Path to provide trusted early-boot data for your own Realm workloads.
