Skip to content

Make credential tamper test detect HMAC coverage gaps - #67

Open
Toporin wants to merge 1 commit into
BryanJacobs:mainfrom
Toporin:patch-tamper-resistant-creds-test
Open

Toporin wants to merge 1 commit into
BryanJacobs:mainfrom
Toporin:patch-tamper-resistant-creds-test

Conversation

@Toporin

@Toporin Toporin commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

The old test_creds_are_tamper_resistant could not have caught the credential HMAC coverage bug fixed in eb2e0d1 ("HMAC over entire credential"): it passed as long as one of five mutations per byte was rejected, and silently ignored accepted forgeries. Mutations in the unauthenticated region were still rejected ~99% of the time anyway, because CBC decryption garbles the credProtect byte, which then fails policy checks - indistinguishable from MAC rejection at the CTAP level.

Rewrite the test to:

  • fail immediately if any mutated credential is accepted
  • flip every single bit of every byte (the old mod-128 mutation never touched the high bit), plus random nonzero XOR masks
  • run 32 mutations per byte instead of 5

Since a black-box client cannot distinguish MAC rejection from policy-check rejection, detection remains statistical: each mutation of an unauthenticated byte is accepted with probability ~1/128, so with 32 mutations over a 14-byte gap the miss probability is about 3%. Comments in the test document this and warn against adding pinAuth or making the credential discoverable, both of which would weaken detection.

Verified by temporarily reverting the applet to the pre-eb2e0d1 HMAC length: the new test fails with a forgery accepted at byte 85, inside the formerly unauthenticated region (bytes 82-95). It passes against the fixed applet.

The old test_creds_are_tamper_resistant could not have caught the
credential HMAC coverage bug fixed in eb2e0d1 ("HMAC over entire
credential"): it passed as long as one of five mutations per byte was
rejected, and silently ignored accepted forgeries. Mutations in the
unauthenticated region were still rejected ~99% of the time anyway,
because CBC decryption garbles the credProtect byte, which then fails
policy checks - indistinguishable from MAC rejection at the CTAP
level.

Rewrite the test to:

- fail immediately if any mutated credential is accepted
- flip every single bit of every byte (the old mod-128 mutation never
 touched the high bit), plus random nonzero XOR masks
- run 32 mutations per byte instead of 5

Since a black-box client cannot distinguish MAC rejection from
policy-check rejection, detection remains statistical: each mutation
of an unauthenticated byte is accepted with probability ~1/128, so
with 32 mutations over a 14-byte gap the miss probability is about
3%. Comments in the test document this and warn against adding
pinAuth or making the credential discoverable, both of which would
weaken detection.

Verified by temporarily reverting the applet to the pre-eb2e0d1 HMAC
length: the new test fails with a forgery accepted at byte 85, inside
the formerly unauthenticated region (bytes 82-95). It passes against
the fixed applet.
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.

1 participant