<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>TPM on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/tpm/</link><image><url>https://giggio.net/images/base/logo-small.png</url><title>TPM on Giovanni Bassi</title><link>https://giggio.net/en/blog/tags/tpm/</link></image><description>TPM no site do Giovanni Bassi</description><generator>Hugo</generator><language>en</language><managingEditor>giggio@giggio.net (Giovanni Bassi)</managingEditor><webMaster>giggio@giggio.net (Giovanni Bassi)</webMaster><copyright>© 2025 Giovanni Bassi</copyright><lastBuildDate>Mon, 01 Jun 2026 11:00:00 -0300</lastBuildDate><atom:link href="https://giggio.net/en/blog/tags/tpm/index.xml" rel="self" type="application/rss+xml"/><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM unlock (part 7 - Mitigating the OS swap attack)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-7-mitigando-o-ataque-de-troca-de-so/</link><pubDate>Mon, 01 Jun 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-7-mitigando-o-ataque-de-troca-de-so/</guid><category>infra</category><description>&lt;p&gt;There is still a loophole where one could obtain the LUKS volume encryption key using another operating system. Let&amp;rsquo;s
fix that.&lt;/p&gt;
&lt;p&gt;This is the seventh post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/"&gt;Unlocking the disk with TPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/"&gt;Mitigating the volume swap attack&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mitigating the OS-switch attack (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;If you are only using your own keys in Secure Boot, this post doesn&amp;rsquo;t apply to your scenario. If you are also using
Microsoft&amp;rsquo;s keys, you need to implement this mitigation.&lt;/p&gt;
&lt;h3 id="the-problem"&gt;
 &lt;a href="#the-problem" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;The Problem&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;So far, we have been linking disk decryption to PCR 7 (and PCR 15 for system loading, as described in the last post). As
seen in previous posts, PCR 7 depends exclusively on the Secure Boot state. This means that, regardless of which
operating system is loaded, PCR 7 will have the same values.&lt;/p&gt;
&lt;p&gt;If you also trust third-party keys (such as Microsoft&amp;rsquo;s or another manufacturer&amp;rsquo;s, like Canonical&amp;rsquo;s) in Secure Boot,
there is still an attack surface that allows booting another system signed by those authorities. Therefore, it&amp;rsquo;s
necessary to bind the protection to something beyond just PCR 7.&lt;/p&gt;
&lt;p&gt;With the operating system loaded and PCR 7 in the correct configuration, it&amp;rsquo;s possible to obtain the LUKS volume header
data and decrypt it using the TPM to get the LUKS master key, and then decrypt the volume. This could even be done using
a Live CD with a correctly signed boot loader.&lt;/p&gt;
&lt;h3 id="naively-sealing-the-luks-key-with-the-nixos-boot-loader"&gt;
 &lt;a href="#naively-sealing-the-luks-key-with-the-nixos-boot-loader" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Naively sealing the LUKS key with the NixOS boot loader&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Sealing the volume key only with PCR 7 is risky, so let&amp;rsquo;s also use another PCR that is closely tied to our operating
system. PCR 4 measures the boot loader and additional drivers — the boot loader/PE binary code executed in the boot path.
This means only an operating system that loads using the NixOS &lt;code&gt;.efi&lt;/code&gt; files we are using would be able to retrieve the
LUKS key via the TPM.&lt;/p&gt;
&lt;p&gt;Since these &lt;code&gt;.efi&lt;/code&gt; files are generated on-demand for our specific NixOS configuration and signed with Lanzaboote using
our private keys, this prevents another OS from decrypting the LUKS key. For instance, if you load Ubuntu, the boot
loader will be Ubuntu&amp;rsquo;s, with different &lt;code&gt;.efi&lt;/code&gt; files, and therefore the content extended into PCR 4 will also be
different. Because PCR 4 measures the EFI/PE binary actually executed at boot, a different boot path produces a
different measurement and cannot reuse the same TPM token.&lt;/p&gt;
&lt;p&gt;This would be simple if the boot loader didn&amp;rsquo;t change every time the Kernel, initrd, or any boot configuration is
updated, which ends up changing PCR 4. If that weren&amp;rsquo;t the case, we could simply run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token from disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# enroll new token using PCR 4 and PCR 7 on disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;4+7 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# do the same for disk 2...&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note:&lt;/strong&gt; The commands above can be combined into one, which will wipe and enroll in a single operation.&lt;/p&gt;
&lt;p&gt;This will only work until the next time the boot loader files (the &lt;code&gt;.efi&lt;/code&gt; files) are changed. On the first boot after
such a change, the volume will no longer decrypt automatically, it will ask for the password, and you would need to run
the commands above again. If that&amp;rsquo;s acceptable to you, you don&amp;rsquo;t even need to read the rest of the post, but I can tell
you there is a better and equally secure way to solve this.&lt;/p&gt;
&lt;h3 id="sealing-the-luks-key-with-the-nixos-boot-loader-using-a-pcr-policy"&gt;
 &lt;a href="#sealing-the-luks-key-with-the-nixos-boot-loader-using-a-pcr-policy" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Sealing the LUKS key with the NixOS boot loader using a PCR policy&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;&lt;strong&gt;Important&lt;/strong&gt;: Before starting, verify that your virtual machine is configured to use only one of the disks as a boot
disk. I noticed that if both disks are marked as boot disks, PCR 4 is not correctly evaluated and the policy is not
created. In the virtual machine settings, you can see this in the &amp;ldquo;Boot Options&amp;rdquo; menu. This might be a quirk of Virtual
Machine Manager (and &lt;code&gt;virsh&lt;/code&gt;), the Firmware used, or something related to Secure Boot and TPM protocols. The problem
doesn&amp;rsquo;t occur on my physical machine, only on the virtual one.&lt;/p&gt;
&lt;p&gt;The secret to solving this problem is a system that can predict the values that will be in the PCR and create an
appropriate policy, and that is exactly what
&lt;a href="https://www.freedesktop.org/software/systemd/man/latest/systemd-pcrlock.html"&gt;systemd-pcrlock&lt;/a&gt; does. While the process
can be done manually, Lanzaboote recently implemented
&lt;a href="https://github.com/nix-community/lanzaboote/blob/master/docs/how-to-guides/enable-measured-boot.md"&gt;TPM measurement&lt;/a&gt;
alongside Secure Boot integration, using systemd-pcrlock. At the time of writing, this hasn&amp;rsquo;t made it into a release
yet, meaning it might contain bugs. By the time you read this, the functionality may have stabilized.&lt;/p&gt;
&lt;p&gt;To enable it, simply add this to your configuration:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;boot&lt;/span&gt;&lt;span class="o"&gt;.&lt;/span&gt;&lt;span class="n"&gt;lanzaboote&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pkiBundle&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;/var/lib/sbctl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;configurationLimit&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;8&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;measuredBoot&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pcrs&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt; &lt;span class="mi"&gt;4&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt; &lt;span class="p"&gt;];&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Finally, you need to perform a &lt;code&gt;nixos-rebuild&lt;/code&gt;, but use &lt;code&gt;boot&lt;/code&gt; instead of &lt;code&gt;switch&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild boot
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This will prepare the configuration, but it will only be used on the next boot. This is important to ensure that the
boot loader files are correctly signed, in addition to preparing the PCR 7 files.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: &lt;code&gt;systemd-pcrlock&lt;/code&gt; has several subcommands that allow you to &amp;ldquo;lock&amp;rdquo; a specific PCR. &amp;ldquo;Locking,&amp;rdquo; as used here,
means inspecting the events of each PCR and generating &lt;code&gt;.pcrlock&lt;/code&gt; files in the &lt;code&gt;/var/lib/pcrlock.d&lt;/code&gt; directory. For
example, &lt;code&gt;systemd-pcrlock lock-firmware-code&lt;/code&gt; generates the file
&lt;code&gt;/var/lib/pcrlock.d/250-firmware-code-early.pcrlock.d/generated.pcrlock&lt;/code&gt; with PCR 0 and 2 measurements. Lanzaboote uses
this system when you enable PCR 7 by running &lt;code&gt;systemd-pcrlock lock-secureboot-authority&lt;/code&gt; and &lt;code&gt;systemd-pcrlock lock-secureboot-policy&lt;/code&gt;. This is done by creating two oneshot systemd services: &lt;code&gt;systemd-pcrlock-secureboot-authority&lt;/code&gt;
and &lt;code&gt;systemd-pcrlock-secureboot-policy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Reboot the system. The LUKS key is still tied only to PCR 7, and the disks will be decrypted automatically. After the
reboot, the policy file will have been created. You can verify if both PCRs are present in the file by running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;.pcrValues.[].pcr&amp;#39;&lt;/span&gt; /var/lib/systemd/pcrlock.json
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;4
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;7
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;If this is correct, you can now remove the token from the disk headers that was tied only to PCR 7 and create a new one
using the policy via the generated file:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token and enroll new token using the policy on disk 1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove previous token and enroll new token using the policy on disk 2&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;With this, you can reboot again. The disk should continue to unlock automatically, but this time it is using the policy
file, and therefore PCRs 4 and 7.&lt;/p&gt;
&lt;p&gt;The functionality is now complete. Whenever the boot loader files are updated, the policy will also be updated, and the
disk will be decrypted automatically without requiring any manual interaction. In the following sections, I will dive
deeper into what is happening.&lt;/p&gt;
&lt;h3 id="inspecting-tpm-events"&gt;
 &lt;a href="#inspecting-tpm-events" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Inspecting TPM events&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;You can check TPM events by running &lt;code&gt;sudo systemd-pcrlock log&lt;/code&gt;, or just &lt;code&gt;sudo systemd-pcrlock&lt;/code&gt;. Unfortunately, the
