Files
mpc/pkg
zeekay cd9814bd27 fix(webauthn): F4 LAX tightening — credentialHash floor + both-must-match
Red round 4 flagged (F4, MEDIUM-HIGH) that the previous LAX binding
mode in VerifyLiveness permitted a cross-ceremony replay: an attacker
who observed a fresh WebAuthn challenge could trick SecureGate into
signing a liveness envelope binding to the challenge alone (no
credentialHash), then replay that envelope against any enrollment for
the same userId that happened to reuse the challengeId-only
expectation.

Two LAX-only tightenings in pkg/webauthn/liveness.go (STRICT already
enforced the missing-both rejection; this sharpens LAX):

1. credentialHash is a hard floor. If the server expects
   credentialHash and the envelope omits it, reject with
   "credentialHash required in LAX mode — envelope without
   credentialHash is replay-vulnerable". challengeId alone cannot
   satisfy the binding requirement in LAX — challengeId can be
   observed and replayed; credentialHash commits to the specific
   pubKey being enrolled.

2. When the envelope supplies BOTH credentialHash AND challengeId AND
   the server expects both, require BOTH to match. No more "either-or"
   loophole. The existing mismatch-rejects guards already caught
   individual mismatches; this closes the pathological
   both-supplied-one-blank-expected corner.

handleBiometricEnroll always commits ExpectedCredentialHash (derived
from the enrollment pubKey via sha256), so the LAX floor always
engages in practice for WebAuthn enrollment — this hardens what
is already the exercised path.

Test coverage (pkg/webauthn/liveness_test.go):

- TestLiveness_R38_LaxRejectsMissingCredentialHash — primary F4 gate:
  envelope with only challengeId, missing credentialHash → reject
- TestLiveness_R38_LaxAcceptsBothMatch — happy path (extended envelope
  after SecureGate rollout)
- TestLiveness_R38_LaxRejectsMismatchedCredentialHashBothPresent
- TestLiveness_R38_LaxRejectsMismatchedChallengeIDBothPresent
- TestLiveness_R38_LaxAcceptsCredentialHashOnly — current SecureGate
  envelope shape still accepted
- TestLiveness_R38_LaxRejectsMissingBindingWhenCredentialHashExpected —
  replaces the old lax-warns-missing path
- TestLiveness_R38_LaxWarnsOnMissing_ChallengeIDOnly — warn path still
  available when credentialHash is not expected

Full pkg/webauthn suite green (18/18). pkg/api regression tests pass.
pkg/custody pre-existing failures unrelated to this change (SQLite
json_extract missing, blame predates F4).

LLM.md entry R35 documents the full set. Cut v1.7.1 on merge.
2026-04-18 18:05:11 -07:00
..
2024-09-21 20:52:40 +07:00
2025-08-02 22:27:51 -05:00