initrd: fix TPM1 counter auth regression (#2068)#2117
Conversation
There was a problem hiding this comment.
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” aftertakeownand 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.
b20be90 to
a5a2ebb
Compare
|
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 |
- 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>
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>
|
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. |
There was a problem hiding this comment.
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 intpm1_counter_increment(owner-auth retry when-pwdcis 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-pwdcor 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)."
Summary
Fixes a TPM1 counter auth regression (PR #2068) where
increment_tpm_counterwas 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 persistentTPM_AUTHFAIL.Per TCG TPM Main Spec Part 3,
TPM_CreateCounteruses owner auth (-pwdo) butTPM_IncrementCounteruses 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 throughforceclearon some implementations.Changes
initrd/bin/tpmr.shtpm1_counter_increment(): detect explicit-pwdc ''and calltpm counter_incrementdirectly, bypassing_tpm_auth_retry. Non-empty or absent-pwdcfalls through to owner-auth retry path for migration of counters created by pre-fix code.initrd/etc/functions.shcheck_tpm_counter(): create counters with-pwdc ''instead of owner passphraseincrement_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 consistencydoc/tpm.md