summaryrefslogtreecommitdiff
path: root/sys/boot/detached-luks.nix
blob: d3e7c29c5dacd7c976b0d3215de02ab030acbd3e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
{
  config,
  lib,
  pkgs,
  ...
}:
with lib; let
  cfg = config.local.boot.detachedLuks;

  bootFs = config.fileSystems."/boot";
  tpmInitrd = config.local.boot.tpm.initrd.enable;

  pcrList = concatStringsSep "," (map toString config.local.boot.tpm.initrd.pcrs);
in {
  options.local.boot.detachedLuks = {
    enable = mkEnableOption "detached LUKS header in initrd";

    headerFromBoot = mkOption {
      type = types.str;
    };

    tpmStorageFromBoot = mkOption {
      type = types.str;
      default = "tpm-boot";
    };

    crypt = mkOption {
      type = types.str;
    };

    target = mkOption {
      type = types.str;
    };
  };

  config = mkIf cfg.enable {
    boot.initrd = let
      headerPath = "/initrd-boot/${cfg.headerFromBoot}";
      headerPathEscaped = escapeShellArg headerPath;

      tpmPath = escapeShellArg "/initrd-boot/${cfg.tpmStorageFromBoot}";
      hardwareKeyPath = "/tpm/unsealed.luks-key";
    in {
      preDeviceCommands = ''
        mkdir -p `dirname ${headerPathEscaped}`
        touch ${headerPathEscaped}
      '';

      postDeviceCommands = mkIf (!config.boot.initrd.systemd.enable) ''
        # Set the system time from the hardware clock to work around a
        # bug in qemu-kvm > 1.5.2 (where the VM clock is initialised
        # to the *boot time* of the host).
        hwclock -s
      '';

      #FIXME: Demasiado vulgar
      preLVMCommands = optionalString (config.local.boot.efi.enable && config.local.boot.efi.removable) ''
        sleep 2
      '';

      luks.devices.${cfg.target} = {
        device = cfg.crypt;
        header = headerPath;
        preLVM = false;

        keyFile = mkIf tpmInitrd hardwareKeyPath;
        fallbackToPassword = tpmInitrd;

        preOpenCommands =
          ''
            mount -o ro -t ${bootFs.fsType} ${bootFs.device} /initrd-boot
          ''
          + optionalString tpmInitrd ''
            mkdir /tpm
            touch ${escapeShellArg hardwareKeyPath}

            unseal_tpm_key() {
              [ -e ${tpmPath}/auth.sig ] || return
              tpm2 createprimary -Q -C owner -g sha256 -G ecc -c /tpm/prim.ctx || return

              tpm2 loadexternal -Q -C owner -G rsa -u ${tpmPath}/signing-key.pub -c /tpm/signing-key.ctx -n /tpm/signing-key.name || return
              tpm2 verifysignature -Q -c /tpm/signing-key.ctx -g sha256 -m ${tpmPath}/auth.policy -s ${tpmPath}/auth.sig -t /tpm/verified.ticket -f rsassa || return

              tpm2 startauthsession -Q -S /tpm/session.ctx --policy-session || return

              tpm_resets=`tpm2 readclock | grep reset_count | sed 's/.*: //g'`
              tpm2 policycountertimer -Q -S /tpm/session.ctx resets="$tpm_resets" || return
              tpm2 policypcr -Q -S /tpm/session.ctx -l sha256:${pcrList} || return
              tpm2 policyauthorize -Q -S /tpm/session.ctx -i ${tpmPath}/auth.policy -n /tpm/signing-key.name -t /tpm/verified.ticket || return

              tpm2 load -Q -C /tpm/prim.ctx -u ${tpmPath}/key.pub -r ${tpmPath}/key.priv -c /tpm/key.ctx || return
              tpm2 unseal -Q -c /tpm/key.ctx -p session:/tpm/session.ctx -o ${escapeShellArg hardwareKeyPath} || return

              tpm2 flushcontext /tpm/session.ctx
            }

            unseal_tpm_key
          '';

        postOpenCommands = mkBefore (''
            umount /initrd-boot
          ''
          + optionalString tpmInitrd ''
            rm -r /tpm
          '');
      };
    };

    local.boot = {
      stack = {
        btrfsToplevelMultidrive.toplevel.device = "/dev/mapper/${cfg.target}";
        luksExt4FscryptImpermanence = {inherit (cfg) target;};
      };

      tpm.initrd.enable = mkDefault config.local.boot.tpm.enable;
    };

    systemd.services = {
      clear-tpm2-boot-auth = let
        inherit (config.local.boot.efi.esp) mountpoint;
        mountUnit = concatStringsSep "-" (splitString "/" (removePrefix "/" mountpoint)) + ".mount";
        tpmBootPath = "${mountpoint}/${cfg.tpmStorageFromBoot}";
      in {
        after = ["tpm2.target" mountUnit];
        wantedBy = ["tpm2.target"];

        serviceConfig = {
          Type = "oneshot";
        };

        script = ''
          for file in auth.policy auth.sig; do
              path="${tpmBootPath}/$file"
              [ -f "$path" ] && shred -fu -- "$path"
          done

          sync -f "${mountpoint}"
        '';
      };
    };
  };
}