command doesn&amp;rsquo;t allow showing events for a specific PCR, so you can do it using JSON and &lt;code&gt;jq&lt;/code&gt;. To see only PCR 4 events,
run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 4)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see the components registered in &lt;code&gt;systemd-pcrlock&lt;/code&gt;, use the &lt;code&gt;list-components&lt;/code&gt; command:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemd-pcrlock list-components
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;ID VARIANTS
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;240-secureboot-policy /var/lib/pcrlock.d/240-secureboot-policy.pcrlock.d/generated.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;350-action-efi-application /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/350-action-efi-application.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;400-secureboot-separator /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/400-secureboot-separator.pcrlock.d/300-0x00000000.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;500-separator /nix/store/f4s218swc4kw35apcqg9al8d7s287x7y-pcrlock.d/500-separator.pcrlock.d/300-0x00000000.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;620-secureboot-authority /var/lib/pcrlock.d/620-secureboot-authority.pcrlock.d/generated.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;630-bootloader /var/lib/pcrlock.d/630-bootloader.pcrlock.d/current.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;635-lanzaboote /var/lib/pcrlock.d/635-lanzaboote.pcrlock.d/7.pcrlock
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;These components are used to predict the values of each PCR. They are what &amp;ldquo;lock&amp;rdquo; the TPM. The idea is that
&lt;code&gt;systemd-pcrlock lock-*&lt;/code&gt; commands check TPM events and generate these components, along with Lanzaboote generating the
PCR 4 (boot loader) ones at every &lt;code&gt;nixos-rebuild&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Finally, you can check the measurements being used by running &lt;code&gt;systemd-pcrlock predict&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; sudo systemd-pcrlock predict --pcr&lt;span class="o"&gt;=&lt;/span&gt;4,7
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 0 (PCR 0, &amp;#34;Raw: 2\0008\000\000\000&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 9 (PCR 1, &amp;#34;Raw: ACPI DATA&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 13 (PCR 2, &amp;#34;Raw: \030\377\004\000&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 27 (PCR 5, &amp;#34;GPT: disk 8118a150-5781-4514-94f0-517c327f4ca7&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 40 (PCR 9, &amp;#34;Linux: kernel command line&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 31 (PCR 11, &amp;#34;String: .linux&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 39 (PCR 12, &amp;#34;String: Global credentials initrd&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Event log record 44 (PCR 15, &amp;#34;cryptsetup:crypt_disk1:5a2876f5-9220-4040-8b56-7bc226c9d649&amp;#34;) not matching any component.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;3 combinations of components.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCR 4 (boot-loader-code) matches event log and fully consists of recognized measurements. Including in set of PCRs.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCR 7 (secure-boot-policy) matches event log and fully consists of recognized measurements. Including in set of PCRs.
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;PCRs in protection mask: 4 (boot-loader-code), 7 (secure-boot-policy)
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Results for PCR 4 (boot-loader-code):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 19d6c3ffa28c30062e84d8e90a357a6319ce921dfa15ee6618a5f9c38a5e9890
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 58d68b2aa60262d9831fdd1b8b0f50fd30610162bf3ae7f19d4722e21ba24277
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: 480319eadf7ad52de18d44758204505cf34a4def787c762e1da3024a69b2ec8e
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;Results for PCR 7 (secure-boot-policy):
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; sha256: cc1dc9cbd11e3ad0a695744ab152362d388e2e97f165117e3e45b8248f4a5078
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The initial lines show event records that were not recognized. Since &lt;code&gt;predict&lt;/code&gt; only generates predictions for PCRs whose
component coverage is complete, those PCRs are excluded from this specific policy. If we wanted to use them, some could
have components created by running &lt;code&gt;systemd-pcrlock lock-*&lt;/code&gt; commands, while others would need manual component creation.&lt;/p&gt;
&lt;p&gt;Lastly, let&amp;rsquo;s compare the PCR 7 logs used in the
&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/#understanding-the-tpm"&gt;fifth post&lt;/a&gt;.
The only difference is the inclusion of the component field (which &amp;ldquo;locks&amp;rdquo; the PCR), highlighted below:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 7)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-driver-config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;9f75b6823bff6af1024a4e2036719cdd548d3cbc2bf1de8e7ef4d0ed01f94bf9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;240-secureboot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Variable: dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;f35567246a92b2fcdd2901e4ad2febdfd80173f25527c5a73033bf100a8eb980&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;620-secureboot-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Authority: db-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="evaluating-the-luks-header-token"&gt;
 &lt;a href="#evaluating-the-luks-header-token" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Evaluating the LUKS header token&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;We can also compare the LUKS header, which was updated when we ran &lt;code&gt;systemd-cryptenroll&lt;/code&gt; with the &lt;code&gt;--wipe-slot&lt;/code&gt; argument
and then &lt;code&gt;--tpm2-pcrlock&lt;/code&gt;. In the
&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/#unlocking-the-disk-during-boot"&gt;fifth post&lt;/a&gt;,
the policy wasn&amp;rsquo;t used, so the &lt;code&gt;tpm-pcrs&lt;/code&gt; field (a list) had the value &lt;code&gt;7&lt;/code&gt;, and now it&amp;rsquo;s empty. The &lt;code&gt;tpm2-pcr-bank&lt;/code&gt;
field is no longer used, and the &lt;code&gt;tpm2_pcrlock_nv&lt;/code&gt; field now has a hash:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump --dump-json-metadata /dev/disk/by-label/NIXLUKS1 &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.tokens.&amp;#34;0&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;systemd-tpm2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keyslots&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-blob&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcrs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-policy-hash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a hash&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_pcrlock&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_srk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;another base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line hl"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_pcrlock_nv&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;yet another base64 value&amp;gt;&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;This last field, ending in &lt;code&gt;_nv&lt;/code&gt;, is especially important. It points to a non-volatile area of the TPM, which is not
erased when the computer turns off. The pcrlock policy data is stored there, and if the TPM is cleared, it forces the
recreation of the policy (more on this below).&lt;/p&gt;
&lt;h3 id="removing-the-tpm-policy"&gt;
 &lt;a href="#removing-the-tpm-policy" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Removing the TPM policy&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If you ever remove the NixOS TPM measurement settings (&lt;code&gt;boot.lanzaboote.measuredBoot&lt;/code&gt;), you will need to remove the
policy manually. It is registered in the aforementioned policy file, but also in the TPM&amp;rsquo;s non-volatile memory. This
should be automated by Lanzaboote (I opened &lt;a href="https://github.com/nix-community/lanzaboote/issues/600"&gt;an issue&lt;/a&gt;), but
from what I&amp;rsquo;ve seen, it isn&amp;rsquo;t yet. After running &lt;code&gt;nixos-rebuild switch&lt;/code&gt;, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# remove the policy from the TPM&amp;#39;s non-volatile memory and also the files:&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /var/lib/systemd/pcrlock.json and&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# /boot/loader/credentials/pcrlock.nixos.cred&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock remove-policy
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Don&amp;rsquo;t forget to remove the TPM keyslot from the LUKS volume headers:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;h3 id="dealing-with-a-clearedwiped-tpm"&gt;
 &lt;a href="#dealing-with-a-clearedwiped-tpm" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Dealing with a cleared/wiped TPM&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If the TPM&amp;rsquo;s non-volatile memory is wiped, the reference to the TPM policy registered in the LUKS volume header will
become outdated, pointing to a record that no longer exists. This will force us to re-enroll the policy into the LUKS
header. You can test this safely by clearing the TPM directly from Linux. Run the following commands and reboot:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nix-shell -p tpm2-tools
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo tpm2 clear
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nb"&gt;exit&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As a result, the operation to decrypt the disk using the TPM during boot will fail, and you will need to decrypt using
your password. To re-enable automatic TPM decryption, you first need to remove the policy from the disk (the
&lt;code&gt;/var/lib/systemd/pcrlock.json&lt;/code&gt; and &lt;code&gt;/boot/loader/credentials/pcrlock.nixos.cred&lt;/code&gt; files—explained in the previous
section), run &lt;code&gt;nixos-rebuild boot&lt;/code&gt;, and reboot to recreate the wiped policy. Then, redo the LUKS header with the
&lt;code&gt;systemd-cryptenroll --wipe-slot&lt;/code&gt; and &lt;code&gt;systemd-cryptenroll --tpm2-pcrlock&lt;/code&gt; commands:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-pcrlock remove-policy
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --wipe-slot&lt;span class="o"&gt;=&lt;/span&gt;tpm2 /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo nixos-rebuild boot
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# reboot, enter LUKS password&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;jq &lt;span class="s1"&gt;&amp;#39;.pcrValues.[].pcr&amp;#39;&lt;/span&gt; /var/lib/systemd/pcrlock.json &lt;span class="c1"&gt;# validate that the result is: 4 7&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrlock&lt;span class="o"&gt;=&lt;/span&gt;/var/lib/systemd/pcrlock.json /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Reboot once more, and you should no longer be prompted for a password.&lt;/p&gt;
&lt;p&gt;You&amp;rsquo;ve already seen these commands in this article, so I won&amp;rsquo;t detail them further.&lt;/p&gt;
&lt;h3 id="conclusion"&gt;
 &lt;a href="#conclusion" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Conclusion&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;With this, your operating system is now protected, and you have the convenience of a system that starts without
constantly asking for a decryption password. Better yet, since everything was done using NixOS, the configuration is
ready to be reused: if it worked once, it will work forever.&lt;/p&gt;
&lt;p&gt;The series was supposed to end here, but since I wrote this post, a few days have passed and I&amp;rsquo;ve used everything I
wrote here to migrate my own machine from Ubuntu to NixOS. I learned a lot and have a few more things to share, so the
series will have at least one more post.&lt;/p&gt;
&lt;p&gt;If you&amp;rsquo;ve followed along this far, ran a proof of concept with the examples, or have ideas or criticisms, leave a
comment so I know!&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 6 - Mitigating the volume swap attack)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/</link><pubDate>Tue, 26 May 2026 10:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-6-mitigando-o-ataque-de-troca-de-volume/</guid><category>infra</category><description>&lt;p&gt;The NixOS disk is encrypted, but a careful LUKS volume swap attack can still be used to obtain the encryption master key.&lt;/p&gt;
&lt;p&gt;In this post, I show how to mitigate it!&lt;/p&gt;
&lt;p&gt;This is the sixth post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/"&gt;Unlocking the disk with TPM&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Mitigating the volume swap attack (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;As I said in the previous post, this kind of attack would not be carried out by an average person. The computer shop
technician who is repairing your laptop probably will not be able to read your LUKS encrypted disk and with the key
sealed with the help of PCR 7. But someone who knows what they are doing will find the weakness. This post and the next
one are for those who want to be fully protected, both from the technician at the shop around the corner and from a
hostile government or corporate agent. By the end of these two posts, the attacks described here will be mitigated,
significantly increasing security (keeping in mind that it is always prudent to consider other attack surfaces outside
these scenarios).&lt;/p&gt;
&lt;h3 id="the-problem"&gt;
 &lt;a href="#the-problem" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;The problem&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The attack
