summaryrefslogtreecommitdiffstatshomepage
path: root/scripts/tpm_nvpcr_unmask.py
diff options
context:
space:
mode:
authorsommerfeld <sommerfeld@sommerfeld.dev>2026-09-30 08:54:59 +0100
committersommerfeld <sommerfeld@sommerfeld.dev>2026-09-30 08:54:59 +0100
commitc1f7133be790a972400ba63ca40f92ed699d216e (patch)
tree12e36f95ff1e87998c1028a6e2c3242cad32aa3a /scripts/tpm_nvpcr_unmask.py
parent76db86a22a23ccfed07d6160cfdab4ef60067ef0 (diff)
downloaddotfiles-c1f7133be790a972400ba63ca40f92ed699d216e.tar.gz
dotfiles-c1f7133be790a972400ba63ca40f92ed699d216e.tar.bz2
dotfiles-c1f7133be790a972400ba63ca40f92ed699d216e.zip
Enable signed initrd and NvPCR measurements
Diffstat (limited to 'scripts/tpm_nvpcr_unmask.py')
-rw-r--r--scripts/tpm_nvpcr_unmask.py53
1 files changed, 53 insertions, 0 deletions
diff --git a/scripts/tpm_nvpcr_unmask.py b/scripts/tpm_nvpcr_unmask.py
new file mode 100644
index 0000000..0c4ea43
--- /dev/null
+++ b/scripts/tpm_nvpcr_unmask.py
@@ -0,0 +1,53 @@
+"""Remove only the old NvPCR masks owned by this repository."""
+
+import hashlib
+import os
+from pathlib import Path
+
+MASKS = [
+ *(
+ Path(f"/etc/nvpcr/{name}.nvpcr")
+ for name in ("cryptsetup", "hardware", "login", "verity")
+ ),
+ Path("/etc/systemd/system/systemd-pcrlogin@.service"),
+ Path("/etc/systemd/system/systemd-pcrproduct.service"),
+]
+DROPIN = Path("/etc/mkinitcpio.conf.d/60-no-nvpcr.conf")
+DROPIN_SHA256 = "69a242f865bbd1adee85f168f19e0f4e5b94a78b3d65e5dfa976c5e0f76deea4"
+RECOVERY = Path("/boot/EFI/Linux/arch-linux-hardened-nvpcr-recovery.efi")
+BACKUP = Path("/var/lib/dotfiles/tpm-nvpcr/backup")
+
+
+def validate(paths: list[Path]) -> list[Path]:
+ removable = []
+ for path in paths:
+ if not path.exists() and not path.is_symlink():
+ continue
+ if not path.is_file() or path.is_symlink():
+ raise ValueError(f"Expected a regular file: {path}")
+ data = path.read_bytes()
+ if path == DROPIN:
+ if hashlib.sha256(data).hexdigest() != DROPIN_SHA256:
+ raise ValueError(f"Old mkinitcpio drop-in was changed: {path}")
+ elif data:
+ raise ValueError(f"NvPCR mask is not empty: {path}")
+ removable.append(path)
+ return removable
+
+
+def main() -> None:
+ if os.geteuid() != 0 or Path("/etc/hostname").read_text().strip() != "halley2":
+ raise ValueError("Run as root on Halley2")
+ if not RECOVERY.is_file() or not BACKUP.is_dir():
+ raise ValueError("Recovery image and backup must exist before removing masks")
+ removable = validate([*MASKS, DROPIN])
+ for path in removable:
+ path.unlink()
+ print(f"removed: {path}")
+
+
+if __name__ == "__main__":
+ try:
+ main()
+ except (OSError, ValueError) as exc:
+ raise SystemExit(f"NvPCR unmask failed: {exc}") from exc