Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 88 additions & 19 deletions nixos/modules/system/boot/luksroot.nix
Original file line number Diff line number Diff line change
Expand Up @@ -439,10 +439,30 @@ let
}
''}

${optionalString (luks.tpm2Support && (dev.tpm2KeyFile != null)) ''
open_with_hardware() {
export TPM2TOOLS_TCTI="device:/dev/tpm0"

tpm2_unseal -c ${dev.tpm2KeyFile.persistentObject} -p ${dev.tpm2KeyFile.authString} > /crypt-ramfs/tpmKeyfile
if [ $? -ne 0 ]; then
echo "TPM keyfile could not be unsealed, falling back to normal open procedure"
open_normally
return
fi

${csopen} --key-file=/crypt-ramfs/tpmKeyfile
if [ $? -ne 0 ]; then
echo "Cannot unlock with TPM keyfile, falling back to normal open procedure"
open_normally
return
fi
}
''}

# commands to run right before we mount our device
${dev.preOpenCommands}

${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && (dev.fido2.credential != null)) then ''
${if (luks.yubikeySupport && (dev.yubikey != null)) || (luks.gpgSupport && (dev.gpgCard != null)) || (luks.fido2Support && (dev.fido2.credential != null)) || (luks.tpm2Support && (dev.tpm2KeyFile != null)) then ''
open_with_hardware
'' else ''
open_normally
Expand Down Expand Up @@ -647,6 +667,31 @@ in
'';
};

tpm2KeyFile = mkOption {
description = ''
Use a TPM-sealed object as a keyfile.
Specify the keyfile object with either <literal>tpm2KeyFile.persistentObject</literal> or <literal>tpm2KeyFile.transientObject</literal>
'';
default = null;
type = types.nullOr (types.submodule { options = {
authString = mkOption {
description = ''
The object's authorization value as defined in <literal>tpm2_unseal (1)</literal>.
For PCR-sealed objects, it would be <literal>pcr:[hash algorithm]:[register numbers, comma-separated]</literal>.
'';
type = types.str;
example = "pcr:sha256:0,1,2,3,4,5,6,7";
};
persistentObject = mkOption {
description = ''
The handle as a string of the keyfile object stored in NVRAM.
'';
example = "0x81000000";
type = types.str;
};
};});
};

gpgCard = mkOption {
default = null;
description = ''
Expand Down Expand Up @@ -833,28 +878,28 @@ in
'';
};

boot.initrd.luks.tpm2Support = mkOption {
default = false;
type = types.bool;
description = ''
Enables support for authenticating with TPM-sealed keys.
'';
};

};

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.";
}
assertions = [
{ assertion = builtins.length (builtins.filter (a: a) [ luks.gpgSupport luks.yubikeySupport luks.fido2Support luks.tpm2Support ]) <= 1;
message = "Only one hardware unlocking method (GPG, Yubikey, FIDO2, TPM keyfile) can be used at once.";
}

{ assertion = !(luks.gpgSupport && luks.fido2Support);
message = "FIDO2 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 = 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";
}
];
{ 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
boot.blacklistedKernelModules = optionals luks.mitigateDMAAttacks
Expand All @@ -865,7 +910,8 @@ in
++ 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 []);
++ (if builtins.elem "xts" luks.cryptoModules then ["ecb"] else [])
++ (lib.optionals luks.tpm2Support ["rng-core" "tpm" "tpm-tis-core" "tpm-tis"]);

# copy the cryptsetup binary and it's dependencies
boot.initrd.extraUtilsCommands = ''
Expand Down Expand Up @@ -914,6 +960,24 @@ in
) (attrValues luks.devices)
}
''}

${optionalString luks.tpm2Support (
# tpm2-tools uses the busybox technique of multiple commands symlinked to a single executable.
# But the symlinks in this package point to an intermediary wrapper, bin/tpm2, which calls bash.
# So we should manually build the symlinks.

# tpm2-tcti patches in hardcoded nix store paths for the tcti drivers '.so's,
# which doesn't work in the initrd structure. We need to disable that.
let
tpm2-tools' = pkgs.tpm2-tools.override { tpm2-tss = pkgs.tpm2-tss.override { loader-path-patch = false; }; };
in ''
copy_bin_and_libs ${tpm2-tools'}/bin/.tpm2-wrapped
ln -s $out/bin/.tpm2-wrapped $out/bin/tpm2_createprimary
ln -s $out/bin/.tpm2-wrapped $out/bin/tpm2_load
ln -s $out/bin/.tpm2-wrapped $out/bin/tpm2_unseal
cp -pv ${pkgs.tpm2-tss}/lib/libtss2-tcti-device.so $out/lib/libtss2-tcti-device.so
''
)}
'';

boot.initrd.extraUtilsCommandsTest = ''
Expand All @@ -931,6 +995,11 @@ in
${optionalString luks.fido2Support ''
$out/bin/fido2luks --version
''}
${optionalString luks.tpm2Support ''
$out/bin/tpm2_createprimary --version
$out/bin/tpm2_load --version
$out/bin/tpm2_unseal --version
''}
'';

boot.initrd.preFailCommands = postCommands;
Expand Down
4 changes: 3 additions & 1 deletion pkgs/development/libraries/tpm2-tss/default.nix
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
, autoreconfHook, autoconf-archive, pkg-config, doxygen, perl
, openssl, json_c, curl, libgcrypt
, cmocka, uthash, ibm-sw-tpm2, iproute2, procps, which
, loader-path-patch ? true
}:

stdenv.mkDerivation rec {
Expand All @@ -27,14 +28,15 @@ stdenv.mkDerivation rec {

enableParallelBuilding = true;

patches = [
patches = lib.optional loader-path-patch [
# Do not rely on dynamic loader path
# TCTI loader relies on dlopen(), this patch prefixes all calls with the output directory
./no-dynamic-loader-path.patch
];

postPatch = ''
patchShebangs script
'' + lib.optionalString loader-path-patch ''
substituteInPlace src/tss2-tcti/tctildr-dl.c \
--replace '@PREFIX@' $out/lib/
substituteInPlace ./test/unit/tctildr-dl.c \
Expand Down