No. I would appreciate if you tested Heads instead of just reporting beliefs…
This is qemu-coreboot-whiptail-tpm1 output on boot, with TPMTOTP (no HOTP here, this part is still not emulated).
[ 4.376412] DEBUG: Debug output enabled from board CONFIG_DEBUG_OUTPUT=y option (/etc/config)
[ 4.394445] TRACE: Under init
[ 4.442100] DEBUG: Applying panic_on_oom setting to sysctl
[ 4.560708] TRACE: /bin/tpmr(32): main
[ 4.657700] TRACE: /bin/cbfs-init(5): main
[ 4.776243] DEBUG: Extending TPM PCR 7 with /.gnupg/pubring.kbx
[ 4.856752] TRACE: /bin/tpmr(32): main
[ 4.911567] DEBUG: Direct translation from tpmr to tpm1 call
[ 4.962287] DEBUG: exec tpm extend -ix 7 -if /tmp/cbfs.94
[ 5.169580] DEBUG: Extending TPM PCR 7 with /.gnupg/trustdb.gpg
[ 5.256487] TRACE: /bin/tpmr(32): main
[ 5.326280] DEBUG: Direct translation from tpmr to tpm1 call
[ 5.379846] DEBUG: exec tpm extend -ix 7 -if /tmp/cbfs.94
[ 5.614361] TRACE: /bin/key-init(5): main
[ 6.998357] TRACE: Under /etc/ash_functions:combine_configs
[ 7.089798] TRACE: Under /etc/ash_functions:pause_recovery
!!! Hit enter to proceed to recovery shell !!!
[ 7.357474] TRACE: /bin/setconsolefont.sh(6): main
[ 7.415098] DEBUG: Board does not ship setfont, not checking console font
[ 7.652789] TRACE: /bin/gui-init(645): main
[ 7.688602] TRACE: /etc/functions(715): detect_boot_device
[ 7.744983] TRACE: /etc/functions(682): mount_possible_boot_device
[ 7.801285] TRACE: /etc/functions(642): is_gpt_bios_grub
[ 7.901675] TRACE: /dev/vda1 is partition 1 of vda
[ 8.039730] TRACE: /etc/functions(619): find_lvm_vg_name
[ 8.205960] TRACE: Try mounting /dev/vda1 as /boot
[ 8.269801] EXT4-fs (vda1): mounted filesystem with ordered data mode. Opts: (null)
[ 8.331458] TRACE: /bin/gui-init(319): clean_boot_check
[ 8.447656] TRACE: /bin/gui-init(348): check_gpg_key
[ 8.559142] TRACE: /bin/gui-init(185): update_totp
[ 8.679132] TRACE: /bin/unseal-totp(8): main
[ 8.781385] TRACE: /bin/tpmr(32): main
[ 8.836697] TRACE: /bin/tpmr(599): tpm1_unseal
[ 8.991293] DEBUG: Running at_exit handlers
[ 9.029942] TRACE: /bin/tpmr(377): cleanup_shred
[ 9.126974] TRACE: /bin/gui-init(254): update_hotp
[ 9.164743] TRACE: /bin/gui-init(679): main
[ 9.202049] TRACE: /bin/gui-init(395): show_main_menu
[ 13.967078] TRACE: /bin/gui-init(614): attempt_default_boot
[ 14.003165] TRACE: /bin/gui-init(23): mount_boot
[ 14.053492] TRACE: /bin/gui-init(69): verify_global_hashes
[ 14.082374] TRACE: /etc/functions(396): check_config
[ 14.186498] TRACE: /etc/functions(587): verify_checksums
[ 18.799817] TRACE: /etc/functions(500): print_tree
[ 19.035544] TRACE: /bin/kexec-select-boot(8): main
[ 19.087871] TRACE: /etc/functions(396): check_config
[ 19.246365] TRACE: /bin/gpgv(5): main
[ 19.439487] TRACE: /etc/functions(758): scan_boot_options
[ 19.527789] DEBUG: kexec-parse-boot /boot /boot/grub/grub.cfg
[ 19.678871] TRACE: /bin/kexec-parse-boot(5): main
[ 19.747174] DEBUG: filedir= /boot/grub
[ 19.808564] DEBUG: bootdir= /boot
[ 19.868310] DEBUG: bootlen= 5
[ 19.931705] DEBUG: appenddir= /grub
[ 22.154550] DEBUG: grub_entry : linux trimcmd prior of kernel/append parsing: linux /vmlinuz-6.1.0-21-amd64 root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro console=ttyS0 console=tty systemd.zram=0 quiet
[ 22.462921] DEBUG: grub_entry: linux initrd= /initrd.img-6.1.0-21-amd64
[ 23.246316] DEBUG: grub_entry : linux trimcmd prior of kernel/append parsing: linux /vmlinuz-6.1.0-21-amd64 root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro console=ttyS0 console=tty systemd.zram=0 quiet
[ 23.556517] DEBUG: grub_entry: linux initrd= /initrd.img-6.1.0-21-amd64
[ 24.344211] DEBUG: grub_entry : linux trimcmd prior of kernel/append parsing: linux /vmlinuz-6.1.0-21-amd64 root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro single console=ttyS0 console=tty systemd.zram=0
[ 24.665755] DEBUG: grub_entry: linux initrd= /initrd.img-6.1.0-21-amd64
[ 25.472082] DEBUG: grub_entry : linux trimcmd prior of kernel/append parsing: linux /vmlinuz-6.1.0-18-amd64 root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro console=ttyS0 console=tty systemd.zram=0 quiet
[ 25.786961] DEBUG: grub_entry: linux initrd= /initrd.img-6.1.0-18-amd64
[ 26.584814] DEBUG: grub_entry : linux trimcmd prior of kernel/append parsing: linux /vmlinuz-6.1.0-18-amd64 root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro single console=ttyS0 console=tty systemd.zram=0
[ 26.901034] DEBUG: grub_entry: linux initrd= /initrd.img-6.1.0-18-amd64
[ 27.068555] TRACE: /etc/functions(587): verify_checksums
[ 30.969222] TRACE: /etc/functions(500): print_tree
[ 31.120977] TRACE: /etc/functions(383): read_tpm_counter
[ 31.232967] TRACE: /bin/tpmr(32): main
[ 31.313545] DEBUG: Direct translation from tpmr to tpm1 call
[ 31.372766] DEBUG: exec tpm counter_read -ix 0
[ 33.471645] TRACE: /bin/kexec-boot(7): main
[ 33.677742] DEBUG: kexectype= elf
[ 33.739605] DEBUG: restval=
[ 33.798215] DEBUG: filepath= /boot/vmlinuz-6.1.0-21-amd64
[ 33.857492] DEBUG: kexeccmd= kexec -d -l /boot/vmlinuz-6.1.0-21-amd64
[ 34.158796] TRACE: /bin/kexec-insert-key(6): main
[ 34.263347] TRACE: /bin/qubes-measure-luks(6): main
[ 34.309960] DEBUG: Arguments passed to qubes-measure-luks: /dev/vda3 /dev/vda5
[ 34.368403] DEBUG: Storing LUKS header for /dev/vda3 into /tmp/lukshdr-_dev_vda3
[ 34.685829] DEBUG: Storing LUKS header for /dev/vda5 into /tmp/lukshdr-_dev_vda5
[ 34.983574] DEBUG: Hashing LUKS headers into /tmp/luksDump.txt
[ 35.658812] DEBUG: Removing /tmp/lukshdr-*
[ 35.739006] DEBUG: Extending TPM PCR 6 with hash of LUKS headers from /tmp/luksDump.txt
[ 35.812643] TRACE: /bin/tpmr(32): main
[ 35.867148] DEBUG: Direct translation from tpmr to tpm1 call
[ 35.911928] DEBUG: exec tpm extend -ix 6 -if /tmp/luksDump.txt
[ 36.162907] TRACE: /bin/kexec-unseal-key(13): main
[ 36.213664] DEBUG: CONFIG_TPM: y
[ 36.254813] DEBUG: CONFIG_TPM2_TOOLS:
[ 36.296320] DEBUG: Show PCRs
[ 36.465515] DEBUG: PCR-00: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 36.489281] PCR-01: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 36.514936] PCR-02: 4C 82 95 9C AD EC 02 D7 AF 23 5D C6 F0 81 E2 6B D0 19 B7 A5
[ 36.549862] PCR-03: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 36.572143] PCR-04: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 36.593810] PCR-05: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
[ 36.623400] PCR-06: 79 99 36 2F FD 42 2C 10 3F 42 BC EE 04 62 32 73 32 4D 1D 72
[ 36.647045] PCR-07: 67 F0 C6 B5 C8 A2 A5 6E FB D0 14 EA 2C 09 1C 6C B4 0B C1 86
At this point, Heads prompts the user to type his TPM Disk Unlock Key, where
- /boot content was not changed since /boot hash digests were detached signed : /boot content was verified against digest and detached signature. Machine is in the state known from the user.
- TOTP was unsealed early at boot prior of
[ 9.202049] TRACE: /bin/gui-init(395): show_main_menu
, the GUI at the point permits to boot system, where no integrity validation was done yet on /boot content. If the firmware was tampered with here, the TOTP unsealing would fail, which is about PCR0-1-2-3-4-7 measurement sealing/unsealing (read initrd/bin/unseal-totp). This has nothing to do with /boot content, which integrity and authenticity is validated per pubkey fused in SPI content. You can see traces of measurements from Heads output above, where TPMTOTP unsealing op was successful at[ 8.836697] TRACE: /bin/tpmr(599): tpm1_unseal
and where TOTP code was provided to user to check, and where HOTP would launch default boot entry if reverse HOTP challenge against USB Security dongle was successful at that step. - Then comes launching the default boot entry ,since we are at TPM DUK prompt here now. So as said previously, the prompt goes this far because /boot integrity+authenticity was verified, LUKS header backup matches what was detached signed: the TPM DUK related measurements + passphrase will then be checked against. Note that here, if you go to recovery shell, if any kernel module was loaded then when TPM DUK was sealed, secret would be unsealed. Let’s say I attempt wrong passphrase:
[ 1174.142253] DEBUG: tpmr unseal 3 0,1,2,3,4,5,6,7 312 /tmp/secret/initrd/secret.key <hidden>
[ 1174.296379] TRACE: /bin/tpmr(32): main
[ 1174.344488] TRACE: /bin/tpmr(599): tpm1_unseal
[ 1174.501948] DEBUG: Running at_exit handlers
[ 1174.528497] TRACE: /bin/tpmr(377): cleanup_shred
[ 1174.605995] DEBUG: tpmr: exited with status 29
[ 1174.643426] *** WARNING: Unable to unseal LUKS Disk Unlock Key from TPM ***
Let’s say I type good TPM DUK passphrase:
[ 1280.251212] DEBUG: tpmr unseal 3 0,1,2,3,4,5,6,7 312 /tmp/secret/initrd/secret.key <hidden>
[ 1280.475393] TRACE: /bin/tpmr(32): main
[ 1280.545405] TRACE: /bin/tpmr(599): tpm1_unseal
[ 1280.704137] DEBUG: Running at_exit handlers
[ 1280.747736] TRACE: /bin/tpmr(377): cleanup_shred
[ 1280.843792] DEBUG: Extending TPM PCR 4 to prevent further secret unsealing
[ 1280.961605] TRACE: /bin/tpmr(32): main
[ 1281.052794] DEBUG: Direct translation from tpmr to tpm1 call
[ 1281.125280] DEBUG: exec tpm extend -ix 4 -ic generic
[ 1283.242338] TRACE: /bin/kexec-boot(7): main
[ 1283.459100] DEBUG: kexectype= elf
[ 1283.527463] DEBUG: restval=
[ 1283.598185] DEBUG: filepath= /boot/vmlinuz-6.1.0-21-amd64
[ 1283.661199] DEBUG: kexeccmd= kexec -d -l /boot/vmlinuz-6.1.0-21-amd64
[ 1284.115952] DEBUG: eval kexec -d -l /boot/vmlinuz-6.1.0-21-amd64 --initrd=/tmp/secret/initrd.cpio --append="root=UUID=8c44b114-b625-440b-a708-177a6b510152 ro console=ttyS0 console=tty systemd.zram=0 console=ttyS0 console=tty systemd.zram=0 "
- And then Heads pass initrd.cpio to next OS through kexec call, overriding crypttab to point to secret.key so that decrypt key passphrase is typed the most reasonably trusted environment from end user owning the keys, having sealed firmware states and detached signed /boot content:
[ 1412.542100] TRACE: /bin/tpmr(32): main
[ 1412.621136] kexec_core: Starting new kernel
Then let’s play your game here and unmix concepts a little…
- So we take for granted someone has access to booted OS here, or access the drive to modify /boot content in the goal of flashing firmware with
iommem=relaxed
(which wouldn’t change a thing for sandy/ivy/haswell platforms because platform lock (PR0) which only permits Heads to flash firmware, which as said previously can be safeguarded with authenticated Heads). So here, we modified grub.cfg in a previous access to booted machine already, and flashrom hook was successful injecting config override, trustdb and keyring as also previously said, while the firmware you talk about was perfectly crafted to contain exactly the same kernel modules (PCR5), keyring and trustdb and user config (PCR7). This is stretched thin, really, and you talk about a synchronized compromise of OS (2 boots needed: one to modify grub.cfg and another one running your hook of flashrom with you coming back and providing a totally perfectly crafted firmware image which will pass coreboot measured boot PCR2 extended measurements sealed as part of TPMTOTP and PCR7 containing needed trustdb+keyring+cofnig overrides in to be flashed firmware here to be successful. In all case this is what would happen on next boot, with firmware perfectly tampered with (which you again oversimplify as being jsut bootblock related and you is what I keep repeating here to not be so trivial but decide to straw man to prove a point). This would happen on first reboot OS compromise to haveiommem=relaxed
in grub.cfg to flash your provided perfectly crafted rom image, simulated just by upgrading my debian-12 qemu drive, where re-ownerhip has been applied with canokey virt OpenPGP smatrcard, vTPM enrolled which you could play with to prove your point technically and would be listened by all. Note that I agree with you (other then your oversimplification pointing to bootblock only needing to be tampered with):
That would fatigue TOTP only users, yes, not HOTP. Where if Time is off, there are helpers to sync NTP over network or manually, where again, if computer is used daily, OS deals with NTP sync and time being off should not happen often but if laptop unused for long period of time. Again: HOTP unaffedted and would go to default boot if /boot not updated with recent OS update. Again, QubesOS requiring user to enforce manually and Heads users pretty used to only update dom0 when they can reboot and sign /boot right after, which also validates their USB Security dongle is still under their control because they validate tey can sign /boot boot content. It is not recommended to update firmware and OS at hte same time and FWUPD not yet pushed forward, but we should expect those firmware files to land under /boot as my use case talked about previously, which is part of hash digests and also detached signed, so would be part of the red error screen above. Agreed that changes in those files should be verified, and there are issues opened to discuss possible implementations, but there is no consensus there yet. Once best practices are decided, it will be implemented; if OS decides to implement signing hooks, then Heads will just apply/adapt that verification scheme as well.
I have a fleet myself, but not big enough to justify BootGuard. Yours do?
No NDA/accepted surveillance contract clauses on hardware you manage, still renting your hardware until EOL from vendor because BootGuarded.