From 9dddbe15c421b9f62cddee6f8cc4147f9d40cd86 Mon Sep 17 00:00:00 2001 From: Gregor Kleen Date: Fri, 23 Apr 2021 21:07:23 +0200 Subject: clevis: implement luksroot support --- modules/luksroot.nix | 1020 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 1020 insertions(+) create mode 100644 modules/luksroot.nix (limited to 'modules') diff --git a/modules/luksroot.nix b/modules/luksroot.nix new file mode 100644 index 00000000..e1a910d7 --- /dev/null +++ b/modules/luksroot.nix @@ -0,0 +1,1020 @@ +{ config, lib, pkgs, ... }: + +with lib; + +let + luks = config.boot.initrd.luks; + kernelPackages = config.boot.kernelPackages; + + commonFunctions = '' + die() { + echo "$@" >&2 + exit 1 + } + + dev_exist() { + local target="$1" + if [ -e $target ]; then + return 0 + else + local uuid=$(echo -n $target | sed -e 's,UUID=\(.*\),\1,g') + blkid --uuid $uuid >/dev/null + return $? + fi + } + + wait_target() { + local name="$1" + local target="$2" + local secs="''${3:-10}" + local desc="''${4:-$name $target to appear}" + + if ! dev_exist $target; then + echo -n "Waiting $secs seconds for $desc..." + local success=false; + for try in $(seq $secs); do + echo -n "." + sleep 1 + if dev_exist $target; then + success=true + break + fi + done + if [ $success == true ]; then + echo " - success"; + return 0 + else + echo " - failure"; + return 1 + fi + fi + return 0 + } + + wait_yubikey() { + local secs="''${1:-10}" + + ykinfo -v 1>/dev/null 2>&1 + if [ $? != 0 ]; then + echo -n "Waiting $secs seconds for YubiKey to appear..." + local success=false + for try in $(seq $secs); do + echo -n . + sleep 1 + ykinfo -v 1>/dev/null 2>&1 + if [ $? == 0 ]; then + success=true + break + fi + done + if [ $success == true ]; then + echo " - success"; + return 0 + else + echo " - failure"; + return 1 + fi + fi + return 0 + } + + wait_gpgcard() { + local secs="''${1:-10}" + + gpg --card-status > /dev/null 2> /dev/null + if [ $? != 0 ]; then + echo -n "Waiting $secs seconds for GPG Card to appear" + local success=false + for try in $(seq $secs); do + echo -n . + sleep 1 + gpg --card-status > /dev/null 2> /dev/null + if [ $? == 0 ]; then + success=true + break + fi + done + if [ $success == true ]; then + echo " - success"; + return 0 + else + echo " - failure"; + return 1 + fi + fi + return 0 + } + ''; + + preCommands = '' + # A place to store crypto things + + # A ramfs is used here to ensure that the file used to update + # the key slot with cryptsetup will never get swapped out. + # Warning: Do NOT replace with tmpfs! + mkdir -p /crypt-ramfs + mount -t ramfs none /crypt-ramfs + + # Cryptsetup locking directory + mkdir -p /run/cryptsetup + + # For YubiKey salt storage + mkdir -p /crypt-storage + + ${optionalString luks.gpgSupport '' + export GPG_TTY=$(tty) + export GNUPGHOME=/crypt-ramfs/.gnupg + + gpg-agent --daemon --scdaemon-program $out/bin/scdaemon > /dev/null 2> /dev/null + ''} + + # Disable all input echo for the whole stage. We could use read -s + # instead but that would ocasionally leak characters between read + # invocations. + stty -echo + ''; + + postCommands = '' + stty echo + umount /crypt-storage 2>/dev/null + umount /crypt-ramfs 2>/dev/null + ''; + + openCommand = name': { name, device, header, keyFile, keyFileSize, keyFileOffset, allowDiscards, yubikey, gpgCard, fido2, clevis, fallbackToPassword, preOpenCommands, postOpenCommands, ... }: assert name' == 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}"}"; + 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" + + ${optionalString (header != null) '' + wait_target "header" ${header} || die "${header} is unavailable" + ''} + + do_open_passphrase() { + local passphrase + + while true; do + echo -n "Passphrase for ${device}: " + passphrase= + while true; do + if [ -e /crypt-ramfs/passphrase ]; then + echo "reused" + passphrase=$(cat /crypt-ramfs/passphrase) + break + else + # ask cryptsetup-askpass + echo -n "${device}" > /crypt-ramfs/device + + # and try reading it from /dev/console with a timeout + IFS= read -t 1 -r passphrase + if [ -n "$passphrase" ]; then + ${if luks.reusePassphrases then '' + # remember it for the next device + echo -n "$passphrase" > /crypt-ramfs/passphrase + '' else '' + # Don't save it to ramfs. We are very paranoid + ''} + echo + break + fi + fi + done + echo -n "Verifying passphrase for ${device}..." + echo -n "$passphrase" | ${csopen} --key-file=- + if [ $? == 0 ]; then + echo " - success" + ${if luks.reusePassphrases then '' + # we don't rm here because we might reuse it for the next device + '' else '' + rm -f /crypt-ramfs/passphrase + ''} + break + else + echo " - failure" + # ask for a different one + rm -f /crypt-ramfs/passphrase + fi + done + } + + # 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}"} + else + ${if fallbackToPassword then "echo" else "die"} "${keyFile} is unavailable" + echo " - failing back to interactive password prompt" + do_open_passphrase + fi + '' else '' + do_open_passphrase + ''} + } + + ${optionalString (luks.yubikeySupport && (yubikey != null)) '' + # YubiKey + rbtohex() { + ( od -An -vtx1 | tr -d ' \n' ) + } + + hextorb() { + ( tr '[:lower:]' '[:upper:]' | sed -e 's/\([0-9A-F]\{2\}\)/\\\\\\x\1/gI' | xargs printf ) + } + + do_open_yubikey() { + # Make all of these local to this function + # to prevent their values being leaked + local salt + local iterations + local k_user + local challenge + local response + local k_luks + local opened + local new_salt + local new_iterations + local new_challenge + local new_response + local new_k_luks + + mount -t ${yubikey.storage.fsType} ${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')" + challenge="$(echo -n $salt | openssl-wrap dgst -binary -sha512 | rbtohex)" + response="$(ykchalresp -${toString yubikey.slot} -x $challenge 2>/dev/null)" + + for try in $(seq 3); do + ${optionalString yubikey.twoFactor '' + echo -n "Enter two-factor passphrase: " + k_user= + while true; do + if [ -e /crypt-ramfs/passphrase ]; then + echo "reused" + k_user=$(cat /crypt-ramfs/passphrase) + break + else + # Try reading it from /dev/console with a timeout + IFS= read -t 1 -r k_user + if [ -n "$k_user" ]; then + ${if luks.reusePassphrases then '' + # Remember it for the next device + echo -n "$k_user" > /crypt-ramfs/passphrase + '' else '' + # Don't save it to ramfs. We are very paranoid + ''} + echo + break + fi + fi + done + ''} + + if [ ! -z "$k_user" ]; then + k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)" + else + k_luks="$(echo | pbkdf2-sha512 ${toString yubikey.keyLength} $iterations $response | rbtohex)" + fi + + echo -n "$k_luks" | hextorb | ${csopen} --key-file=- + + if [ $? == 0 ]; then + opened=true + ${if luks.reusePassphrases then '' + # We don't rm here because we might reuse it for the next device + '' else '' + rm -f /crypt-ramfs/passphrase + ''} + break + else + opened=false + echo "Authentication failed!" + fi + done + + [ "$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 + byte="$(dd if=/dev/random bs=1 count=1 2>/dev/null | rbtohex)"; + new_salt="$new_salt$byte"; + echo -n . + done; + echo "ok" + + new_iterations="$iterations" + ${optionalString (yubikey.iterationStep > 0) '' + new_iterations="$(($new_iterations + ${toString 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)" + + if [ ! -z "$k_user" ]; then + new_k_luks="$(echo -n $k_user | pbkdf2-sha512 ${toString yubikey.keyLength} $new_iterations $new_response | rbtohex)" + else + new_k_luks="$(echo | pbkdf2-sha512 ${toString 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} + else + echo "Warning: Could not update LUKS key, current challenge persists!" + fi + + rm -f /crypt-ramfs/new_key + umount /crypt-storage + } + + open_with_hardware() { + if wait_yubikey ${toString yubikey.gracePeriod}; then + do_open_yubikey + else + echo "No YubiKey found, falling back to non-YubiKey open procedure" + open_normally + fi + } + ''} + + ${optionalString (luks.gpgSupport && (gpgCard != null)) '' + + do_open_gpg_card() { + # Make all of these local to this function + # to prevent their values being leaked + local pin + local opened + + gpg --import /gpg-keys/${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}: " + pin= + while true; do + if [ -e /crypt-ramfs/passphrase ]; then + echo "reused" + pin=$(cat /crypt-ramfs/passphrase) + break + else + # and try reading it from /dev/console with a timeout + IFS= read -t 1 -r pin + if [ -n "$pin" ]; then + ${if luks.reusePassphrases then '' + # remember it for the next device + echo -n "$pin" > /crypt-ramfs/passphrase + '' else '' + # Don't save it to ramfs. We are very paranoid + ''} + echo + break + 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 + if [ $? == 0 ]; then + echo " - success" + ${if luks.reusePassphrases then '' + # we don't rm here because we might reuse it for the next device + '' else '' + rm -f /crypt-ramfs/passphrase + ''} + break + else + echo " - failure" + # ask for a different one + rm -f /crypt-ramfs/passphrase + fi + done + + [ "$opened" == false ] && die "Maximum authentication errors reached" + } + + open_with_hardware() { + if wait_gpgcard ${toString gpgCard.gracePeriod}; then + do_open_gpg_card + else + echo "No GPG Card found, falling back to normal open procedure" + open_normally + fi + } + ''} + + ${optionalString (luks.fido2Support && (fido2.credential != null)) '' + + open_with_hardware() { + local passsphrase + + ${if fido2.passwordLess then '' + export passphrase="" + '' else '' + read -rsp "FIDO2 salt for ${device}: " passphrase + echo + ''} + ${optionalString (lib.versionOlder kernelPackages.kernel.version "5.4") '' + echo "On systems with Linux Kernel < 5.4, it might take a while to initialize the CRNG, you might want to use linuxPackages_latest." + 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 + if [ $? -ne 0 ]; then + echo "No FIDO2 key found, falling back to normal open procedure" + open_normally + fi + } + ''} + + ${optionalString (luks.clevisSupport && clevis) '' + + open_with_hardware() { + mkdir -p /crypt-ramfs/clevis + + TMPDIR=/crypt-ramfs/clevis clevis luks unlock -d ${device} -n ${name} + + if [ $? -ne 0 ]; then + echo "Unlocking with clevis failed, falling back to normal open procedure" + open_normally + fi + } + + ''} + + # commands to run right before we mount our device + ${preOpenCommands} + + ${if (luks.yubikeySupport && (yubikey != null)) || (luks.gpgSupport && (gpgCard != null)) || (luks.fido2Support && (fido2.credential != null)) || (luks.clevisSupport && clevis) then '' + open_with_hardware + '' else '' + open_normally + ''} + + # commands to run right after we mounted our device + ${postOpenCommands} + ''; + + askPass = pkgs.writeScriptBin "cryptsetup-askpass" '' + #!/bin/sh + + ${commonFunctions} + + while true; do + wait_target "luks" /crypt-ramfs/device 10 "LUKS to request a passphrase" || die "Passphrase is not requested now" + device=$(cat /crypt-ramfs/device) + + echo -n "Passphrase for $device: " + IFS= read -rs passphrase + echo + + rm /crypt-ramfs/device + echo -n "$passphrase" > /crypt-ramfs/passphrase + done + ''; + + preLVM = filterAttrs (n: v: v.preLVM) luks.devices; + postLVM = filterAttrs (n: v: !v.preLVM) luks.devices; + +in +{ + disabledModules = [ "system/boot/luksroot.nix" ]; + + imports = [ + (mkRemovedOptionModule [ "boot" "initrd" "luks" "enable" ] "") + ]; + + options = { + + boot.initrd.luks.mitigateDMAAttacks = mkOption { + type = types.bool; + default = true; + description = '' + Unless enabled, encryption keys can be easily recovered by an attacker with physical + access to any machine with PCMCIA, ExpressCard, ThunderBolt or FireWire port. + More information is available at . + + This option blacklists FireWire drivers, but doesn't remove them. You can manually + load the drivers if you need to use a FireWire device, but don't forget to unload them! + ''; + }; + + boot.initrd.luks.cryptoModules = mkOption { + type = types.listOf types.str; + default = + [ "aes" "aes_generic" "blowfish" "twofish" + "serpent" "cbc" "xts" "lrw" "sha1" "sha256" "sha512" + "af_alg" "algif_skcipher" + ]; + description = '' + A list of cryptographic kernel modules needed to decrypt the root device(s). + The default includes all common modules. + ''; + }; + + boot.initrd.luks.forceLuksSupportInInitrd = mkOption { + type = types.bool; + default = false; + internal = true; + description = '' + Whether to configure luks support in the initrd, when no luks + devices are configured. + ''; + }; + + boot.initrd.luks.reusePassphrases = mkOption { + type = types.bool; + default = true; + description = '' + When opening a new LUKS device try reusing last successful + passphrase. + + Useful for mounting a number of devices that use the same + passphrase without retyping it several times. + + Such setup can be useful if you use cryptsetup + luksSuspend. Different LUKS devices will still have + different master keys even when using the same passphrase. + ''; + }; + + boot.initrd.luks.devices = mkOption { + default = { }; + example = { luksroot.device = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; }; + description = '' + The encrypted disk that should be opened before the root + filesystem is mounted. Both LVM-over-LUKS and LUKS-over-LVM + setups are supported. The unencrypted devices can be accessed as + /dev/mapper/name. + ''; + + type = with types; attrsOf (submodule ( + { name, ... }: { options = { + + name = mkOption { + visible = false; + default = name; + example = "luksroot"; + type = types.str; + description = "Name of the unencrypted device in /dev/mapper."; + }; + + device = mkOption { + example = "/dev/disk/by-uuid/430e9eff-d852-4f68-aa3b-2fa3599ebe08"; + type = types.str; + description = "Path of the underlying encrypted block device."; + }; + + header = mkOption { + default = null; + example = "/root/header.img"; + type = types.nullOr types.str; + description = '' + The name of the file or block device that + should be used as header for the encrypted device. + ''; + }; + + keyFile = mkOption { + default = null; + example = "/dev/sdb1"; + type = types.nullOr types.str; + description = '' + The name of the file (can be a raw device or a partition) that + should be used as the decryption key for the encrypted device. If + not specified, you will be prompted for a passphrase instead. + ''; + }; + + keyFileSize = mkOption { + default = null; + example = 4096; + type = types.nullOr types.int; + description = '' + The size of the key file. Use this if only the beginning of the + key file should be used as a key (often the case if a raw device + or partition is used as key file). If not specified, the whole + keyFile will be used decryption, instead of just + the first keyFileSize bytes. + ''; + }; + + keyFileOffset = mkOption { + default = null; + example = 4096; + type = types.nullOr types.int; + description = '' + The offset of the key file. Use this in combination with + keyFileSize to use part of a file as key file + (often the case if a raw device or partition is used as a key file). + If not specified, the key begins at the first byte of + keyFile. + ''; + }; + + # FIXME: get rid of this option. + preLVM = mkOption { + default = true; + type = types.bool; + description = "Whether the luksOpen will be attempted before LVM scan or after it."; + }; + + allowDiscards = mkOption { + default = false; + type = types.bool; + description = '' + Whether to allow TRIM requests to the underlying device. This option + has security implications; please read the LUKS documentation before + activating it. + ''; + }; + + fallbackToPassword = mkOption { + default = false; + type = types.bool; + description = '' + Whether to fallback to interactive passphrase prompt if the keyfile + cannot be found. This will prevent unattended boot should the keyfile + go missing. + ''; + }; + + gpgCard = mkOption { + default = null; + description = '' + The option to use this LUKS device with a GPG encrypted luks password by the GPG Smartcard. + If null (the default), GPG-Smartcard will be disabled for this device. + ''; + + type = with types; nullOr (submodule { + options = { + gracePeriod = mkOption { + default = 10; + type = types.int; + description = "Time in seconds to wait for the GPG Smartcard."; + }; + + encryptedPass = mkOption { + default = ""; + type = types.path; + description = "Path to the GPG encrypted passphrase."; + }; + + publicKey = mkOption { + default = ""; + type = types.path; + description = "Path to the Public Key."; + }; + }; + }); + }; + + fido2 = { + credential = mkOption { + default = null; + example = "f1d00200d8dc783f7fb1e10ace8da27f8312d72692abfca2f7e4960a73f48e82e1f7571f6ebfcee9fb434f9886ccc8fcc52a6614d8d2"; + type = types.nullOr types.str; + description = "The FIDO2 credential ID."; + }; + + gracePeriod = mkOption { + default = 10; + type = types.int; + description = "Time in seconds to wait for the FIDO2 key."; + }; + + passwordLess = mkOption { + default = false; + type = types.bool; + description = '' + Defines whatever to use an empty string as a default salt. + + Enable only when your device is PIN protected, such as Trezor. + ''; + }; + }; + + yubikey = mkOption { + default = null; + description = '' + The options to use for this LUKS device in YubiKey-PBA. + If null (the default), YubiKey-PBA will be disabled for this device. + ''; + + type = with types; nullOr (submodule { + options = { + twoFactor = mkOption { + default = true; + type = types.bool; + description = "Whether to use a passphrase and a YubiKey (true), or only a YubiKey (false)."; + }; + + slot = mkOption { + default = 2; + type = types.int; + description = "Which slot on the YubiKey to challenge."; + }; + + saltLength = mkOption { + default = 16; + type = types.int; + description = "Length of the new salt in byte (64 is the effective maximum)."; + }; + + keyLength = mkOption { + default = 64; + type = types.int; + description = "Length of the LUKS slot key derived with PBKDF2 in byte."; + }; + + iterationStep = mkOption { + default = 0; + type = types.int; + description = "How much the iteration count for PBKDF2 is increased at each successful authentication."; + }; + + gracePeriod = mkOption { + default = 10; + type = types.int; + description = "Time in seconds to wait for the YubiKey."; + }; + + /* TODO: Add to the documentation of the current module: + + Options related to the storing the salt. + */ + storage = { + device = mkOption { + default = "/dev/sda1"; + type = types.path; + description = '' + An unencrypted device that will temporarily be mounted in stage-1. + Must contain the current salt to create the challenge for this LUKS device. + ''; + }; + + fsType = mkOption { + default = "vfat"; + type = types.str; + description = "The filesystem of the unencrypted device."; + }; + + path = mkOption { + default = "/crypt-storage/default"; + type = types.str; + description = '' + Absolute path of the salt on the unencrypted device with + that device's root directory as "/". + ''; + }; + }; + }; + }); + }; + + clevis = mkOption { + type = types.bool; + default = false; + description = '' + Unlock device via clevis (e.g. with a tpm) + ''; + }; + + preOpenCommands = mkOption { + type = types.lines; + default = ""; + example = '' + mkdir -p /tmp/persistent + mount -t zfs rpool/safe/persistent /tmp/persistent + ''; + description = '' + Commands that should be run right before we try to mount our LUKS device. + This can be useful, if the keys needed to open the drive is on another partion. + ''; + }; + + postOpenCommands = mkOption { + type = types.lines; + default = ""; + example = '' + umount /tmp/persistent + ''; + description = '' + Commands that should be run right after we have mounted our LUKS device. + ''; + }; + }; + })); + }; + + boot.initrd.luks.gpgSupport = mkOption { + default = false; + type = types.bool; + description = '' + Enables support for authenticating with a GPG encrypted password. + ''; + }; + + boot.initrd.luks.yubikeySupport = mkOption { + default = false; + type = types.bool; + description = '' + Enables support for authenticating with a YubiKey on LUKS devices. + See the NixOS wiki for information on how to properly setup a LUKS device + and a YubiKey to work with this feature. + ''; + }; + + boot.initrd.luks.fido2Support = mkOption { + default = false; + type = types.bool; + description = '' + Enables support for authenticating with FIDO2 devices. + ''; + }; + + boot.initrd.luks.clevisSupport = mkOption { + default = false; + type = types.bool; + description = '' + Enables support for unlocking luks volumes via clevis (e.g. with a tpm) + ''; + }; + + }; + + config = mkIf (luks.devices != {} || luks.forceLuksSupportInInitrd) { + + assertions = + [ { assertion = !(luks.gpgSupport && luks.yubikeySupport); + message = "YubiKey and GPG Card may not be used at the same time."; + } + + { assertion = !(luks.gpgSupport && luks.fido2Support); + message = "FIDO2 and GPG Card may not be used at the same time."; + } + + { assertion = !(luks.gpgSupport && luks.clevisSupport); + message = "Clevis and GPG Card may not be used at the same time."; + } + + { assertion = !(luks.fido2Support && luks.yubikeySupport); + message = "FIDO2 and YubiKey may not be used at the same time."; + } + + { assertion = !(luks.fido2Support && luks.clevisSupport); + message = "FIDO2 and Clevis may not be used at the same time."; + } + + { assertion = !(luks.yubikeySupport && luks.clevisSupport); + message = "Clevis and YubiKey may not be used at the same time."; + } + + ]; + + # actually, sbp2 driver is the one enabling the DMA attack, but this needs to be tested + boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks + ["firewire_ohci" "firewire_core" "firewire_sbp2"]; + + # Some modules that may be needed for mounting anything ciphered + boot.initrd.availableKernelModules = [ "dm_mod" "dm_crypt" "cryptd" "input_leds" ] + ++ luks.cryptoModules + # workaround until https://marc.info/?l=linux-crypto-vger&m=148783562211457&w=4 is merged + # remove once 'modprobe --show-depends xts' shows ecb as a dependency + ++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else []); + + # copy the cryptsetup binary and it's dependencies + boot.initrd.extraUtilsCommands = + let + extraUtils = config.system.build.extraUtils; + + ipkgs = pkgs.appendOverlays [ + (final: prev: { + tpm2-tss = prev.tpm2-tss.overrideAttrs (oldAttrs: { + doCheck = false; + patches = []; + postPatch = '' + patchShebangs script + ''; + configureFlags = []; + }); + }) + ]; + in '' + copy_bin_and_libs ${pkgs.cryptsetup}/bin/cryptsetup + copy_bin_and_libs ${askPass}/bin/cryptsetup-askpass + sed -i s,/bin/sh,$out/bin/sh, $out/bin/cryptsetup-askpass + + ${optionalString luks.yubikeySupport '' + copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykchalresp + copy_bin_and_libs ${pkgs.yubikey-personalization}/bin/ykinfo + copy_bin_and_libs ${pkgs.openssl.bin}/bin/openssl + + cc -O3 -I${pkgs.openssl.dev}/include -L${pkgs.openssl.out}/lib ${./pbkdf2-sha512.c} -o pbkdf2-sha512 -lcrypto + strip -s pbkdf2-sha512 + copy_bin_and_libs pbkdf2-sha512 + + mkdir -p $out/etc/ssl + cp -pdv ${pkgs.openssl.out}/etc/ssl/openssl.cnf $out/etc/ssl + + cat > $out/bin/openssl-wrap <