Naïm Favier 2021-05-26 15:43:38 +02:00 committed by GitHub
parent 11e02bd01d
commit a6788be01a
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 63 additions and 44 deletions

View File

@ -140,24 +140,27 @@ let
umount /crypt-ramfs 2>/dev/null
'';
openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, gpgCard, fido2, fallbackToPassword, preOpenCommands, postOpenCommands,... }: assert name' == name;
openCommand = name: dev: assert name == dev.name;
let
csopen = "cryptsetup luksOpen ${device} ${name} ${optionalString allowDiscards "--allow-discards"} ${optionalString (header != null) "--header=${header}"}";
cschange = "cryptsetup luksChangeKey ${device} ${optionalString (header != null) "--header=${header}"}";
csopen = "cryptsetup luksOpen ${dev.device} ${dev.name}"
+ optionalString dev.allowDiscards " --allow-discards"
+ optionalString dev.bypassWorkqueues " --perf-no_read_workqueue --perf-no_write_workqueue"
+ optionalString (dev.header != null) " --header=${dev.header}";
cschange = "cryptsetup luksChangeKey ${dev.device} ${optionalString (dev.header != null) "--header=${dev.header}"}";
in ''
# Wait for luksRoot (and optionally keyFile and/or header) to appear, e.g.
# if on a USB drive.
wait_target "device" ${device} || die "${device} is unavailable"
wait_target "device" ${dev.device} || die "${dev.device} is unavailable"
${optionalString (header != null) ''
wait_target "header" ${header} || die "${header} is unavailable"
${optionalString (dev.header != null) ''
wait_target "header" ${dev.header} || die "${dev.header} is unavailable"
''}
do_open_passphrase() {
local passphrase
while true; do
echo -n "Passphrase for ${device}: "
echo -n "Passphrase for ${dev.device}: "
passphrase=
while true; do
if [ -e /crypt-ramfs/passphrase ]; then
@ -166,7 +169,7 @@ let
break
else
# ask cryptsetup-askpass
echo -n "${device}" > /crypt-ramfs/device
echo -n "${dev.device}" > /crypt-ramfs/device
# and try reading it from /dev/console with a timeout
IFS= read -t 1 -r passphrase
@ -182,7 +185,7 @@ let
fi
fi
done
echo -n "Verifying passphrase for ${device}..."
echo -n "Verifying passphrase for ${dev.device}..."
echo -n "$passphrase" | ${csopen} --key-file=-
if [ $? == 0 ]; then
echo " - success"
@ -202,13 +205,13 @@ let
# LUKS
open_normally() {
${if (keyFile != null) then ''
if wait_target "key file" ${keyFile}; then
${csopen} --key-file=${keyFile} \
${optionalString (keyFileSize != null) "--keyfile-size=${toString keyFileSize}"} \
${optionalString (keyFileOffset != null) "--keyfile-offset=${toString keyFileOffset}"}
${if (dev.keyFile != null) then ''
if wait_target "key file" ${dev.keyFile}; then
${csopen} --key-file=${dev.keyFile} \
${optionalString (dev.keyFileSize != null) "--keyfile-size=${toString dev.keyFileSize}"} \
${optionalString (dev.keyFileOffset != null) "--keyfile-offset=${toString dev.keyFileOffset}"}
else
${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable"
${if dev.fallbackToPassword then "echo" else "die"} "${dev.keyFile} is unavailable"
echo " - failing back to interactive password prompt"
do_open_passphrase
fi
@ -217,7 +220,7 @@ let
''}
}
${optionalString (luks.yubikeySupport && (yubikey != null)) ''
${optionalString (luks.yubikeySupport && (dev.yubikey != null)) ''
# YubiKey
rbtohex() {
( od -An -vtx1 | tr -d ' \n' )
@ -243,16 +246,16 @@ let
local new_response
local new_k_luks
mount -t ${yubikey.storage.fsType} ${yubikey.storage.device} /crypt-storage || \
mount -t ${dev.yubikey.storage.fsType} ${dev.yubikey.storage.device} /crypt-storage || \
die "Failed to mount YubiKey salt storage device"
salt="$(cat /crypt-storage${yubikey.storage.path} | sed -n 1p | tr -d '\n')"
iterations="$(cat /crypt-storage${yubikey.storage.path} | sed -n 2p | tr -d '\n')"
salt="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 1p | tr -d '\n')"
iterations="$(cat /crypt-storage${dev.yubikey.storage.path} | sed -n 2p | tr -d '\n')"
challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)"
response="$(ykchalresp -${toString dev.yubikey.slot} -x $challenge 2>/dev/null)"
for try in $(seq 3); do
${optionalString yubikey.twoFactor ''
${optionalString dev.yubikey.twoFactor ''
echo -n "Enter two-factor passphrase: "
k_user=
while true; do
@ -278,9 +281,9 @@ let
''}
if [ ! -z "$k_user" ]; then
k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
else
k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)"
k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $iterations $response | rbtohex)"
fi
echo -n "$k_luks" | hextorb | ${csopen} --key-file=-
@ -302,7 +305,7 @@ let
[ "$opened" == false ] && die "Maximum authentication errors reached"
echo -n "Gathering entropy for new salt (please enter random keys to generate entropy if this blocks for long)..."
for i in $(seq ${toString yubikey.saltLength}); do
for i in $(seq ${toString dev.yubikey.saltLength}); do
byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)";
new_salt="$new_salt$byte";
echo -n .
@ -310,25 +313,25 @@ let
echo "ok"
new_iterations="$iterations"
${optionalString (yubikey.iterationStep > 0) ''
new_iterations="$(($new_iterations + ${toString yubikey.iterationStep}))"
${optionalString (dev.yubikey.iterationStep > 0) ''
new_iterations="$(($new_iterations + ${toString dev.yubikey.iterationStep}))"
''}
new_challenge="$(echo -n $new_salt | openssl-wrap dgst -binary -sha512 | rbtohex)"
new_response="$(ykchalresp -${toString yubikey.slot} -x $new_challenge 2>/dev/null)"
new_response="$(ykchalresp -${toString dev.yubikey.slot} -x $new_challenge 2>/dev/null)"
if [ ! -z "$k_user" ]; then
new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
else
new_k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)"
new_k_luks="$(echo | pbkdf2-sha512 ${toString dev.yubikey.keyLength} $new_iterations $new_response | rbtohex)"
fi
echo -n "$new_k_luks" | hextorb > /crypt-ramfs/new_key
echo -n "$k_luks" | hextorb | ${cschange} --key-file=- /crypt-ramfs/new_key
if [ $? == 0 ]; then
echo -ne "$new_salt\n$new_iterations" > /crypt-storage${yubikey.storage.path}
echo -ne "$new_salt\n$new_iterations" > /crypt-storage${dev.yubikey.storage.path}
else
echo "Warning: Could not update LUKS key, current challenge persists!"
fi
@ -338,7 +341,7 @@ let
}
open_with_hardware() {
if wait_yubikey ${toString yubikey.gracePeriod}; then
if wait_yubikey ${toString dev.yubikey.gracePeriod}; then
do_open_yubikey
else
echo "No YubiKey found, falling back to non-YubiKey open procedure"
@ -347,7 +350,7 @@ let
}
''}
${optionalString (luks.gpgSupport && (gpgCard != null)) ''
${optionalString (luks.gpgSupport && (dev.gpgCard != null)) ''
do_open_gpg_card() {
# Make all of these local to this function
@ -355,12 +358,12 @@ let
local pin
local opened
gpg --import /gpg-keys/${device}/pubkey.asc > /dev/null 2> /dev/null
gpg --import /gpg-keys/${dev.device}/pubkey.asc > /dev/null 2> /dev/null
gpg --card-status > /dev/null 2> /dev/null
for try in $(seq 3); do
echo -n "PIN for GPG Card associated with device ${device}: "
echo -n "PIN for GPG Card associated with device ${dev.device}: "
pin=
while true; do
if [ -e /crypt-ramfs/passphrase ]; then
@ -382,8 +385,8 @@ let
fi
fi
done
echo -n "Verifying passphrase for ${device}..."
echo -n "$pin" | gpg -q --batch --passphrase-fd 0 --pinentry-mode loopback -d /gpg-keys/${device}/cryptkey.gpg 2> /dev/null | ${csopen} --key-file=- > /dev/null 2> /dev/null
echo -n "Verifying passphrase for ${dev.device}..."
echo -n "$pin" | gpg -q --batch --passphrase-fd 0 --pinentry-mode loopback -d /gpg-keys/${dev.device}/cryptkey.gpg 2> /dev/null | ${csopen} --key-file=- > /dev/null 2> /dev/null
if [ $? == 0 ]; then
echo " - success"
${if luks.reusePassphrases then ''
@ -403,7 +406,7 @@ let
}
open_with_hardware() {
if wait_gpgcard ${toString gpgCard.gracePeriod}; then
if wait_gpgcard ${toString dev.gpgCard.gracePeriod}; then
do_open_gpg_card
else
echo "No GPG Card found, falling back to normal open procedure"
@ -412,15 +415,15 @@ let
}
''}
${optionalString (luks.fido2Support && (fido2.credential != null)) ''
${optionalString (luks.fido2Support && (dev.fido2.credential != null)) ''
open_with_hardware() {
local passsphrase
${if fido2.passwordLess then ''
${if dev.fido2.passwordLess then ''
export passphrase=""
'' else ''
read -rsp "FIDO2 salt for ${device}: " passphrase
read -rsp "FIDO2 salt for ${dev.device}: " passphrase
echo
''}
${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") ''
@ -428,7 +431,7 @@ let
echo "Please move your mouse to create needed randomness."
''}
echo "Waiting for your FIDO2 device..."
fido2luks open ${device} ${name} ${fido2.credential} --await-dev ${toString fido2.gracePeriod} --salt string:$passphrase
fido2luks open ${dev.device} ${dev.name} ${dev.fido2.credential} --await-dev ${toString dev.fido2.gracePeriod} --salt string:$passphrase
if [ $? -ne 0 ]; then
echo "No FIDO2 key found, falling back to normal open procedure"
open_normally
@ -437,16 +440,16 @@ let
''}
# commands to run right before we mount our device
${preOpenCommands}
${dev.preOpenCommands}
${if (luks.yubikeySupport && (yubikey != null)) || (luks.gpgSupport && (gpgCard != null)) || (luks.fido2Support && (fido2.credential != null)) then ''
${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && (dev.fido2.credential != null)) then ''
open_with_hardware
'' else ''
open_normally
''}
# commands to run right after we mounted our device
${postOpenCommands}
${dev.postOpenCommands}
'';
askPass = pkgs.writeScriptBin "cryptsetup-askpass" ''
@ -621,6 +624,17 @@ in
'';
};
bypassWorkqueues = mkOption {
default = false;
type = types.bool;
description = ''
Whether to bypass dm-crypt's internal read and write workqueues.
Enabling this should improve performance on SSDs; see
<link xlink:href="https://wiki.archlinux.org/index.php/Dm-crypt/Specialties#Disable_workqueue_for_increased_solid_state_drive_(SSD)_performance">here</link>
for more information. Needs Linux 5.9 or later.
'';
};
fallbackToPassword = mkOption {
default = false;
type = types.bool;
@ -833,6 +847,11 @@ in
{ assertion = !(luks.fido2Support && luks.yubikeySupport);
message = "FIDO2 and YubiKey may not be used at the same time.";
}
{ assertion = any (dev: dev.bypassWorkqueues) (attrValues luks.devices)
-> versionAtLeast kernelPackages.kernel.version "5.9";
message = "boot.initrd.luks.devices.<name>.bypassWorkqueues is not supported for kernels older than 5.9";
}
];
# actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested