Skip to content

Commit 8be0849

Browse files
committed
Harden TPM reseal flows and document QEMU debugging
- make TPM reset path automatically re-sign /boot and auto-reseal DUK when key metadata exists - keep DUK reseal enforced across TPM reset and TOTP/HOTP reseal paths - improve integrity-gate diagnostics and clean up duplicate/noisy trace/debug/comment artifacts - refine integrity investigation/reporting behavior and detached-signature debug handling - document canokey state reuse between QEMU build dirs and TPM2 PCAP capture workflow - ignore main dir *.asc files Signed-off-by: Thierry Laurion <insurgo@riseup.net>
1 parent 9a76157 commit 8be0849

File tree

15 files changed

+415
-75
lines changed

15 files changed

+415
-75
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
*.asc
12
*.bad
23
*.bz2
34
*.cpio

boards/qemu-coreboot-fbwhiptail-tpm2/qemu-coreboot-fbwhiptail-tpm2.config

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,9 @@ export CONFIG_PRIMARY_KEY_TYPE=ecc
7979
export CONFIG_DEBUG_OUTPUT=y
8080
export CONFIG_ENABLE_FUNCTION_TRACING_OUTPUT=y
8181
#Enable TPM2 pcap output under /tmp
82+
# When enabled, tpmr writes TPM2 command/response capture to /tmp/tpm0.pcap
83+
# (inside the Heads runtime). This can be inspected with Wireshark to debug
84+
# TPM interaction similarly to a TPM bus sniffer.
8285
export CONFIG_TPM2_CAPTURE_PCAP=y
8386
#Enable quiet mode: technical information logged under /tmp/debug.log
8487
export CONFIG_QUIET_MODE=n

initrd/bin/gui-init

Lines changed: 123 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ export BG_COLOR_MAIN_MENU="normal"
1515
# reset when we reach the main menu so the user can retry from the main menu and
1616
# # see errors again.
1717
skip_to_menu="false"
18+
INTEGRITY_GATE_REQUIRED="n"
1819

1920
mount_boot() {
2021
TRACE_FUNC
@@ -115,23 +116,44 @@ verify_global_hashes() {
115116
less /tmp/hash_output_mismatches
116117
#move outdated hash mismatch list
117118
mv /tmp/hash_output_mismatches /tmp/hash_output_mismatch_old
118-
TEXT="Would you like to update your checksums now?"
119+
TEXT="${CHANGED_FILES_COUNT} files failed the verification process.\n\nThis could indicate a compromise!\n\nWould you like to investigate discrepancies or update your checksums now?"
119120
else
120-
TEXT="The following files failed the verification process:\n\n${CHANGED_FILES}\n\nThis could indicate a compromise!\n\nWould you like to update your checksums now?"
121+
TEXT="The following files failed the verification process:\n\n${CHANGED_FILES}\n\nThis could indicate a compromise!\n\nWould you like to investigate discrepancies or update your checksums now?"
121122
fi
122123
fi
123124

124-
if (whiptail_error --title 'ERROR: Boot Hash Mismatch' --yesno "$TEXT" 0 80); then
125-
if update_checksums; then
126-
BG_COLOR_MAIN_MENU="normal"
127-
return 0
128-
else
129-
whiptail_error --title 'ERROR' \
130-
--msgbox "Failed to update checksums / sign default config" 0 80
131-
fi
132-
fi
133-
BG_COLOR_MAIN_MENU="error"
134-
return 1
125+
while true; do
126+
TRACE_FUNC
127+
whiptail_error --title 'ERROR: Boot Hash Mismatch' \
128+
--menu "$TEXT\n\nChoose an action:" 0 80 8 \
129+
'i' ' Investigate discrepancies -->' \
130+
'u' ' Update checksums now' \
131+
'm' ' Return to main menu' \
132+
2>/tmp/whiptail || {
133+
BG_COLOR_MAIN_MENU="error"
134+
return 1
135+
}
136+
137+
option=$(cat /tmp/whiptail)
138+
case "$option" in
139+
i)
140+
investigate_integrity_discrepancies
141+
;;
142+
u)
143+
if update_checksums; then
144+
BG_COLOR_MAIN_MENU="normal"
145+
return 0
146+
else
147+
whiptail_error --title 'ERROR' \
148+
--msgbox "Failed to update checksums / sign default config" 0 80
149+
fi
150+
;;
151+
m | *)
152+
BG_COLOR_MAIN_MENU="error"
153+
return 1
154+
;;
155+
esac
156+
done
135157
fi
136158
}
137159

@@ -146,6 +168,63 @@ prompt_update_checksums() {
146168
fi
147169
}
148170

171+
gate_reseal_with_integrity_report() {
172+
TRACE_FUNC
173+
local token_ok="y"
174+
175+
if [ "$INTEGRITY_GATE_REQUIRED" != "y" ]; then
176+
DEBUG "Skipping integrity gate: no TOTP/HOTP failure context"
177+
return 0
178+
fi
179+
180+
INTEGRITY_REPORT_HASH_STATE="UNKNOWN"
181+
report_integrity_measurements
182+
local report_rc=$?
183+
DEBUG "gate_reseal_with_integrity_report: report_integrity_measurements rc=$report_rc"
184+
DEBUG "gate_reseal_with_integrity_report: INTEGRITY_REPORT_HASH_STATE=$INTEGRITY_REPORT_HASH_STATE"
185+
if [ "$INTEGRITY_REPORT_HASH_STATE" != "OK" ]; then
186+
DEBUG "returned from integrity report, now running investigation"
187+
if ! investigate_integrity_discrepancies; then
188+
DEBUG "investigation indicated problem, aborting gate"
189+
return 1
190+
fi
191+
192+
DEBUG "gate_reseal_with_integrity_report: about to verify detached signature"
193+
DEBUG "ls -l /boot/kexec.sig: $(ls -l /boot/kexec.sig 2>/dev/null || echo missing)"
194+
if ! detached_kexec_signature_valid /boot; then
195+
DEBUG "detached_kexec_signature_valid failed"
196+
whiptail_error --title 'ERROR: Signature Verification Failed' \
197+
--msgbox "Cannot proceed with sealing new secrets because /boot/kexec.sig could not be verified with your current keyring.\n\nTreat /boot as untrusted and recover ownership first." 0 80
198+
return 1
199+
fi
200+
else
201+
DEBUG "gate_reseal_with_integrity_report: integrity is OK, skipping investigation and detached signature verification"
202+
fi
203+
204+
if [ -x /bin/hotp_verification ]; then
205+
token_ok="n"
206+
while [ "$token_ok" != "y" ]; do
207+
enable_usb
208+
if hotp_verification info >/dev/null 2>&1; then
209+
token_ok="y"
210+
break
211+
fi
212+
if ! whiptail_warning --title "USB Security Dongle Required" \
213+
--yes-button "Retry" --no-button "Abort" \
214+
--yesno "Your USB security dongle must be present before sealing new secrets.\n\nInsert the dongle and choose Retry, or Abort." 0 80; then
215+
return 1
216+
fi
217+
done
218+
fi
219+
220+
if ! whiptail_warning --title 'Integrity Gate Passed' \
221+
--yesno "Integrity checks completed.\n\nProceed with TOTP/HOTP reseal action?" 0 80; then
222+
return 1
223+
fi
224+
INTEGRITY_GATE_REQUIRED="n"
225+
return 0
226+
}
227+
149228
generate_totp_hotp() {
150229
TRACE_FUNC
151230
tpm_owner_password="$1" # May be empty, will prompt if needed and empty
@@ -216,16 +295,14 @@ update_totp() {
216295
if [ "$CONFIG_TPM" != "y" ]; then
217296
TOTP="NO TPM"
218297
else
219-
TOTP=$(unseal-totp)
298+
TOTP=$(HEADS_NONFATAL_UNSEAL=y unseal-totp)
220299
if [ $? -ne 0 ]; then
300+
INTEGRITY_GATE_REQUIRED="y"
221301
BG_COLOR_MAIN_MENU="error"
222302
if [ "$skip_to_menu" = "true" ]; then
223303
return 1 # Already asked to skip to menu from a prior error
224304
fi
225305

226-
DEBUG "CONFIG_TPM: $CONFIG_TPM"
227-
DEBUG "CONFIG_TPM2_TOOLS: $CONFIG_TPM2_TOOLS"
228-
DEBUG "Show PCRs"
229306
DEBUG "$(pcrs)"
230307

231308
whiptail_error --title "ERROR: TOTP Generation Failed!" \
@@ -245,7 +322,7 @@ update_totp() {
245322
option=$(cat /tmp/whiptail)
246323
case "$option" in
247324
g)
248-
if (whiptail_warning --title 'Generate new TOTP/HOTP secret' \
325+
if gate_reseal_with_integrity_report && (whiptail_warning --title 'Generate new TOTP/HOTP secret' \
249326
--yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 0 80); then
250327
if generate_totp_hotp && update_totp && BG_COLOR_MAIN_MENU="normal"; then
251328
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
@@ -257,14 +334,16 @@ update_totp() {
257334
return 1
258335
;;
259336
p)
260-
if reset_tpm && update_totp && BG_COLOR_MAIN_MENU="normal"; then
337+
if gate_reseal_with_integrity_report && reset_tpm && update_totp && BG_COLOR_MAIN_MENU="normal"; then
261338
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
262339
fi
263340
;;
264341
x)
265342
recovery "User requested recovery shell"
266343
;;
267344
esac
345+
else
346+
INTEGRITY_GATE_REQUIRED="n"
268347
fi
269348
fi
270349
}
@@ -286,7 +365,7 @@ update_hotp() {
286365
return
287366
fi
288367
fi
289-
HOTP=$(unseal-hotp)
368+
HOTP=$(HEADS_NONFATAL_UNSEAL=y unseal-hotp)
290369
# Don't output HOTP codes to screen, so as to make replay attacks harder
291370
hotp_verification check "$HOTP"
292371
case "$?" in
@@ -308,9 +387,7 @@ update_hotp() {
308387
fi
309388

310389
if [[ "$HOTP" = "Invalid code" ]]; then
311-
#Do not propose to generate a new secret if there is no /boot/kexec_hotp_counter
312-
# tpm unseal succeeded: so the sealed secret is correct: we should propose to reset TPM if not already
313-
# Here: the OS was most probably reinstalled since TPM can still unseal the secret
390+
INTEGRITY_GATE_REQUIRED="y"
314391
whiptail_error --title "ERROR: HOTP Validation Failed!" \
315392
--menu "ERROR: $CONFIG_BRAND_NAME couldn't validate the HOTP code.\n\nIf you just reflashed your BIOS, you should generate a new TOTP/HOTP secret.\n\nIf you have not just reflashed your BIOS, THIS COULD INDICATE TAMPERING!\n\nHow would you like to proceed?" 0 80 4 \
316393
'g' ' Generate new TOTP/HOTP secret' \
@@ -321,9 +398,11 @@ update_hotp() {
321398
option=$(cat /tmp/whiptail)
322399
case "$option" in
323400
g)
324-
if (whiptail_warning --title 'Generate new TOTP/HOTP secret' \
401+
if gate_reseal_with_integrity_report && (whiptail_warning --title 'Generate new TOTP/HOTP secret' \
325402
--yesno "This will erase your old secret and replace it with a new one!\n\nDo you want to proceed?" 0 80); then
326-
if generate_totp_hotp && BG_COLOR_MAIN_MENU="normal"; then
403+
if generate_totp_hotp; then
404+
update_totp || true
405+
BG_COLOR_MAIN_MENU="normal"
327406
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
328407
fi
329408
fi
@@ -335,6 +414,8 @@ update_hotp() {
335414
recovery "User requested recovery shell"
336415
;;
337416
esac
417+
else
418+
INTEGRITY_GATE_REQUIRED="n"
338419
fi
339420
}
340421

@@ -449,6 +530,7 @@ show_options_menu() {
449530
--menu "" 0 80 10 \
450531
'b' ' Boot Options -->' \
451532
't' ' TPM/TOTP/HOTP Options -->' \
533+
'i' ' Investigate integrity discrepancies -->' \
452534
'h' ' Change system time' \
453535
'u' ' Update checksums and sign all files in /boot' \
454536
'c' ' Change configuration settings -->' \
@@ -470,6 +552,9 @@ show_options_menu() {
470552
t)
471553
show_tpm_totp_hotp_options_menu
472554
;;
555+
i)
556+
investigate_integrity_discrepancies
557+
;;
473558
h)
474559
change-time.sh
475560
;;
@@ -545,12 +630,12 @@ show_tpm_totp_hotp_options_menu() {
545630
option=$(cat /tmp/whiptail)
546631
case "$option" in
547632
g)
548-
if generate_totp_hotp; then
633+
if gate_reseal_with_integrity_report && generate_totp_hotp; then
549634
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
550635
fi
551636
;;
552637
r)
553-
if reset_tpm; then
638+
if gate_reseal_with_integrity_report && reset_tpm; then
554639
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
555640
fi
556641
;;
@@ -616,19 +701,24 @@ reset_tpm() {
616701
GPG_KEY_COUNT=$(gpg -k 2>/dev/null | wc -l)
617702
if [ "$GPG_KEY_COUNT" -eq 0 ]; then
618703
prompt_missing_gpg_key_action
619-
elif (whiptail --title 'TPM Reset Successfully' \
620-
--yesno "Would you like to update the checksums and sign all of the files in /boot?\n\nYou will need your GPG key to continue and this will modify your disk.\n\nOtherwise the system will reboot immediately." 0 80); then
704+
else
705+
DEBUG "TPM reset successful: updating checksums/signatures without additional confirmation"
621706
if ! update_checksums; then
622707
whiptail_error --title 'ERROR' \
623708
--msgbox "Failed to update checksums / sign default config" 0 80
709+
return 1
624710
fi
625-
else
626-
warn "TPM reset successful, but user chose not to update+sign /boot checksums. Rebooting"
627-
reboot
628711
fi
629712
mount -o ro,remount /boot
630713

631-
generate_totp_hotp "$tpm_owner_password"
714+
if ! generate_totp_hotp "$tpm_owner_password"; then
715+
return 1
716+
fi
717+
718+
if [ -s /boot/kexec_key_devices.txt ] || [ -s /boot/kexec_key_lvm.txt ]; then
719+
DEBUG "TPM reset successful: auto-resealing TPM Disk Unlock Key (DUK)"
720+
reseal_tpm_disk_decryption_key || prompt_missing_gpg_key_action
721+
fi
632722
else
633723
echo "Returning to the main menu"
634724
fi

initrd/bin/kexec-insert-key

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ if [ -r "$TMP_KEY_LVM" ]; then
2424
if [ -z "$TMP_KEY_LVM" ]; then
2525
die "No LVM volume group defined for activation"
2626
fi
27-
lvm vgchange -a y $VOLUME_GROUP ||
27+
run_lvm vgchange -a y $VOLUME_GROUP ||
2828
die "$VOLUME_GROUP: unable to activate volume group"
2929
fi
3030

initrd/bin/kexec-save-default

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ PRIMHASH_FILE="$paramsdir/kexec_primhdl_hash.txt"
3838
KEY_DEVICES="$paramsdir/kexec_key_devices.txt"
3939
KEY_LVM="$paramsdir/kexec_key_lvm.txt"
4040

41-
lvm_suggest=$(lvm vgscan 2>/dev/null | awk -F '"' {'print $1'} | tail -n +2)
41+
lvm_suggest=$(run_lvm vgscan 2>/dev/null | awk -F '"' {'print $1'} | tail -n +2)
4242
num_lvm=$(echo "$lvm_suggest" | wc -l)
4343
if [ "$num_lvm" -eq 1 ] && [ -n "$lvm_suggest" ]; then
4444
lvm_volume_group="$lvm_suggest"

initrd/bin/kexec-save-key

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ paramsdir="${paramsdir%%/}"
4040
DEBUG "kexec-save-key prior of last override: paramsdir: $paramsdir, paramsdev: $paramsdev, lvm_volume_group: $lvm_volume_group"
4141

4242
if [ -n "$lvm_volume_group" ]; then
43-
lvm vgchange -a y $lvm_volume_group ||
43+
run_lvm vgchange -a y $lvm_volume_group ||
4444
die "Failed to activate the LVM group"
4545
for dev in /dev/$lvm_volume_group/*; do
4646
key_devices="$key_devices $dev"

initrd/bin/kexec-seal-key

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ if [ -r "$KEY_LVM" ]; then
5555
if [ -z "$VOLUME_GROUP" ]; then
5656
die "No LVM volume group defined for activation"
5757
fi
58-
lvm vgchange -a y $VOLUME_GROUP ||
58+
run_lvm vgchange -a y $VOLUME_GROUP ||
5959
die "$VOLUME_GROUP: unable to activate volume group"
6060
else
6161
DEBUG "No LVM volume group defined for activation"

initrd/bin/oem-factory-reset

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1245,8 +1245,8 @@ else
12451245
generate_OEM_gpg_keys
12461246
fi
12471247

1248-
# Obtain GPG key ID
1249-
GPG_GEN_KEY=$(gpg --list-keys --with-colons | grep "^fpr" | cut -d: -f10 | head -n1)
1248+
# Obtain GPG key ID without printing trustdb maintenance chatter to console
1249+
GPG_GEN_KEY=$(gpg --list-keys --with-colons 2>/dev/null | grep "^fpr" | cut -d: -f10 | head -n1)
12501250
#Where to export the public key
12511251
PUBKEY="/tmp/${GPG_GEN_KEY}.asc"
12521252

@@ -1408,11 +1408,26 @@ while true; do
14081408
done
14091409

14101410
## all done -- reboot
1411-
whiptail --msgbox "
1412-
OEM Factory Reset / Re-Ownership has completed successfully\n\n
1413-
After rebooting, you will need to generate new TOTP/HOTP secrets\n
1414-
when prompted in order to complete the setup process.\n\n
1415-
Press Enter to reboot.\n" \
1411+
if [ "${CONFIG_TPM_DISK_UNLOCK_KEY:-n}" = "y" ]; then
1412+
boot_next_steps="Then open: Options -> Boot Options -> Show OS boot menu
1413+
and set a new default boot option.
1414+
This step also configures/reseals the TPM Disk Unlock Key (DUK).
1415+
"
1416+
else
1417+
boot_next_steps="Then open: Options -> Boot Options -> Show OS boot menu
1418+
and set a new default boot option.
1419+
"
1420+
fi
1421+
1422+
completion_msg="OEM Factory Reset / Re-Ownership has completed successfully
1423+
1424+
After rebooting, you will need to generate new TOTP/HOTP secrets
1425+
when prompted in order to complete the setup process.
1426+
1427+
${boot_next_steps}
1428+
Press Enter to reboot."
1429+
1430+
whiptail --msgbox "${completion_msg}" \
14161431
$HEIGHT $WIDTH --title "OEM Factory Reset / Re-Ownership Complete"
14171432

14181433
# Clean LUKS secrets

0 commit comments

Comments
 (0)