&lt;a href="https://oddlama.org/blog/bypassing-disk-encryption-with-tpm2-unlock/"&gt;was described by Oddlama&lt;/a&gt;, and it basically
consists of removing the disk from the original computer, creating a fake encrypted partition with the same
identification data as the original partition, and then using that to obtain the key. He even describes how to do this
against NixOS.&lt;/p&gt;
&lt;p&gt;This is possible because the UKI (Unified Kernel Image — the binary loaded by UEFI that contains the kernel and the
initrd) is not encrypted — if it were, UEFI would not be able to load it. The LUKS volume header is also not encrypted,
since it must be read by the system in initrd so that the LUKS volume can be decrypted. It is possible to inspect what
will be loaded during boot and replace the LUKS volume with another one carefully prepared to steal the master key from
the original LUKS volume.&lt;/p&gt;
&lt;p&gt;I will not go into more detail because the explanation is long. You can read Oddlama’s post; it is quite interesting.
The important point is that this attack is only possible because the UKI does not validate the identity of the LUKS
volume, which allows it to be replaced.&lt;/p&gt;
&lt;h3 id="closing-the-breach"&gt;
 &lt;a href="#closing-the-breach" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Closing the breach&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;To solve the problem, we just need to start validating the decrypted volume before proceeding, something that is not
being done yet and will be addressed using PCR 15 (&lt;code&gt;system-identity&lt;/code&gt;). In the previous post, I showed how to list the
hashes of all PCRs with &lt;code&gt;systemd-analyze pcrs&lt;/code&gt;. Notice that PCR 15 was zeroed out, meaning it had not been extended.
systemd can start writing to it by simply enabling &lt;code&gt;tpm2-measure-pcr=yes&lt;/code&gt; for &lt;code&gt;systemd-cryptsetup&lt;/code&gt; during the boot
process.&lt;/p&gt;
&lt;p&gt;To start writing to PCR 15 in the NixOS way, we need a Nix configuration. Fortunately, Oddlama himself provided the
solution, pointing to
&lt;a href="https://forge.lal.lol/patrick/nix-config/src/branch/master/modules/ensure-pcr.nix"&gt;a Forgejo repository by PatrickDaG&lt;/a&gt;,
with the file &lt;code&gt;ensure-pcr.nix&lt;/code&gt;, which
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/ensure-pcr.nix"&gt;has already been adapted in the repository&lt;/a&gt;
cloned into &lt;code&gt;/etc/nixos&lt;/code&gt;, and which makes it possible to read this PCR through a NixOS module.&lt;/p&gt;
&lt;p&gt;I incorporated this module into my solution, which made it very easy to apply. The example commit is
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/commit/f690b8882df0993f05c3ad849fe42ebc7676e8d1"&gt;f690b88&lt;/a&gt;,
described as &amp;ldquo;Enable PCR15&amp;rdquo;, but we do not need to check it out; it is enough to update the configuration in
&lt;code&gt;/etc/nixos/configuration.nix&lt;/code&gt; (it is already in the code, just remove the comment):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;systemIdentity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Then apply it:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;nixos-rebuild switch
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Restart the virtual machine, and after the reboot, confirm that the value of PCR 15 is present (the value below is just
an example — replaced by a sequence from 0 to 9):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemd-analyze pcrs &lt;span class="m"&gt;15&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;NR NAME SHA256
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;15 system-identity 0123456789012345678901234567890123456789012345678901234567890123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;That is the hash of the system identity (derived from the activated LUKS volume key); it is what will ensure that the
unlocked volume is the expected one, reducing the volume swap attack.&lt;/p&gt;
&lt;p&gt;The value found must be copied into the &lt;code&gt;pcr15&lt;/code&gt; field in the configuration. For example, using the fictitious sequence
above from 0 to 9:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-nix" data-lang="nix"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="n"&gt;systemIdentity&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;enable&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="no"&gt;true&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="n"&gt;pcr15&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;0123456789012345678901234567890123456789012345678901234567890123&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Change this value, reboot once more, and everything should continue working.&lt;/p&gt;
&lt;h3 id="how-this-solution-prevents-volume-swapping"&gt;
 &lt;a href="#how-this-solution-prevents-volume-swapping" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;How this solution prevents volume swapping&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The explanation is in the Nix module defined in the file
&lt;a href="https://codeberg.org/giggio/nixos_raid_btrfs_luks_tpm/src/branch/main/ensure-pcr.nix"&gt;ensure-pcr.nix&lt;/a&gt;.&lt;/p&gt;
&lt;p&gt;It creates a systemd service that starts extending PCR 15 when &lt;code&gt;systemIdentity.enable&lt;/code&gt; is enabled, running:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemd-cryptsetup attach crypt_disk1 /dev/disk/by-partlabel/NIXLUKS1 - &lt;span class="s1"&gt;&amp;#39;tpm2-device=auto,tpm2-measure-pcr=yes,discard&amp;#39;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And when the hash of PCR 15 has been defined in &lt;code&gt;systemIdentity.pcr15&lt;/code&gt;, it creates a &lt;code&gt;check-pcrs&lt;/code&gt; service that runs in
initrd and, if it fails, prevents boot from continuing. This service is very simple: it just compares the hash stored in
the configuration with the value found in PCR 15:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="o"&gt;[[&lt;/span&gt; &lt;span class="k"&gt;$(&lt;/span&gt;systemd-analyze pcrs &lt;span class="m"&gt;15&lt;/span&gt; --json&lt;span class="o"&gt;=&lt;/span&gt;short &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s2"&gt;&amp;#34;.[0].sha256&amp;#34;&lt;/span&gt;&lt;span class="k"&gt;)&lt;/span&gt; !&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="si"&gt;${&lt;/span&gt;&lt;span class="nv"&gt;config&lt;/span&gt;&lt;span class="p"&gt;.systemIdentity.pcr15&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt; &lt;span class="o"&gt;]]&lt;/span&gt; &lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="k"&gt;then&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PCR 15 check failed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;exit&lt;/span&gt; &lt;span class="m"&gt;1&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;else&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nb"&gt;echo&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;PCR 15 check succeed&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="k"&gt;fi&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;As this service is required by &lt;code&gt;sysroot.mount&lt;/code&gt;, if &lt;code&gt;check-pcrs&lt;/code&gt; fails the root disk cannot be mounted and the entire
boot fails, entering emergency mode.&lt;/p&gt;
&lt;h3 id="what-if-the-boot-fails"&gt;
 &lt;a href="#what-if-the-boot-fails" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;What if the boot fails?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;If the value of PCR 15 changes for any reason, the boot will fail and enter emergency mode. In that case, you will need
to log in with the emergency password configured in initrd and run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl disable check-pcrs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;systemctl default
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt;: see the &lt;code&gt;boot.initrd.systemd.emergencyAccess&lt;/code&gt; attribute in &lt;code&gt;configuration.nix&lt;/code&gt;, that is what defines the
emergency initrd password (using a hash of the password). To create the password as &lt;code&gt;test&lt;/code&gt;, run (replace the salt,
&lt;code&gt;xyz&lt;/code&gt;, and the password):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;openssl passwd -6 -salt xyz &lt;span class="nb"&gt;test&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;Tip&lt;/strong&gt;: I recommend that you test this by changing the configuration value to the wrong hash, running &lt;code&gt;switch&lt;/code&gt;, and
confirming that the boot failed. Then test the commands above: you will need to provide the initrd emergency password,
disable the &lt;code&gt;check-pcrs&lt;/code&gt; service, and continue the boot. In the worst-case scenario, if you cannot do that, you can
choose the previous configuration from the boot menu. After logging in again, set the PCR 15 value in the configuration
back to the correct one, run a new &lt;code&gt;switch&lt;/code&gt; and reboot, making sure that boot works again without issues.&lt;/p&gt;
&lt;h3 id="is-it-secure"&gt;
 &lt;a href="#is-it-secure" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Is it secure?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Again: not yet.&lt;/p&gt;
&lt;p&gt;The attack described by Oddlama no longer works, but there is still another one: since we only bind the disk encryption
to PCRs 7 and 15, it is still possible to replace the operating system with another one and use the TPM to decrypt the
disk. We will solve that in the next post.&lt;/p&gt;</description></item><item><title>NixOS: Installation Guide with RAID 1, encryption, and TPM Unlock (part 5 - unlocking the disk with TPM)</title><link>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/</link><pubDate>Mon, 18 May 2026 11:00:00 -0300</pubDate><author>giggio@giggio.net (Giovanni Bassi)</author><guid>https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-5-destrancando-o-disco-com-tpm/</guid><category>infra</category><description>&lt;p&gt;At last, we are going to automatically decrypt the NixOS disk using the TPM!&lt;/p&gt;
&lt;p&gt;This is the fifth post in the series:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-1/"&gt;Preparing the virtual machine and partitioning the disks&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-2-disko-luks-e-btrfs/"&gt;Disko, LUKS, and btrfs&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-3-instalando-o-so/"&gt;Installing the OS&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://giggio.net/en/blog/nix-os-guia-de-instalacao-com-raid-1-criptografia-e-tpm-unlock-parte-4-secure-boot/"&gt;Enabling Secure Boot&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Unlocking the disk with TPM (this post)&lt;/strong&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Up to now, every boot has required the LUKS password to decrypt the disk. But if the machine’s integrity has not been
compromised, there is no problem in doing this automatically. But how?&lt;/p&gt;
&lt;p&gt;Now it is time to orchestrate everything we have put together so far, connecting LUKS, Secure Boot, and the TPM.&lt;/p&gt;
&lt;h3 id="why-this-is-safe"&gt;
 &lt;a href="#why-this-is-safe" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Why this is safe&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Assuming the machine has not been compromised, when the operating system comes up, the only safe way to access it is
with valid credentials — assuming the system has no exploitable vulnerability. That means logging in through the
console, SSH, the graphical interface, or a serial connection. In practice, that means that without a login password
there is no access to the computer, and no privilege is gained.&lt;/p&gt;
&lt;p&gt;Everything works based on this principle: an uncompromised and intact system protects itself, and if it is compromised
(firmware modification, disk removal, etc), encryption protects the data.&lt;/p&gt;
&lt;h3 id="understanding-the-tpm"&gt;
 &lt;a href="#understanding-the-tpm" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Understanding the TPM&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;The TPM (Trusted Platform Module) is a dedicated processor that stores, in an inviolable way, measurements taken of the
hardware and software running on the computer. These measurements are stored in &lt;strong&gt;PCRs&lt;/strong&gt; (Platform Configuration
Registers), whose values can be extended, but never set directly. Every value sent to the TPM extends the previous
value and produces a new one. If we were to model this as a formula, it would be something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;PCR = HASH(PCR || new_measurement)
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;In other words, the PCR hash is used together with the new measured value to create the new hash.&lt;/p&gt;
&lt;p&gt;You can see the hashes of each PCR on your running TPM (fictitious values):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-console" data-lang="console"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="gp"&gt;$&lt;/span&gt; systemd-analyze pcrs
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;NR NAME SHA256
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 0 platform-code 0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 1 platform-config 123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 2 external-code 23456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 3 external-config 3456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 4 boot-loader-code 456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 5 boot-loader-config 56789abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 6 host-platform 6789abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 7 secure-boot-policy 789abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 8 - 89abcdef0123456789abcdef0123456789abcdef0123456789abcdef01234567
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt; 9 kernel-initrd 9abcdef0123456789abcdef0123456789abcdef0123456789abcdef012345678
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;10 ima abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;11 kernel-boot 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;12 kernel-config 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;13 sysexts 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;14 shim-policy bcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789a
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;15 system-identity 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;16 debug 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;17 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;18 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;19 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;20 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;21 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;22 - ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="go"&gt;23 application-support 0000000000000000000000000000000000000000000000000000000000000000
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Each PCR is independent and is extended freely, without depending on the others. On compatible TPMs there are 24 PCRs;
the &lt;a href="https://uapi-group.org/"&gt;UAPI (Linux Userspace API Group)&lt;/a&gt; in
&lt;a href="https://uapi-group.org/specifications/specs/linux_tpm_pcr_registry/"&gt;its specifications&lt;/a&gt; considers 0–7 to be firmware
and 8–15 to be the range available to the operating system, although the systemd ecosystem also uses specific PCRs for
additional measurements, and PCRs 16–23 are also usable.&lt;/p&gt;
&lt;p&gt;When the computer powers on, all PCRs start out with no value. The firmware then begins extending the PCRs. For example,
PCR 0 depends on the software running in the firmware — what we normally call the BIOS. When you “update the BIOS”,
PCR 0 changes. PCR 1 may change if you replace a hardware component. PCR 4 is tied to the operating system; it measures
the boot loader and additional drivers. And so on.&lt;/p&gt;
&lt;p&gt;PCR 7 depends on the Secure Boot state. Its values are commonly extended using the keys enrolled in Secure Boot, as well
as the Secure Boot Authority. It is this PCR that we will use to unlock the disk, at this moment.&lt;/p&gt;
&lt;p&gt;I will come back to PCRs in the next posts, but it is important to understand what they are for. If you decide to use
what I am proposing in these posts, this knowledge may be useful if you run into trouble.&lt;/p&gt;
&lt;p&gt;You can see all the events that led to PCR extensions by running &lt;code&gt;sudo systemd-pcrlock log&lt;/code&gt;. Using &lt;code&gt;jq&lt;/code&gt;, you can show
only the events that led to PCR 7:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo /run/current-system/systemd/lib/systemd/systemd-pcrlock log --json&lt;span class="o"&gt;=&lt;/span&gt;short 2&amp;gt;/dev/null &lt;span class="p"&gt;|&lt;/span&gt; jq &lt;span class="s1"&gt;&amp;#39;[.log[] | select(.pcr == 7)]&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result, simplified so it does not get too long, is this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-driver-config&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;9f75b6823bff6af1024a4e2036719cdd548d3cbc2bf1de8e7ef4d0ed01f94bf9&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Variable: dbx-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcr&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;pcrname&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;secure-boot-policy&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;event&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;efi-variable-authority&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;match&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;f35567246a92b2fcdd2901e4ad2febdfd80173f25527c5a73033bf100a8eb980&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;phase&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;F&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;component&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;description&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Authority: db-d719b2cb-3d3a-4596-a3bc-dad00e67656f&amp;#34;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Note that the hash value, stored in the &lt;code&gt;sha256&lt;/code&gt; field, changes with each event. When the TPM receives a request to
decrypt a value, its state must match exactly what was used when the value was encrypted (this is done through TPM
policies). That means a value encrypted early in the boot process may no longer be decryptable at the end of the boot
process, if the PCR has been extended again. This is especially useful for this very reason: allowing a value to be
accessible only at a specific point in the boot sequence, such as during &lt;code&gt;initrd&lt;/code&gt; startup (PCR 11 has measurements that
make this possible).&lt;/p&gt;
&lt;p&gt;Systemd &lt;a href="https://systemd.io/TPM2_PCR_MEASUREMENTS/"&gt;documents&lt;/a&gt; how it uses PCRs, and since nowadays there is practically
no Linux without systemd, it is worth understanding. This document will be useful later, when we look more closely at
PCR 15 (system identity).&lt;/p&gt;
&lt;h3 id="unlocking-the-disk-during-boot"&gt;
 &lt;a href="#unlocking-the-disk-during-boot" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Unlocking the disk during boot&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Only one command is needed to make everything happen. In our case, since we have two disks and two partitions, we run
the command once for each of them (you will need to enter your LUKS password for the process to complete):&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt; /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo systemd-cryptenroll --tpm2-device&lt;span class="o"&gt;=&lt;/span&gt;auto --tpm2-pcrs&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="m"&gt;7&lt;/span&gt; /dev/disk/by-label/NIXLUKS2
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;When this command runs, the volume master key is obtained using your password. It is then encrypted (sealed) using the
TPM, and this sealed value is stored in the LUKS volume header. In this case, only PCR 7 is being used to seal the
value, meaning only the Secure Boot state. That means if the Secure Boot state changes (if it is reset, or if a new key
is removed or added), the TPM’s PCR 7 state will also change, and the disk will no longer be decrypted during boot. In
this post, the goal is to tie automatic disk decryption to the Secure Boot state, and that is what we have done.&lt;/p&gt;
&lt;p&gt;You can see the details stored in the LUKS volume header by running the following command after &lt;code&gt;systemd-cryptenroll&lt;/code&gt;
has been executed:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump /dev/disk/by-label/NIXLUKS1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;Notice the &lt;code&gt;Tokens&lt;/code&gt; field, with the first one being &lt;code&gt;systemd-tpm2&lt;/code&gt;, with a &lt;code&gt;Keyslot&lt;/code&gt;:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Tokens:
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 0: systemd-tpm2
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; Keyslot: 1
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;To see more details about the sealed value, run:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;sudo cryptsetup luksDump --dump-json-metadata /dev/disk/by-label/NIXLUKS1 &lt;span class="p"&gt;|&lt;/span&gt; jq -r &lt;span class="s1"&gt;&amp;#39;.tokens.&amp;#34;0&amp;#34;&amp;#39;&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The result will look something like this:&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;type&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;systemd-tpm2&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;keyslots&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-blob&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcrs&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;7&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-pcr-bank&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;sha256&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2-policy-hash&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;a hash&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;tpm2_srk&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;lt;another base64 value&amp;gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The &lt;code&gt;tpm2-blob&lt;/code&gt; contains the value encrypted by the TPM (plus metadata). Using the tools from the
&lt;a href="https://github.com/tpm2-software/tpm2-tools"&gt;tpm2-tools&lt;/a&gt; package and root access, you could recover the master password
from this value. That is not a security problem: the system is already running and the disk is already decrypted. The
purpose of TPM protection is not to protect a healthy running system from its administrator, but to protect against
integrity violations that happen before, during, and after boot.&lt;/p&gt;
&lt;p&gt;With just these two commands, the TPM will start unlocking both LUKS volumes. Reboot to confirm that it no longer asks
for the password. If it does, inspect the boot logs with &lt;code&gt;sudo journalctl --boot&lt;/code&gt;.&lt;/p&gt;
&lt;h3 id="is-it-secure"&gt;
 &lt;a href="#is-it-secure" class="site-blog-post-header"&gt;
 &lt;span class="site-blog-post-header-text"&gt;Is it secure?&lt;/span&gt;
 &lt;i class="fa-solid fa-link site-blog-post-header-paragraph"&gt;&lt;/i&gt;
 &lt;/a&gt;
&lt;/h3&gt;
&lt;p&gt;Not yet.&lt;/p&gt;
&lt;p&gt;There are two vulnerabilities, and I will address them in the next posts. Neither is trivial, but someone who
understands how all of this works could gain access to the data in a short amount of time. None of the work done so far
will be lost; we will build on the current solution. The good news is that both issues are easy to fix.&lt;/p&gt;
&lt;p&gt;That said, it is fair assume that most people, including technicians who are not systems penetration experts, would no
longer be able to read the disk of a computer encrypted this way. In other words, sending a laptop for repair with this
configuration would probably not present a risk. But that is still not good enough.&lt;/p&gt;
&lt;p&gt;In the next post we will start closing the gaps.&lt;/p&gt;</description></item></channel></rss>