Skip to content

test: verify CTAP2 signature round-trip and pin CBOR/COSE golden vectors #259

@AlfioEmanueleFresta

Description

@AlfioEmanueleFresta

What's wrong

The end-to-end tests in libwebauthn-tests drive the virtual authenticator (get_virtual_device() in libwebauthn-tests/src/virt/mod.rs) through make-credential and get-assertion, but never cryptographically verify a signature. tests/basic_ctap2.rs and tests/prf.rs only assert that the ceremony completes and extension outputs are present.

There is also no golden-vector test pinning the canonical CTAP2 wire bytes. Ctap2MakeCredentialRequest and Ctap2GetAssertionRequest (libwebauthn/src/proto/ctap2/model/) are encoded with serde-indexed through the canonical encoder in cbor/serde.rs, and public keys with cosey, but no test pins the exact bytes. Existing tests in cose.rs and model/make_credential.rs check read_alg / to_spki and relative key order only.

Why it matters

The client returns the device's authenticatorData verbatim (AuthenticatorData::raw / to_response_bytes() in libwebauthn/src/fido.rs, #249) and emits make-credential extensions in canonical CBOR order (#243), because re-encoding or reordering the signed bytes makes a relying party reject a valid signature. A regression in either path, or a byte-layout change in cosey or serde-indexed, would silently break verification while every current test still passes. The virtual authenticator embeds a real attestation cert and key (fido-cert.der, fido-key.trussed) and signs in-process, so the round-trip needs no hardware. Its default ES256 is verifiable with the already-present p256 crate.

What to do

  • Add a virtual-authenticator round-trip test in libwebauthn-tests: register a credential, take the COSE public key from the registration authenticatorData, run an assertion, recompute SHA-256(clientDataJSON), and verify the assertion signature over authenticatorData || clientDataHash (WebAuthn L3 §6.3.3).
  • Exercise the raw-bytes path from fix(ctap2): preserve raw authenticatorData bytes #249 with an extension that writes into the signed authenticatorData. hmac-secret (PRF) already works end to end and returns its output in the assertion authData. credProtect at registration is an alternative. Use whichever GetInfo advertises.
  • Verify the attestation path: parse the client-emitted attestationObject, verify the packed attStmt signature over authData || clientDataHash with the embedded cert's public key (WebAuthn L3 §8.2), and confirm its authData is byte-identical to the surfaced authenticatorData.
  • Add golden-vector tests in libwebauthn pinning, as fixed hex, the canonical CBOR of a fixed Ctap2MakeCredentialRequest and Ctap2GetAssertionRequest (the public CborRequest path in cbor/request.rs) and the COSE_Key of a fixed cosey::PublicKey. They must fail if map ordering drifts or either dependency changes its byte layout.
  • Enable the verification deps: the ecdsa feature on p256 and sha2. The attestation cert parses with x509-parser, already a core-crate dependency.

Pointers

  • attestationObject assembly: build_attestation_object / to_idl_model in ops/webauthn/make_credential.rs.
  • Assertion signature: Assertion in ops/webauthn/get_assertion.rs.
  • Attestation statement shapes: Ctap2AttestationStatement / PackedAttestationStmt in proto/ctap2/model/get_assertion.rs.

Metadata

Metadata

Assignees

No one assigned

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions