Skip to content

initrd: fix TPM1 counter auth regression and defend lock cascade failure#2117

Open
tlaurion wants to merge 1 commit into
masterfrom
tpm1_fixes
Open

initrd: fix TPM1 counter auth regression and defend lock cascade failure#2117
tlaurion wants to merge 1 commit into
masterfrom
tpm1_fixes

Conversation

@tlaurion
Copy link
Copy Markdown
Collaborator

@tlaurion tlaurion commented May 14, 2026

Summary

Fixes a TPM1 regression introduced by PR #2068 (tpm_reseal_ux-integrity_report-detect_disk_and_tpm_swap) where increment_tpm_counter was changed from hardcoded -pwdc '' (empty counter auth) to -pwdc "${tpm_passphrase:-}" (owner passphrase from cache/prompt), but check_tpm_counter — when called from kexec-sign-config.sh without a $3 passphrase argument — continued to create counters with -pwdc "". This caused every counter increment to compute SHA1(owner_pass) while the counter was created with SHA1(""), producing persistent TPM_AUTH_FAIL.

Per TCG TPM Main Spec Part 3, TPM_CreateCounter uses owner auth (-pwdo) but TPM_IncrementCounter uses the counter's own authData, not the owner password. The correct design for Heads' rollback counter is empty auth: rollback security comes from the signed /boot/kexec_rollback.txt and TPM sealing, not counter access control.

The repeated auth failures (3 per boot x ~5 boots via the _tpm_auth_retry loop) triggered TPM 1.2 dictionary-attack lockout (TPM_DEFEND_LOCK_RUNNING), which persisted through forceclear on some implementations, causing tpm takeown to fail during TPM reset — a cascade failure from the counter auth mismatch.

Note: the TPM2 path within increment_tpm_counter was not affected — it correctly used -pwdc "" (empty index auth first, owner auth fallback), consistent with how tpm2 nvdefine creates counters without explicit NV index auth.

Changes

initrd/bin/tpmr.sh — auth detection + defend lock recovery + pass-through fix

  • Add 'defend' and '0x98e|0x149' to all auth detection grep patterns so defend lock and TPM2 RC codes are retried rather than fatal
  • tpm1_reset(): detect "defend lock" in takeown output and cycle physical presence to clear the lock before retrying
  • tpm1_counter_increment(): detect -pwdc '' and call tpm directly (bypassing _tpm_auth_retry which injected owner passphrase). Uses || return to prevent set -e from killing the script on expected auth failure

initrd/etc/functions.sh — counter auth fix + migration fallback

initrd/bin/oem-factory-reset.sh — consistency fix

  • counter_create: -pwdc '' instead of -pwdc "${TPM_PASS:-}" — OEM factory reset now also creates counters with empty auth

doc/tpm.md — documentation

  • Document TPM1 boot chain, tpmtotp tool selection, auth retry patterns, defend lock recovery, and physical presence

Testing

Test scenarios to verify:

Copilot AI review requested due to automatic review settings May 14, 2026 19:09
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR improves Heads’ TPM error handling for TPM1 by correctly classifying tpmtotp “Defend lock running” output as an authorization-related failure (so it doesn’t immediately hard-fail), and adds a recovery path in tpm1_reset() to attempt clearing TPM1 defend-lock after forceclear by cycling physical presence.

Changes:

  • Extend auth-failure grep patterns to include defend (and unify inclusion of TPM2 auth hex codes in the shared retry helper).
  • Enhance tpm1_reset() to detect “defend lock” after takeown and retry after cycling physical presence.
  • Expand TPM documentation to describe tool selection, auth retry detection, and TPM1 defend-lock behavior.

Reviewed changes

Copilot reviewed 1 out of 2 changed files in this pull request and generated 3 comments.

File Description
initrd/bin/tpmr.sh Treat “defend lock” as auth-related and add TPM1 defend-lock recovery logic during reset.
doc/tpm.md Document TPM toolchain selection and the updated auth retry / defend-lock behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread doc/tpm.md Outdated
Comment thread doc/tpm.md Outdated
Comment thread doc/tpm.md
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 2 changed files in this pull request and generated 2 comments.

Comment thread doc/tpm.md Outdated
Comment thread doc/tpm.md Outdated
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 2 changed files in this pull request and generated 1 comment.

Comment thread doc/tpm.md
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 1 out of 2 changed files in this pull request and generated no new comments.

@notgivenby
Copy link
Copy Markdown
Contributor

The fix did not work…will copy logs.

@tlaurion
Copy link
Copy Markdown
Collaborator Author

The fix did not work…will copy logs.

Found the bug and where the regression comes from. Damn that one was not easy. Pushing fix and updating the other pr

@tlaurion tlaurion requested a review from Copilot May 16, 2026 01:08
@tlaurion tlaurion changed the title initrd/bin/tpmr.sh: fix TPM1 auth failure detection and defend lock recovery initrd: fix TPM1 counter auth regression and defend lock cascade failure May 16, 2026
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated 1 comment.

Comment thread initrd/etc/functions.sh
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 3 changed files in this pull request and generated no new comments.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 2 out of 4 changed files in this pull request and generated no new comments.

PR #2068 (tpm_reseal_ux-integrity_report-detect_disk_and_tpm_swap,
merged at d3d8053) changed increment_tpm_counter from hardcoded
-pwdc '' (empty counter auth) to -pwdc "${tpm_passphrase:-}" (owner
passphrase from cache/prompt), but left check_tpm_counter using empty
-pwdc when called from kexec-sign-config.sh without a $3 passphrase
argument.  This caused every counter increment to compute
SHA1(owner_pass) while the counter was created with SHA1("") -
persistent TPM_AUTH_FAIL.

Per TCG TPM Main Spec Part 3, TPM_CreateCounter uses owner auth
(-pwdo) but TPM_IncrementCounter uses the counter's own authData,
not the owner password.  The correct design for Heads' rollback
counter is empty auth: rollback security comes from the signed
/boot/kexec_rollback.txt and TPM sealing, not counter access control.

The repeated auth failures (3 per boot x ~5 boots via the
_tpm_auth_retry loop) triggered TPM 1.2 dictionary-attack lockout
(TPM_DEFEND_LOCK_RUNNING), which persisted through forceclear on
some implementations, causing tpm takeown to fail and TPM reset to
abort - a cascade failure from the counter auth mismatch.

Changes:
- initrd/bin/tpmr.sh (_tpm_auth_retry, tpm2_counter_inc, tpm2_seal,
  tpm1_seal): add 'defend' and '0x98e|0x149' to auth detection grep
  patterns so defend lock and TPM2 RC codes are treated as retryable
  auth failures rather than fatal errors
- initrd/bin/tpmr.sh (tpm1_reset): detect defend lock after takeown
  failure and cycle physical presence to clear the lock state before
  retrying; full AC power cycle remains the fallback if software
  presence is insufficient
- initrd/bin/tpmr.sh (tpm1_counter_increment): detect -pwdc '' and
  call tpm directly, bypassing _tpm_auth_retry which injected the
  owner passphrase.  Use || return to survive set -e on expected
  auth failure.
- initrd/etc/functions.sh (check_tpm_counter): pass -pwdc '' instead
  of -pwdc "${tpm_passphrase:-}" so counters use SHA1("") per TCG
  spec.  Document that $3 is intentionally ignored.
- initrd/etc/functions.sh (increment_tpm_counter): try -pwdc '' first
  for TPM1.  If that fails on a readable counter (created by PR #2068
  era code), prompt for owner passphrase and retry as migration
  fallback with clear WARN explaining the one-time migration and
  TPM reset option.
- initrd/etc/functions.sh (increment_tpm_counter): remove the
  TPM1-specific owner-passphrase prompt block added by PR #2068
- initrd/etc/functions.sh (increment_tpm_counter): DIE-path fallback
  counter_create: -pwdc '' for consistency
- initrd/bin/oem-factory-reset.sh: counter_create -pwdc '' for
  consistency with the empty-auth design
- doc/tpm.md: document TPM1 boot chain, tpmtotp tool selection,
  auth retry patterns, defend lock recovery, and physical presence

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants