Skip to content

initrd: fix TPM1 counter auth regression (#2068)#2117

Open
tlaurion wants to merge 1 commit into
masterfrom
tpm1_fixes
Open

initrd: fix TPM1 counter auth regression (#2068)#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 counter auth regression (PR #2068) where increment_tpm_counter was changed from hardcoded -pwdc '' (empty counter auth) to -pwdc "${tpm_passphrase:-}" (owner passphrase), while counters continued to be created with -pwdc ''. This caused every increment to compute SHA1(owner_pass) against a counter created with SHA1(""), producing persistent TPM_AUTHFAIL.

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.

The repeated auth failures (3 per boot) triggered TPM 1.2 dictionary-attack lockout (TPM_DEFEND_LOCK_RUNNING), which persisted through forceclear on some implementations.

Changes

initrd/bin/tpmr.sh

  • tpm1_counter_increment(): detect explicit -pwdc '' and call tpm counter_increment directly, bypassing _tpm_auth_retry. Non-empty or absent -pwdc falls through to owner-auth retry path for migration of counters created by pre-fix code.

initrd/etc/functions.sh

  • check_tpm_counter(): create counters with -pwdc '' instead of owner passphrase
  • increment_tpm_counter(): increment with -pwdc '' instead of owner passphrase; counter_create fallback uses -pwdc ''

initrd/bin/oem-factory-reset.sh

  • -pwdc "${TPM_PASS:-}"-pwdc '' for consistency

doc/tpm.md

  • Documented TPM1 counter auth requirements under new "TPM1 counter auth" subsection

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 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 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 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 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.

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 5 changed files in this pull request and generated 3 comments.

Comment thread initrd/etc/functions.sh Outdated
Comment thread doc/tpm.md Outdated
Comment thread doc/tpm.md Outdated
tlaurion added a commit that referenced this pull request May 17, 2026
- Fix TPM2 time remaining table entry: estimate is derived from
  LOCKOUT_COUNTER vs MAX_AUTH_FAIL times LOCKOUT_INTERVAL, not
  LOCKOUT_RECOVERY (which is the lockout-auth-blocked-after-failure
  timer, not the remaining-until-unlock)
- Reword migration WARN: 'older Heads version' not 'older firmware'
  (the migration case is caused by previous Heads code, not platform
  firmware)
- Remove fragile PR #2117 reference from preventing-future-lockouts
  section: describe the fix generically (restoring empty counter auth)
  so the doc is correct regardless of branch context

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
@tlaurion tlaurion changed the title initrd: fix TPM1 counter auth regression and defend lock cascade failure initrd: add TPM DA lockout gating, counter auth fix, bad_auth tooling and documentation May 17, 2026
@tlaurion tlaurion marked this pull request as draft May 17, 2026 18:35
PR #2068 introduced a regression where `increment_tpm_counter`
was changed from hardcoded `-pwdc ''` (empty counter auth per
TCG spec) to `-pwdc "${tpm_passphrase:-}"` (owner passphrase),
while counters continued to be created with `-pwdc ''`. This
caused every increment to compute SHA1(owner_pass) against a
counter created with SHA1(""), producing persistent TPM_AUTHFAIL.

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.

The repeated auth failures (3 per boot) accumulated the TPM's
dictionary attack (DA) failedTries counter until lockout was
reached (~10 boots = 30 failures). Users reported "hours of
waiting" on affected hardware. On some implementations the DA
state persisted through tpm forceclear.

Fix: restore TCG-compliant empty counter auth:
- tpm1_counter_increment: detect explicit -pwdc '' and call tpm
  directly, bypassing _tpm_auth_retry. Non-empty or absent -pwdc
  falls through to owner-auth retry path for migration of counters
  created by pre-fix code.
- check_tpm_counter: create counters with -pwdc '' instead of
  owner passphrase.
- increment_tpm_counter: increment with -pwdc '' instead of
  owner passphrase; counter_create fallback uses empty auth.
- oem-factory-reset.sh: create counters with -pwdc ''.

Signed-off-by: Thierry Laurion <insurgo@riseup.net>
@tlaurion tlaurion changed the title initrd: add TPM DA lockout gating, counter auth fix, bad_auth tooling and documentation initrd: fix TPM1 counter auth regression (#2068) May 20, 2026
@tlaurion tlaurion requested a review from Copilot May 20, 2026 14:57
@tlaurion
Copy link
Copy Markdown
Collaborator Author

tlaurion commented May 20, 2026

The rabbit hole of tpm1 da lockout went too far. This pr will just fix regression and on later time i will hopefully add diagnostics and helpers to test further issues.

on x230, i have a STM (not infineon) that doesn't support proper tpm da policy getcap, so i cannot ask what is the threshold and even counter; which required me to modify tpm increment handler where da lockout is the only place in code to check if tpm is in da defend.

I was not aware (or forgot) that older devices like x230 came with a not compliant tpm 1.2 spec revision, which complicates testing by a big margin. Let's just do the right thing from tpm reset up to increment counter for now and fix #2068 regression.

This PR now includes only the regression fix.

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.

Comments suppressed due to low confidence (1)

initrd/etc/functions.sh:2104

  • The TPM1 increment path now always calls tpmr.sh counter_increment ... -pwdc ''. That fixes the empty-auth regression, but it also prevents the new migration logic in tpm1_counter_increment (owner-auth retry when -pwdc is absent or non-empty) from ever being exercised by this boot flow. As a result, systems with an existing TPM1 counter created with non-empty counter auth (e.g., from older factory-reset flows) will hit the "readable but not incrementable" fatal path and require a TPM reset even if the correct passphrase is available. Consider a one-time fallback: on TPM1 increment failure with empty auth, retry once via the owner-auth path (e.g., omit -pwdc or pass a non-empty cached/passsed value) before declaring the counter non-incrementable.
		# actual command stderr is already handled inside DO_WITH_DEBUG.
		if (
			set -o pipefail
			DO_WITH_DEBUG --mask-position 5 \
				tpmr.sh counter_increment -ix "$counter_id" -pwdc '' \
					2>/dev/null | tee /tmp/counter-"$counter_id" >/dev/null
		); then
			increment_ok="y"
		fi
	fi

	if [ "$increment_ok" != "y" ]; then
		if [ "$counter_present" = "y" ]; then
			mkdir -p /tmp/secret || true
			: >"$reset_required_marker"
			DIE "TPM rollback counter '$counter_id' is readable but not incrementable. Reset TPM from GUI (Options -> TPM/TOTP/HOTP Options -> Reset the TPM)."

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants