Files
u-boot/doc/usage/tkey-fde.rst
Simon Glass f716e618b8 doc: Add TKey full disk encryption documentation
Add comprehensive documentation for TKey-based full disk encryption:

- doc/usage/tkey-fde.rst: Main documentation covering the TKey FDE
  workflow, key derivation process, U-Boot integration, the
  tkey-fde-key.py script usage, creating test images, troubleshooting,
  and security considerations

- Add cross-references from related documentation pages including
  blkmap, bootflow, luks, and tkey command references

- Update luks.rst with a section on hardware-backed key derivation

Series-to: concept
Cover-letter:
luks: Complete TKey implementation
This series finishes off the TKey implementation, including an expo
menu (bootctl), a Python script to automate common operations and some
documentation to pull it all together.
END

Co-developed-by: Claude <noreply@anthropic.com>
Signed-off-by: Simon Glass <simon.glass@canonical.com>
2025-12-08 05:22:18 -07:00

586 lines
17 KiB
ReStructuredText

.. SPDX-License-Identifier: GPL-2.0+
TKey Full Disk Encryption
==========================
Overview
--------
U-Boot supports using `Tillitis TKey <https://tillitis.se/>`_ hardware-security
tokens to unlock LUKS-encrypted partitions. This provides hardware-backed
full-disk encryption (FDE) where the encryption key is derived from two pieces
of information:
* A user password/passphrase (USS - User Supplied Secret)
* The TKey's internal Unique Device Identifier (UDI)
The same password on the same TKey always produces the same encryption key,
making it suitable for unlocking encrypted root filesystems at boot time.
Note: Despite its name, FDE generally refers to the encryption of a single
partition on a disk, rather than an entire disk.
**Key Features:**
* Hardware-backed key derivation using TKey security token
* Compatible with standard LUKS1 and LUKS2 encrypted partitions
* Automatic unlock during boot flow detection
* Test infrastructure for creating encrypted disk images
* Python tools for key generation and disk encryption
How It Works
------------
TKey Key Derivation Process
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The TKey derives encryption keys through this process:
1. **Load Signer App**: The TKey firmware loads the signer application with
your password (USS)
2. **Generate Key Pair**: The signer app combines the USS with the device's UDI
to generate an Ed25519 key pair
3. **Derive Disk Key**: The public key is hashed with SHA-256 to produce a
32-byte encryption key
4. **Decrypt Partition**: The key is used to unlock the LUKS-encrypted
partition
This means:
* The same password always produces the same key (deterministic)
* Different passwords produce completely different keys
* The physical TKey device is required (UDI is device-specific)
* No key material is stored on disk - only derived when needed
U-Boot Integration
~~~~~~~~~~~~~~~~~~
When U-Boot detects a LUKS-encrypted partition during bootflow booting:
1. Prompts the user for their password
2. Detects if a TKey device is present
3. Loads the TKey signer app with the password
4. Derives the encryption key from the TKey
5. Attempts to unlock the LUKS partition
6. Creates a blkmap device for accessing decrypted data
7. Continues with normal boot process
Tools and Workflow
------------------
The ``scripts/tkey_fde_key.py`` Script
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
This Python script handles TKey key derivation and disk encryption operations.
**Basic Usage - Generate a key:**
::
# Generate key interactively (prompts for password)
$ ./scripts/tkey_fde_key.py
# Generate key from password file
$ echo "mypassword" > passfile
$ ./scripts/tkey_fde_key.py -p passfile -o diskkey.bin --binary
# Generate key from stdin
$ echo "mypassword" | ./scripts/tkey_fde_key.py -p - --binary
**Encrypting a Disk Image:**
::
# Create a disk image
$ dd if=/dev/zero of=rootfs.img bs=1M count=1000
# Encrypt the entire disk with LUKS
$ ./scripts/tkey_fde_key.py -e rootfs.img -p passfile
# Encrypt a specific partition
$ ./scripts/tkey_fde_key.py -e disk.img -P 2 -p passfile
**Opening an Encrypted Disk:**
::
# Open encrypted disk (creates /dev/mapper/tkey-disk)
$ ./scripts/tkey_fde_key.py -O rootfs.img -p passfile
# Mount the decrypted filesystem
$ sudo mount /dev/mapper/tkey-disk /mnt
# When done, unmount and close
$ sudo umount /mnt
$ sudo cryptsetup close tkey-disk
**Advanced Options:**
::
# Save both encrypted disk and backup key file
$ ./scripts/tkey_fde_key.py -e disk.img -p passfile -o backup.key
# Use verbose output to see what's happening
$ ./scripts/tkey_fde_key.py -e disk.img -p passfile --verbose
# Use debug mode for troubleshooting
$ ./scripts/tkey_fde_key.py --debug -e disk.img -p passfile
Creating Test Images
--------------------
Test Disk Images
~~~~~~~~~~~~~~~~
The U-Boot test infrastructure creates several LUKS-encrypted test images:
* ``mmc11.img`` - LUKS1 encrypted Ubuntu image
* ``mmc12.img`` - LUKS2 encrypted Ubuntu image with Argon2id KDF
* ``mmc13.img`` - LUKS2 encrypted Ubuntu image for TKey testing
* ``mmc14.img`` - LUKS2 encrypted image with pre-derived master key
By default, ``mmc13.img`` is encrypted with a key derived from the TKey
emulator's deterministic public key. This allows testing without physical
hardware.
Using override.bin for Physical TKey Testing
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To test with a physical TKey device instead of the emulator, create an
``override.bin`` file containing the TKey-derived disk key:
::
# Generate override.bin from your physical TKey with password "test"
$ echo "test" | ./scripts/tkey_fde_key.py -p - -o override.bin --binary
# Regenerate test images with your TKey's key
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
# Test unlocking mmc13 with your physical TKey
$ /tmp/b/sandbox/u-boot -T -c \
"tkey connect sandbox_tkey; sb devon mmc13; luks unlock -t mmc d:2 test"
When ``override.bin`` exists in the source directory, the test infrastructure
uses it instead of the emulator's key to encrypt ``mmc13.img``. This allows
you to test the full TKey unlock flow with real hardware.
To switch back to emulator testing, simply remove the override file:
::
$ rm override.bin
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
Using the Python Test Infrastructure
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
The test infrastructure in ``test/py/tests/test_ut.py`` handles TKey key
generation automatically:
::
def test_ut_dm_init_bootstd(u_boot_config, u_boot_log):
"""Initialize data for bootflow tests with TKey encryption"""
# Check for override key file (for physical TKey testing)
override_keyfile = os.path.join(u_boot_config.source_dir, 'override.bin')
if os.path.exists(override_keyfile):
keyfile = override_keyfile
u_boot_log.action(f'Using override TKey key: {keyfile}')
else:
# Generate key matching TKey emulator's deterministic pubkey
pubkey = bytes([0x50 + (i & 0xf) for i in range(32)])
disk_key = hashlib.sha256(pubkey.hex().encode()).digest()
keyfile = os.path.join(u_boot_config.persistent_data_dir,
'tkey_emul.key')
with open(keyfile, 'wb') as f:
f.write(disk_key)
# Create LUKS2 encrypted image for TKey testing
setup_ubuntu_image(u_boot_config, u_boot_log, 13, 'mmc',
use_fde=2, luks_kdf='argon2id',
encrypt_keyfile=keyfile)
**Helper Class Usage:**
See ``test/py/tests/fs_helper.py`` for the ``FsHelper`` class:
::
from fs_helper import FsHelper, DiskHelper
# Create LUKS2 encrypted filesystem with TKey key file
with FsHelper(config, 'ext4', 30, 'test',
part_mb=60,
encrypt_keyfile='/path/to/tkey-derived-key.bin') as fsh:
fsh.setup()
# Add files to fsh.srcdir
with open(os.path.join(fsh.srcdir, 'hello.txt'), 'w') as f:
f.write('Hello from TKey FDE!\n')
# Create encrypted filesystem
fsh.mk_fs()
Step-by-Step Workflow
----------------------
Complete Example: Testing TKey FDE with mmc13
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
**1. Create Test Disk Images**
Run the test infrastructure to create encrypted images:
::
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
This creates ``mmc13.img`` (LUKS2 with Argon2id) encrypted with the TKey
emulator's key.
**2. Test Unlocking with TKey Emulator**
Run U-Boot sandbox and test the unlock process with the emulator:
::
$ /tmp/b/sandbox/u-boot -T -c \
"tkey connect tkey-emul; sb devon mmc13; luks unlock -t mmc d:2 test"
**Expected Output:**
::
Connected to TKey device
Device 'mmc13' enabled
Unlocking LUKS2 partition...
Using TKey for disk encryption key
Loading TKey signer app (6d78 bytes) with USS...
TKey public key: 50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
50 51 52 53 54 55 56 57 58 59 5a 5b 5c 5d 5e 5f
TKey disk key derived successfully
TKey derived disk key: e9 b0 59 92 68 ff 8b 08 3e f8 0d bd 04 be 20 7c
e9 a1 9a 60 a8 88 cc b3 fe 93 71 0a 0a 70 a3 4e
Unlocked LUKS partition as blkmap device 'luks-mmc-d:2'
**3. Test with Physical TKey**
To test with a real TKey device:
::
# Generate override.bin from your physical TKey with password "test"
$ echo "test" | ./scripts/tkey_fde_key.py -p - -o override.bin --binary -f -v
Reading password from stdin...
Password length: 4, repr: 'test'
TKey detected via USB enumeration
...
Ed25519 public key (hex): df4faa680d9fd79079cc572c1f84fb3fa59ab904dad652e90a22e5b672a67eb1
Derived disk key (hex): 1546bdaf99e9ed9867d83ae69062c9da3202a617584a35ee4ae38672ec775a7f
Key saved to override.bin
# Regenerate mmc13.img with your TKey's key
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
# Test unlocking with physical TKey (ensure TKey is plugged in)
$ /tmp/b/sandbox/u-boot -T -c "sb devon mmc13; luks unlock -t mmc d:2 test" -P
**Expected Output with Physical TKey:**
::
Device 'mmc13' enabled
Unlocking LUKS2 partition...
Using TKey for disk encryption key
Loading TKey signer app (6d78 bytes) with USS...
TKey public key: df 4f aa 68 0d 9f d7 90 79 cc 57 2c 1f 84 fb 3f
a5 9a b9 04 da d6 52 e9 0a 22 e5 b6 72 a6 7e b1
TKey disk key derived successfully
TKey derived disk key: 15 46 bd af 99 e9 ed 98 67 d8 3a e6 90 62 c9 da
32 02 a6 17 58 4a 35 ee 4a e3 86 72 ec 77 5a 7f
Unlocked LUKS partition as blkmap device 'luks-mmc-d:2'
**4. Verify Encryption**
You can verify the disk is encrypted by checking with cryptsetup:
::
# Extract partition 2 from the disk image
$ dd if=mmc13.img bs=512 skip=38912 count=122880 of=mmc13_part2.img
# Check LUKS header
$ cryptsetup luksDump mmc13_part2.img
LUKS header information
Version: 2
...
# Test unlock with the TKey emulator key (when no override.bin exists)
# First generate the emulator's key
$ python3 -c "
import hashlib
pubkey = bytes([0x50 + (i & 0xf) for i in range(32)])
key = hashlib.sha256(pubkey.hex().encode()).digest()
open('emul.key', 'wb').write(key)
"
$ sudo cryptsetup open mmc13_part2.img test-luks --key-file=emul.key
$ ls /dev/mapper/test-luks
/dev/mapper/test-luks
$ sudo cryptsetup close test-luks
Troubleshooting
---------------
Key Mismatch Errors
~~~~~~~~~~~~~~~~~~~
**Problem:** U-Boot shows "Failed to unlock LUKS partition"
::
LUKS1: Keyslot 0 failed with error -13
Failed to unlock LUKS1 with binary passphrase (err=-2)
Failed to unlock LUKS partition (err=-13: Permission denied)
**Cause:** The disk was encrypted with a different key than U-Boot is deriving.
**Solutions:**
1. **Verify you're using the correct password:** Make sure you enter the same
password in U-Boot that was used to generate the key file.
2. **Regenerate the key and verify it matches:**
::
# Generate key again with same password
$ ./scripts/tkey_fde_key.py -p mykey.txt -o test-key.bin --binary
# Compare with original
$ diff mykey test-key.bin
# If different, TKey may have been in different state
3. **Recreate disk images with the correct key:** If U-Boot consistently
derives a different key than what was used for encryption, capture the key
U-Boot derives and use that to encrypt the disk:
::
# Run U-Boot and note the "Binary pass" hex values from debug output
# Then recreate the key file with those values
$ python3 << 'EOF'
key_hex = "10f1132e6c27e6e8...d29b6b8a" # from U-Boot output
with open('mykey', 'wb') as f:
f.write(bytes.fromhex(key_hex))
EOF
# Recreate disk images with this key
$ rm mmc11.img mmc12.img
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
-k test_ut_dm_init_bootstd
TKey Not Detected
~~~~~~~~~~~~~~~~~
**Problem:** U-Boot doesn't detect the TKey device
**Solutions:**
* Ensure TKey is plugged in before starting U-Boot
* Check USB device is accessible: ``ls /dev/ttyACM*``
* Try replugging the TKey device
* For sandbox, ensure ``sandbox,device-path`` is set correctly in device tree
TKey App Already Loaded
~~~~~~~~~~~~~~~~~~~~~~~~
**Problem:** TKey is in app mode instead of firmware mode
**Solution:** Remove and reinsert the TKey. The device must be in firmware mode
to load the signer app.
Configuration
-------------
Kconfig Options
~~~~~~~~~~~~~~~
To enable TKey FDE support in U-Boot:
::
CONFIG_CMD_TKEY=y # TKey command
CONFIG_TKEY_DRIVER=y # TKey device driver
CONFIG_CMD_LUKS=y # LUKS command
CONFIG_BLK_LUKS=y # LUKS block device support
CONFIG_BLKMAP=y # Block device mapping
CONFIG_BOOTCTL=y # Boot control with unlock support
CONFIG_BOOTCTL_LOGIC=y # Boot control unlock logic
CONFIG_ARGON2=y # For LUKS2 Argon2id support (optional)
Device Tree Configuration
~~~~~~~~~~~~~~~~~~~~~~~~~
For sandbox testing, the TKey is configured in ``arch/sandbox/dts/test.dts``:
::
tkey {
compatible = "sandbox,tkey";
sandbox,device-path = "/dev/ttyACM0";
};
For real hardware, configure the USB serial device path appropriately.
Security Considerations
-----------------------
Key Storage
~~~~~~~~~~~
* The TKey-derived key should **never be stored permanently** on disk
* Only temporary key files (like ``mykey``) used during testing should exist
* In production, keys should be derived fresh each boot from TKey + password
* The ``mykey`` file is for **testing only** and should be kept secure
Password Security
~~~~~~~~~~~~~~~~~
* Use a strong password (at least 16 characters recommended)
* Different passwords produce completely different encryption keys
* The TKey's UDI adds additional entropy to the key derivation
* Consider using a hardware security token for additional protection
Hardware Security
~~~~~~~~~~~~~~~~~
* Physical access to the TKey is required to derive keys
* The TKey's UDI is unique per device - keys cannot be derived without it
* If the TKey is lost, encrypted data cannot be recovered
* Consider keeping a backup TKey or traditional key recovery mechanism
Memory Security
~~~~~~~~~~~~~~~
* Keys are held in memory while the device is unlocked
* Memory is not securely erased on warm reboot
* This is acceptable for boot-time use but not for long-term key storage
Comparison with Traditional LUKS
---------------------------------
**Traditional LUKS (Password Only):**
* Encryption key derived only from password
* Vulnerable to offline password cracking attacks
* No hardware requirement - same password works anywhere
**TKey-Enhanced LUKS:**
* Encryption key derived from password + TKey UDI
* Requires physical TKey device to derive key
* Resistant to offline password cracking (attacker needs both password and
TKey)
* Same password + same TKey always produces same key
* Different TKey devices produce different keys even with same password
Example Use Case: Secure Boot
------------------------------
A typical secure boot workflow with TKey FDE:
1. **System Powers On**
* U-Boot starts and scans for boot devices
* Finds LUKS-encrypted root partition
2. **User Authentication**
* U-Boot prompts user for password
* User inserts TKey device
* User enters password
3. **Key Derivation**
* U-Boot loads TKey signer app with password
* TKey derives encryption key from password + UDI
* Returns 32-byte encryption key to U-Boot
4. **Partition Unlock**
* U-Boot attempts to unlock LUKS partition
* If successful, creates blkmap device
* Encrypted data is accessible as standard block device
5. **Boot Continues**
* U-Boot loads kernel from unlocked partition
* System boots into encrypted root filesystem
Testing
-------
Unit Tests
~~~~~~~~~~
Run the bootctl TKey unlock tests:
::
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
-k bootctl_logic_tkey
Manual Testing
~~~~~~~~~~~~~~
Test mmc13 with the TKey emulator:
::
# 1. Build sandbox and create test images
$ crosfw sandbox -L
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
# 2. Test unlocking mmc13 with TKey emulator
$ /tmp/b/sandbox/u-boot -T -c \
"tkey connect tkey-emul; sb devon mmc13; luks unlock -t mmc d:2 test"
Test mmc13 with a physical TKey:
::
# 1. Generate override.bin with your TKey and password "test"
$ echo "test" | ./scripts/tkey_fde_key.py -p - -o override.bin --binary
# 2. Regenerate test images
$ ./test/py/test.py -B sandbox --build-dir /tmp/b/sandbox \
--build -k test_ut_dm_init_bootstd
# 3. Test unlocking with physical TKey
$ /tmp/b/sandbox/u-boot -T -c \
"tkey connect sandbox_tkey; sb devon mmc13; luks unlock -t mmc d:2 test"
See Also
--------
* :doc:`luks` - LUKS encryption documentation
* :doc:`cmd/luks` - LUKS command reference
* :doc:`cmd/tkey` - TKey command reference
* :doc:`cmd/blkmap` - Blkmap device mapping
* ``scripts/tkey_fde_key.py`` - TKey key derivation tool
* ``test/py/tests/fs_helper.py`` - Filesystem test helpers
* `Tillitis TKey Documentation <https://tillitis.se/>`_