Skip to content

raspberrypi/cryptsetup-passphrase-agent

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

cryptsetup-passphrase-agent

A non-interactive systemd password agent for encrypted disk passphrases written in Rust.

Overview

This agent watches /run/systemd/ask-password/ for password requests from systemd and automatically responds to cryptsetup requests (encrypted disk passphrases) with a configured passphrase. All other password requests are ignored.

This is useful for systems with encrypted disks that need to be unlocked automatically without user interaction, such as headless servers or embedded systems.

Project Structure

src/
├── main.rs              # Entry point and main loop
├── parse.rs             # Parsing utilities for systemd config values
├── password_request.rs  # PasswordRequest parsing and PasswordRequestWatcher iterator
├── cryptsetup.rs        # CryptsetupPassphraseRequest for handling cryptsetup requests
├── passphrase.rs        # Passphrase derivation from device ID
└── rpifwcrypto.rs       # FFI bindings to librpifwcrypto

debian/
├── control              # Package metadata
├── rules                # Build rules
├── changelog            # Package changelog
├── copyright            # Licence information
├── gbp.conf             # git-buildpackage configuration
├── cargo-checksum.json  # Cargo checksum placeholder
├── salsa-ci.yml         # Salsa CI configuration
├── source/format        # Source package format
├── cryptsetup-passphrase-agent.service  # systemd service unit
├── cryptsetup-passphrase-agent.path     # systemd path unit
└── cryptsetup-passphrase-agent.install  # Installation paths

Building for Debian

This project is packaged as a native Debian package and uses Debian's librust-* packages for dependencies rather than downloading crates from crates.io, ensuring an isolated and reproducible build environment.

Build Instructions

Install build dependencies:

sudo mk-build-deps --install --remove

Build the package:

debuild -b -uc -us

The built .deb package will be created in the parent directory.

Incremental Builds with Cargo

Cargo may be used directly in support of incremental builds by setting the following environment variables:

export CARGO_HOME=debian/cargo_home
export CARGO_REGISTRY=debian/cargo_registry

Then use cargo as normal:

cargo build --release
cargo test

The binary will be at target/release/cryptsetup-passphrase-agent.

Usage

The agent must be run as root (or with appropriate privileges) to access /run/systemd/ask-password/ and respond to password requests.

sudo ./target/release/cryptsetup-passphrase-agent

The agent will:

  1. Watch /run/systemd/ask-password/ for new password request files
  2. Parse each ask.* file that appears
  3. For cryptsetup: requests only, send the configured passphrase
  4. Ignore all other password requests

Passphrase Derivation

The passphrase for each encrypted device is derived using:

  1. Device Innate ID - A unique identifier for the block device (e.g., the CID for eMMC/SD cards)
  2. HMAC-SHA256 - The ID is hashed using a firmware OTP key via rpifwcrypto

This ensures that:

  • Each device gets a unique passphrase
  • The passphrase cannot be derived without access to the firmware crypto service
  • The raw key material never leaves the firmware

The device innate ID is retrieved using the block-device-id crate, which reads hardware identifiers from sysfs (e.g., MMC CID from /sys/block/mmcblk0/device/cid or NVMe serial numbers).

systemd Integration

The package installs two systemd units:

cryptsetup-passphrase-agent.path

A path unit that watches /run/systemd/ask-password/ and triggers the service when the directory becomes non-empty. Configured to run before cryptsetup.target:

[Unit]
DefaultDependencies=no
Before=paths.target cryptsetup.target
Conflicts=emergency.service shutdown.target

[Path]
DirectoryNotEmpty=/run/systemd/ask-password
MakeDirectory=yes

[Install]
WantedBy=sysinit.target

cryptsetup-passphrase-agent.service

The service unit that runs the agent:

[Service]
Type=simple
ExecStart=/usr/bin/cryptsetup-passphrase-agent

Enabling

After installing the Debian package:

sudo systemctl enable cryptsetup-passphrase-agent.path

The path unit will automatically start the service when password requests appear.

How It Works

The agent implements the systemd Password Agents protocol:

  1. Uses inotify to watch for IN_CLOSE_WRITE and IN_MOVED_TO events in /run/systemd/ask-password/
  2. Parses .ini format ask files using rust-ini
  3. Validates requests:
    • Checks if the requesting process is still alive
    • Checks if the request has expired (NotAfter field)
    • Only handles cryptsetup: requests (based on Id field)
  4. Sends the passphrase prefixed with + to the Unix datagram socket specified in Socket=

Testing

Unit Tests

With the environment variables set (see above):

cargo test

End-to-End Testing

To test the full passphrase derivation and disk unlock flow, you need a Raspberry Pi with an OTP key provisioned and a spare block device.

1. Prerequisites

Ensure an OTP key is provisioned:

rpi-fw-crypto get-key-status 1

2. Prepare a Test Block Device

Format the block device and create a single partition. In this example, we use mmcblk0 (the SD card) whilst booting from NVMe:

# Save the device's CID for passphrase derivation
cp /sys/block/mmcblk0/device/cid ./tmp_cid

3. Derive the Passphrase

Derive the disk encryption passphrase using the firmware HMAC:

rpi-fw-crypto hmac --in ./tmp_cid --key-id 1 --outform hex

This prints the passphrase to stdout. Use this when setting up LUKS.

4. Set Up LUKS Encryption

Format the partition with LUKS using the derived passphrase:

sudo cryptsetup luksFormat /dev/mmcblk0p1

Enter the passphrase from step 3 when prompted.

5. Test the Password Agent

The password agent only responds to requests from systemd services, not interactive terminal sessions. In production, /etc/crypttab entries are processed by systemd-cryptsetup-generator to create unlock services. For testing, create a service manually:

Create /etc/systemd/system/systemd-cryptsetup@test.service:

[Unit]
Description=Cryptography Setup for %I
Documentation=man:crypttab(5) man:systemd-cryptsetup-generator(8) man:systemd-cryptsetup@.service(8)

DefaultDependencies=no
After=cryptsetup-pre.target systemd-udevd-kernel.socket
Before=blockdev@dev-mapper-%i.target
Wants=blockdev@dev-mapper-%i.target
IgnoreOnIsolate=true
Conflicts=umount.target
Before=umount.target
Before=cryptsetup.target
BindsTo=dev-mmcblk0p1
After=dev-mmcblk0p1

[Service]
Type=oneshot
RemainAfterExit=yes
TimeoutSec=infinity
KeyringMode=shared
OOMScoreAdjust=500
ExecStart=/usr/bin/systemd-cryptsetup attach 'test' '/dev/mmcblk0p1' 'none' 'luks,discard'
ExecStop=/usr/bin/systemd-cryptsetup detach 'test'

Reload systemd to pick up the new service, then start it:

sudo systemctl daemon-reload
sudo systemctl start systemd-cryptsetup@test.service

6. Verify

Check the agent responded successfully:

systemctl status cryptsetup-passphrase-agent.service

The encrypted device should now be available at /dev/mapper/test.

Security Considerations

  • Passphrases are derived at runtime using hardware-bound cryptography:
    • The device's innate ID (e.g., MMC CID) is read from sysfs
    • HMAC-SHA256 is computed using a firmware OTP key that never leaves the hardware
  • The agent runs with root privileges to access block device information and password sockets
  • The passphrase is sent over a Unix socket with restricted permissions
  • The systemd service unit includes security hardening options

Dependencies

All Rust dependencies are satisfied by Debian's librust-* packages as specified in debian/control. This ensures the build is completely isolated from external package registries and uses only packages that have been reviewed and packaged by Debian maintainers.

Rust Crates

  • block-device-id - Retrieve innate unique identifiers from block devices
  • nix - Safe Rust bindings to Unix APIs
  • rust-ini - INI file parser

System Libraries

  • librpifwcrypto - Raspberry Pi firmware cryptography service

Licence

BSD-3-Clause - See LICENSE file for details.

About

No description, website, or topics provided.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors