-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fido2luks: unlock LUKS volumes at boot time using a FIDO2 token
- Loading branch information
0 parents
commit f8d55b8
Showing
15 changed files
with
643 additions
and
0 deletions.
There are no files selected for viewing
Large diffs are not rendered by default.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
build: | ||
@echo "Run 'make install' to install fido2luks" | ||
|
||
clean: | ||
@echo "Nothing to do" | ||
|
||
install: | ||
install -D -m 0755 keyscript.sh $(DESTDIR)/lib/fido2luks/keyscript.sh | ||
install -D -m 0755 initramfs-hook $(DESTDIR)/etc/initramfs-tools/hooks/fido2luks |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,72 @@ | ||
# fido2luks | ||
|
||
This is an extension to initramfs-tools to unlock LUKS-encrypted | ||
volumes at boot time using a FIDO2 token (YubiKey, Nitrokey, ...). | ||
|
||
`fido2luks` is designed for scenarios where a FIDO2 token was enrolled | ||
into a LUKS volume using `systemd-cryptenroll --fido2-device` but | ||
systemd itself is not used in the initramfs. | ||
|
||
This has successfully been tested with Debian bookworm and trixie (as | ||
of May 2024). | ||
|
||
## How to use it | ||
|
||
- First of all, a word of warning: this can potentially render your | ||
system unbootable, so make sure that you have a backup of your files | ||
or a working initramfs that you can use as a fallback in case things | ||
go wrong. | ||
|
||
- Dependencies: you need `initramfs-tools`, `fido2-tools` and `jq` on | ||
your system. | ||
|
||
- Install `fido2luks`: you can generate a Debian package using the | ||
scripts that are included for convenience. Simply run `fakeroot | ||
debian/rules binary` and install the resulting `.deb` file. If you | ||
prefer not to do that you can run `make install` instead. | ||
|
||
- Make sure that the LUKS volume has been set up, e.g.: | ||
`systemd-cryptenroll --fido2-device=auto --fido2-with-client-pin=true --fido2-with-user-presence=true /dev/XXX`. | ||
You should be able to see the `systemd-fido2` token data if you run | ||
`cryptsetup luksDump /dev/XXX`. | ||
|
||
- Edit `/etc/crypttab` and add `keyscript=/lib/fido2luks/keyscript.sh` | ||
to the options of the volume that you want to unlock. | ||
|
||
- Generate a new initramfs with `update-initramfs -u`. | ||
|
||
This should be all. Next time you boot the system `fido2luks` should | ||
detect if your FIDO2 token is inserted and use it to unlock the LUKS | ||
volume. If the token is not detected then it will fall back to using a | ||
regular passphrase as usual. | ||
|
||
## How this works | ||
|
||
If you are not interested in the technical details you can skip this | ||
section. | ||
|
||
When systemd enrolls a FIDO2 token into a LUKS volume it uses an | ||
extension called hmac-secret, supported by many hardware tokens. | ||
|
||
In a nutshell, the token calculates an HMAC using a secret that never | ||
leaves the device and a salt provided by the user. The result is sent | ||
back to the user and is used to unlock the LUKS volume. | ||
|
||
Since nothing is stored on the hardware token itself the user needs to | ||
provide some data that is kept on the LUKS header: | ||
|
||
- A credential ID (previously generated during the enrollment process). | ||
- A _relying party_ ID (`io.systemd.cryptsetup` in this case). | ||
- The aforementioned salt (which should be random and different for | ||
each LUKS volume). | ||
- Some settings such as whether to require a PIN or presence | ||
verification (usually physically touching the USB key). | ||
|
||
You can look at the scripts under the examples/ directory to see how | ||
to generate your own credentials and secrets. See also the | ||
`fido2-cred(1)` and `fido2-assert(1)` manpages for more details. | ||
|
||
## Credits and license | ||
|
||
fido2luks was written by Alberto Garcia and is distributed under the | ||
GNU GPL. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
fido2luks (0.0.1-1) UNRELEASED; urgency=medium | ||
|
||
* Initial release. | ||
|
||
-- Alberto Garcia <[email protected]> Sat, 11 May 2024 18:07:00 +0200 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
Source: fido2luks | ||
Section: admin | ||
Priority: optional | ||
Maintainer: Alberto Garcia <[email protected]> | ||
Build-Depends: debhelper-compat (= 13) | ||
Standards-Version: 4.7.0 | ||
|
||
Package: fido2luks | ||
Architecture: all | ||
Depends: ${shlibs:Depends}, ${misc:Depends}, initramfs-tools, fido2-tools, jq | ||
Description: Unlock a LUKS volume using a FIDO2 token on boot | ||
This is an extension to initramfs-tools to unlock a LUKS-encrypted | ||
disk at boot time using a FIDO2 token. | ||
. | ||
It is designed for scenarios where a FIDO2 token was enrolled into a | ||
LUKS volume using systemd-cryptenroll --fido2-device. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ | ||
Upstream-Name: fido2luks | ||
|
||
Files: * | ||
Copyright: 2024 Alberto Garcia <[email protected]> | ||
License: GPL-2+ | ||
This package is free software; you can redistribute it and/or modify | ||
it under the terms of the GNU General Public License as published by | ||
the Free Software Foundation; either version 2 of the License, or | ||
(at your option) any later version. | ||
. | ||
This package is distributed in the hope that it will be useful, | ||
but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
GNU General Public License for more details. | ||
. | ||
You should have received a copy of the GNU General Public License | ||
along with this program. If not, see <https://www.gnu.org/licenses/> | ||
. | ||
On Debian systems, the complete text of the GNU General | ||
Public License version 2 can be found in "/usr/share/common-licenses/GPL-2". |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
README.md |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
examples/* |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
#!/usr/bin/make -f | ||
%: | ||
dh $@ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
3.0 (quilt) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# In order to use fido2luks add 'keyscript=/lib/fido2luks/keyscript.sh' | ||
# and run update-initramfs -u | ||
|
||
<device>_crypt UUID=<LUKS-VOLUME-UUID> none luks,keyscript=/lib/fido2luks/keyscript.sh |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
#!/bin/sh | ||
|
||
set -eu | ||
|
||
RELYING_PARTY_ID="org.test.some_app" | ||
USER_NAME="User Name" | ||
USER_ID="User ID" | ||
CLIENT_DATA="" # This can be empty | ||
|
||
FIDO2_DEVICE=$(fido2-token -L | head -n 1 | cut -d : -f 1) | ||
|
||
if [ -z "$FIDO2_DEVICE" ]; then | ||
echo "ERROR: no FIDO2 device found" | ||
exit 1 | ||
fi | ||
|
||
CRED_PARAMS="$(mktemp /tmp/cred-params.XXXXXX)" | ||
CRED_DATA="$(mktemp /tmp/cred-data.XXXXXX)" | ||
CRED_VERIFY="$(mktemp /tmp/cred-verify.XXXXXX)" | ||
trap "rm -f $CRED_PARAMS $CRED_DATA $CRED_VERIFY" INT EXIT | ||
|
||
printf "%s\n%s\n%s\n%s\n" \ | ||
$(echo -n "$CLIENT_DATA" | openssl sha256 -binary | base64) \ | ||
"$RELYING_PARTY_ID" \ | ||
"$USER_NAME" \ | ||
$(echo -n "$USER_ID" | openssl sha256 -binary | base64) > "$CRED_PARAMS" | ||
|
||
fido2-cred -M -h -i "$CRED_PARAMS" -o "$CRED_DATA" "$FIDO2_DEVICE" | ||
fido2-cred -V -h -i "$CRED_DATA" -o "$CRED_VERIFY" | ||
|
||
CRED_ID=$(head -n 1 "$CRED_VERIFY") | ||
|
||
echo "A new credential has been generated" | ||
echo "ID: $CRED_ID" | ||
echo "Public key:" | ||
tail -n +2 "$CRED_VERIFY" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
#!/bin/bash | ||
|
||
set -eu | ||
|
||
# Set CREDENTIAL_ID and PUBLIC_KEY to the values that you got | ||
# from generate-cred.sh, otherwise this won't work | ||
|
||
RELYING_PARTY_ID="org.test.some_app" | ||
CREDENTIAL_ID="E2NDqozxIGOlcUhlrg6+XIjZSRC8i5C69PgOiHzWXGBZTJ6No9fa6fEcPvQjxL5slKGVr7ioYBcxKwPREJnuMA==" | ||
SALT="The HMAC secret depends on this value" | ||
CLIENT_DATA="" # This can be empty | ||
|
||
# Set these to true or false | ||
REQUIRE_PIN=false | ||
REQUIRE_USER_PRESENCE=true | ||
|
||
# The public key is only needed to verify the result, see below | ||
PUBLIC_KEY=' | ||
-----BEGIN PUBLIC KEY----- | ||
MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEjX1Eiv/1H39f+b+MmSTymbdR8l3+ | ||
GqJHf3X0CyREljyHi7mS5LgkmsyvO0fgc6SryYCUKG6MREdnKirNilXLYQ== | ||
-----END PUBLIC KEY----- | ||
' | ||
|
||
FIDO2_DEVICE=$(fido2-token -L | head -n 1 | cut -d : -f 1) | ||
|
||
if [ -z "$FIDO2_DEVICE" ]; then | ||
echo "ERROR: no FIDO2 device found" | ||
exit 1 | ||
fi | ||
|
||
ASSERT_PARAMS="$(mktemp /tmp/cred-params.XXXXXX)" | ||
ASSERT_DATA="$(mktemp /tmp/cred-data.XXXXXX)" | ||
trap "rm -f $ASSERT_PARAMS $ASSERT_DATA" INT EXIT | ||
|
||
printf "%s\n%s\n%s\n%s\n" \ | ||
$(echo -n "$CLIENT_DATA" | openssl sha256 -binary | base64) \ | ||
"$RELYING_PARTY_ID" \ | ||
"$CREDENTIAL_ID" \ | ||
$(echo -n "$SALT" | openssl sha256 -binary | base64) > "$ASSERT_PARAMS" | ||
|
||
fido2-assert -G -h \ | ||
-t up="$REQUIRE_USER_PRESENCE" -t pin="$REQUIRE_PIN" \ | ||
-i "$ASSERT_PARAMS" -o "$ASSERT_DATA" "$FIDO2_DEVICE" | ||
|
||
# If you want to verify the result with the public key: | ||
# fido2-assert -V -h -i "$ASSERT_DATA" <(echo "$PUBLIC_KEY") es256 | ||
|
||
HMAC_SECRET="$(tail -n 1 "$ASSERT_DATA")" | ||
echo "The generated secret is $HMAC_SECRET" |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
#!/bin/sh | ||
|
||
PREREQ="" | ||
|
||
prereqs() { | ||
echo "$PREREQ" | ||
} | ||
|
||
case "$1" in | ||
prereqs) | ||
prereqs | ||
exit 0 | ||
;; | ||
esac | ||
|
||
. "${CONFDIR}/initramfs.conf" | ||
. /usr/share/initramfs-tools/hook-functions | ||
|
||
copy_exec /usr/bin/fido2-assert /bin | ||
copy_exec /usr/bin/fido2-token /bin | ||
copy_exec /usr/bin/jq /bin |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,64 @@ | ||
#!/bin/sh | ||
|
||
cleanup () { | ||
rm -f "$ASSERT_PARAMS" "$LUKS_TOKEN" | ||
} | ||
|
||
ASSERT_PARAMS=$(mktemp -t params.XXXXXX) | ||
LUKS_TOKEN=$(mktemp -t token.XXXXXX) | ||
trap cleanup INT EXIT | ||
|
||
cryptsetup luksDump --dump-json-metadata "$CRYPTTAB_SOURCE" | \ | ||
jq -e '[.tokens[] | select(."fido2-credential" != null)][0]' > "$LUKS_TOKEN" | ||
|
||
if [ $? -ne 0 ]; then | ||
echo "*** No FIDO2 credentials found in $CRYPTTAB_SOURCE" >&2 | ||
else | ||
echo "*** Waiting for a FIDO2 authenticator..." >&2 | ||
for f in $(seq 5); do | ||
FIDO2_AUTHENTICATOR=$(fido2-token -L) | ||
[ -n "$FIDO2_AUTHENTICATOR" ] && break | ||
sleep 1 | ||
done | ||
|
||
if [ -n "$FIDO2_AUTHENTICATOR" ]; then | ||
echo "*** Found FIDO2 authenticator $FIDO2_AUTHENTICATOR" >&2 | ||
|
||
REQ_PIN=$(jq -r '."fido2-clientPin-required"' "$LUKS_TOKEN") | ||
REQ_UP=$(jq -r '."fido2-up-required"' "$LUKS_TOKEN") | ||
|
||
# echo -n | openssl sha256 -binary | base64 | ||
jq -r '"47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=", | ||
."fido2-rp", | ||
."fido2-credential", | ||
."fido2-salt"' "$LUKS_TOKEN" > "$ASSERT_PARAMS" | ||
|
||
if [ "$REQ_PIN" = "true" ]; then | ||
stty -echo | ||
fi | ||
|
||
sleep 2 | ||
|
||
FIDO2_DEV=${FIDO2_AUTHENTICATOR%%:*} | ||
SECRET=$(fido2-assert -G -h -t up="$REQ_UP" -t pin="$REQ_PIN" \ | ||
-i "$ASSERT_PARAMS" "$FIDO2_DEV" | tail -n 1) | ||
|
||
if [ "$REQ_PIN" = "true" ]; then | ||
stty echo | ||
fi | ||
|
||
if [ -n "$SECRET" ]; then | ||
echo >&2 | ||
echo -n "$SECRET" | ||
exit 0 | ||
else | ||
echo "*** Error obtaining secret from $FIDO2_DEV" >&2 | ||
fi | ||
else | ||
echo "*** No FIDO2 authenticator found" >&2 | ||
fi | ||
fi | ||
|
||
echo "*** Unlocking $CRYPTTAB_NAME using a regular passphrase" >&2 | ||
|
||
/lib/cryptsetup/askpass "Enter passphrase: " |