Skip to content

fix: enforce WebAuthn assertion type and User-Present flag - #118

Open
alberto-crossmint wants to merge 1 commit into
mainfrom
fix/webauthn-type-and-user-present
Open

fix: enforce WebAuthn assertion type and User-Present flag#118
alberto-crossmint wants to merge 1 commit into
mainfrom
fix/webauthn-type-and-user-present

Conversation

@alberto-crossmint

Copy link
Copy Markdown
Collaborator

Summary

The WebAuthn verifier (auth/signers/webauthn.rs) was missing two spec-mandated checks:

Spec Check Fix
WebAuthn §7.2.11 clientDataJSON.type == "webauthn.get" Add ty field to ClientDataJson and reject anything other than "webauthn.get"
WebAuthn §6.1 User-Present (UP) bit set in authenticator_data flags Reject if authenticator_data[32] & 0x01 == 0

Both checks are cheap (one string compare, one bit test) and live in the existing fail-fast block ahead of the ECDSA verification.

Why this matters

Type check: A signature produced during a navigator.credentials.create() ceremony is signed over a clientDataJSON whose type is "webauthn.create". Without the type check, that signature could be replayed against this contract if its challenge happened to match a Stellar transaction hash. The attacker scenario requires luring the victim into a registration ceremony on a hostile origin, but the defense is essentially free and removes a class of cross-ceremony replay risk that the WebAuthn spec specifically calls out.

UP flag: Modern authenticators almost always set the UP bit, but the spec is clear that the verifier must require it — without enforcement the contract trusts whatever the authenticator reports, including assertions that occurred without any user interaction.

Test plan

  • test_webauthn_wrong_type_rejected — assertion with type: "webauthn.create" returns Err(InvalidWebauthnClientDataJson).
  • test_webauthn_wrong_type_rejected_end_to_end — same, through try_invoke_contract_check_auth.
  • test_webauthn_missing_user_present_flag_rejectedauthenticator_data[32] = 0x00 returns Err(InvalidWebauthnClientDataJson).
  • All existing webauthn tests still pass — the existing test_utils::WebauthnTestSigner already produces type: "webauthn.get" and flags: 0x01 so no regression.
  • Full suite: 129 passed, 0 failed (3 new tests, no regressions).

Out of scope

  • clientDataJSON.origin and authenticator_data rpIdHash enforcement. Both require a per-signer expected value (origin string / RP ID), which means a WebauthnSigner schema change and migration. Tracked for a future v3 schema bump.
  • Counter monotonicity. WebAuthn lets the RP enforce it; in our model each transaction signs a fresh challenge so replay isn't possible without a counter, but spec compliance would add it. Tracked separately.

Backwards compatibility

Real-world authenticators producing type: "webauthn.get" with UP set (overwhelming majority) are unaffected. Any client previously relying on the lax verification behavior — sending non-"webauthn.get" ceremonies or zero flags — will now be rejected. A grep across known integrations is suggested before rollout.

Two spec-mandated checks were missing from the WebAuthn verifier
(W3C WebAuthn §6.1, §7.2.11):

1. clientDataJSON.type must be "webauthn.get" for an authentication
   assertion. Without this, a signature produced during a
   navigator.credentials.create() ceremony (type "webauthn.create")
   could be replayed against the smart account if its challenge
   happened to match a transaction hash.

2. The User-Present (UP) bit in authenticator_data flags must be set.
   Without this, the contract trusted whatever the authenticator
   reported, accepting assertions where the user never interacted.

Both checks are cheap (string compare, single bit test) and run in
the existing fail-fast block before the ECDSA verify.

Out of scope: clientDataJSON.origin and authenticator_data rpIdHash
enforcement. Both require storing per-signer expected values and
amount to a WebauthnSigner schema change tracked separately.
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