Skip to content

fido4vc-jcs-2026 Cryptosuite

This page is the normative specification for the fido4vc-jcs-2026 cryptosuite. Implementers should be able to produce interoperable signers and verifiers from this page alone.

fido4vc-jcs-2026 is a W3C VC Data Integrity cryptosuite. It maps a FIDO2 / WebAuthn assertion produced by an external authenticator into a DataIntegrityProof value attached to a Verifiable Presentation (or, in principle, any signed JSON-LD document).

Verify-only by design. Signing is performed outside a cryptosuite library — by a FIDO authenticator over a WebAuthn ceremony. The cryptosuite specifies how to construct the WebAuthn challenge from the document so the resulting assertion can be embedded as a proof and later verified.

PropertyValue
Cryptosuite namefido4vc-jcs-2026
Proof typeDataIntegrityProof
CanonicalizationJSON Canonicalization Scheme (RFC 8785)
Hash algorithmSHA-256 (FIPS 180-4)
Signature algorithmECDSA over P-256 with SHA-256 (ES256, FIPS 186-5, RFC 7518 §3.4)
Signing mechanismWebAuthn navigator.credentials.get() ceremony
Verification method typeJsonWebKey with kty: "EC", crv: "P-256"
DID methodsAny DID method whose resolution yields a conforming JsonWebKey (tested with did:jwk, did:key, did:web)

The cryptosuite name follows the W3C VC-DI naming convention <base>-<canonicalization>-<year>: fido4vc identifies the underlying signing mechanism family, jcs identifies the canonicalization, 2026 is the year of registration.

Implementations conforming to this specification MUST:

  1. Use RFC 8785 (JCS) for canonicalization. Implementations SHOULD use Erdtman’s reference implementation (canonicalize on npm, io.github.erdtman:java-json-canonicalization on Maven Central) to ensure bit-identical canonicalization across language bindings.
  2. Use SHA-256 for all hashing operations.
  3. Recognize proofValue as a multibase-encoded string (prefix u, base64url without padding per RFC 4648 §5) whose decoded payload is a CBOR-encoded (RFC 8949) 3-element byte-string array [authenticatorData, signature, clientDataJSON], in that fixed field order.
  4. Validate the WebAuthn challenge binding before checking the signature.
  5. Dereference proof.verificationMethod per VC-DI §4 — Retrieve Verification Method and CID 1.0 §3.3 to a JsonWebKey whose publicKeyJwk has kty: "EC" and crv: "P-256". The cryptosuite is DID-method-agnostic; any URL whose dereferencing yields such a key MAY be used (e.g., did:jwk, did:key, did:web).
  6. Set proof.proofPurpose to "authentication". WebAuthn is fundamentally an authentication primitive; verifiers MUST reject proofs whose proofPurpose has any other value.

Implementations MAY:

  1. Support additional EC curves beyond P-256 (e.g., P-384) via extension. Such implementations MUST register a distinct cryptosuite name.
  2. Support attested authenticators (validate attestationObject chain). The base spec leaves attestation out-of-scope.

This cryptosuite specifies how to verify WebAuthn assertions; it does not perform credential creation. Before Proof Serialization can run, a WebAuthn credential MUST already exist on the user’s authenticator. The credential is created out-of-band, prior to any signing ceremony, via the WebAuthn navigator.credentials.create() ceremony.

To ensure the resulting assertions are verifiable under fido4vc-jcs-2026, the registration ceremony MUST constrain the credential to this cryptosuite’s signature algorithm. The PublicKeyCredentialCreationOptions.pubKeyCredParams array MUST contain at least one entry whose:

The array SHOULD NOT contain entries for algorithms unsupported by this cryptosuite (e.g., -257 RS256, -8 EdDSA). If the authenticator selects an unsupported algorithm during credential creation, the resulting assertions will not verify and the credential will be unusable for fido4vc-jcs-2026.

Other aspects of credential creation — RP/user identifier provisioning, attestation conveyance, resident-key requirements, authenticator-selection criteria — are WebAuthn-layer concerns and out of scope of this cryptosuite. See W3C WebAuthn Level 3 §5.1.3 for the full registration ceremony specification.

This algorithm configures the fido4vc-jcs-2026 cryptographic suite to be used by the Add Proof and Verify Proof functions of Verifiable Credential Data Integrity 1.0. The algorithm takes an options object (map options) as input and returns a cryptosuite instance (struct cryptosuite).

  1. Initialize cryptosuite to an empty struct.
  2. If options.type does not equal DataIntegrityProof, return cryptosuite.
  3. If options.cryptosuite equals fido4vc-jcs-2026, then:
  4. Return cryptosuite.

This cryptosuite uses JSON Canonicalization Scheme (JCS, RFC 8785) for canonicalization, SHA-256 for hashing, and ECDSA over P-256 with SHA-256 (ES256) executed inside a FIDO2/WebAuthn authenticator for signature production. The proof value packages the WebAuthn assertion (authenticatorData, signature, clientDataJSON) into a deterministic CBOR blob (RFC 8949 §4.2) encoded as a multibase base64url string (prefix u), conforming to W3C VC-DI §2.1.

The following algorithm specifies how to transform an unsecured input document into a transformed document ready to be provided as input to the hashing algorithm.

Required inputs are an unsecured data document (map unsecuredDocument) and transformation options (map options). The transformation options MUST contain a type identifier (type) for the cryptographic suite and a cryptosuite identifier (cryptosuite). A transformed data document (byte sequence) is produced as output. Whenever this algorithm encodes strings, it MUST use UTF-8 encoding.

  1. If options.type is not DataIntegrityProof or options.cryptosuite is not fido4vc-jcs-2026, an error MUST be raised and SHOULD convey an error type of PROOF_TRANSFORMATION_ERROR.
  2. Let transformedDocument be the result of applying RFC 8785 (JCS) canonicalization to unsecuredDocument, producing a UTF-8 byte sequence.
  3. Return transformedDocument as the transformed data document.

The following algorithm specifies how to cryptographically hash a transformed data document and a canonical proof configuration into a hash value ready to be provided as input to the proof serialization or proof verification algorithms.

Required inputs are a transformed data document (byte sequence transformedDocument) and a canonical proof configuration (byte sequence canonicalProofConfig). A single hash value (byte sequence hashData) is produced as output.

  1. Let combinedBytes be the concatenation transformedDocument ‖ canonicalProofConfig.
  2. Let hashData be the result of applying SHA-256 to combinedBytes.
  3. Return hashData as the hash data.

The following algorithm specifies how to generate a canonical proof configuration from a set of proof options. The output is used as input to the hashing algorithm.

Required inputs are proof options (map options) and the unsecured data document (map unsecuredDocument). The proof options MUST contain a type identifier (type) and a cryptosuite identifier (cryptosuite). A canonical proof configuration (byte sequence) is produced as output.

  1. Let proofConfig be a clone of options.
  2. If proofConfig.type is not DataIntegrityProof or proofConfig.cryptosuite is not fido4vc-jcs-2026, an error MUST be raised and SHOULD convey an error type of PROOF_GENERATION_ERROR.
  3. If proofConfig.created is set and the value is not a valid [[XMLSCHEMA11-2]] datetime, an error MUST be raised and SHOULD convey an error type of PROOF_GENERATION_ERROR.
  4. Remove the proofValue property from proofConfig, if present.
  5. Set proofConfig.@context to unsecuredDocument.@context.
  6. Let canonicalProofConfig be the result of applying RFC 8785 (JCS) canonicalization to proofConfig.
  7. Return canonicalProofConfig.

The following algorithm specifies how to derive a proof value from hash data. The cryptographic signing operation is performed by a FIDO2/WebAuthn authenticator external to the cryptosuite library; the library prepares the WebAuthn challenge from hashData and packages the authenticator’s response into the proof value.

Required inputs are a hash value (byte sequence hashData) and proof options (map options). A proof value (string proofValue) is produced as output.

  1. Invoke a WebAuthn navigator.credentials.get() ceremony with PublicKeyCredentialRequestOptions containing:
    • challenge set to hashData;
    • the relying party identifier (rpId);
    • the user’s allowCredentials;
    • userVerification set to "required".
  2. Let assertion be the resulting AuthenticatorAssertionResponse, containing:
    • authenticatorData (byte sequence) — the WebAuthn authenticator data structure;
    • clientDataJSON (byte sequence) — the UTF-8 JSON encoding of CollectedClientData, which MUST include a challenge field whose value equals the base64url encoding of challengeBytes;
    • signature (byte sequence) — DER-encoded ECDSA-P256-SHA-256 signature over authenticatorData ‖ SHA-256(clientDataJSON).
  3. Let proofBytes be the deterministic CBOR encoding (RFC 8949 §4.2) of the 3-element byte-string array [authenticatorData, signature, clientDataJSON], in that fixed order.
  4. Let proofValue be the multibase encoding of proofBytes using prefix u (base64url without padding, RFC 4648 §5).
  5. Return proofValue.

The following algorithm specifies how to create a data integrity proof given an unsecured data document.

Required inputs are an unsecured data document (map unsecuredDocument) and a set of proof options (map options). A data integrity proof (map proof), or an error, is produced as output.

  1. Let proof be a clone of options.
  2. Let canonicalProofConfig be the result of running the Proof Configuration algorithm with options and unsecuredDocument passed as parameters.
  3. Let transformedData be the result of running the Transformation algorithm with unsecuredDocument and options passed as parameters.
  4. Let hashData be the result of running the Hashing algorithm with transformedData and canonicalProofConfig passed as parameters.
  5. Let proofValue be the result of running the Proof Serialization algorithm with hashData and options passed as parameters.
  6. Set proof.proofValue to proofValue.
  7. Return proof as the data integrity proof.

The following algorithm specifies how to verify a data integrity proof given a secured data document.

Required input is a secured data document (map securedDocument). The algorithm returns a verification result, a struct whose items are:

When a step below says “an error MUST be raised”, the algorithm MUST return a verification result with verified set to false and verifiedDocument set to Null, and SHOULD convey the specified error type.

  1. Let unsecuredDocument be a copy of securedDocument with the proof property removed.
  2. Let proof be the value of securedDocument.proof.
  3. Validate proof purpose. If proof.proofPurpose is not equal to "authentication", an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR. (This cryptosuite is bound to the WebAuthn authentication primitive; other proof purposes such as assertionMethod are not supported.)
  4. Decode proof value.
    • If proof.proofValue is not a string beginning with the multibase prefix u, an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR.
    • Strip the leading u; base64url-decode the remaining characters to obtain proofBytes.
    • CBOR-decode proofBytes as a 3-element byte-string array [authenticatorData, signature, clientDataJSON]. If the structure does not match, an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR.
  5. Let options be a clone of proof with the proofValue property removed.
  6. Let canonicalProofConfig be the result of running the Proof Configuration algorithm with options and unsecuredDocument passed as parameters.
  7. Let transformedData be the result of running the Transformation algorithm with unsecuredDocument and options passed as parameters.
  8. Let hashData be the result of running the Hashing algorithm with transformedData and canonicalProofConfig passed as parameters.
  9. Parse clientDataJSON. Interpret clientDataJSON as a UTF-8 JSON object. If parsing fails, an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR.
  10. Validate WebAuthn assertion type. If clientDataJSON.type is not equal to "webauthn.get", an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR. (This prevents confusion attacks where a registration-ceremony assertion is substituted for an authentication assertion.)
  11. Bind challenge. If clientDataJSON.challenge does not equal the base64url encoding of hashData, an error MUST be raised and SHOULD convey an error type of INVALID_CHALLENGE_ERROR.
  12. Retrieve verification method. Let publicKey be the public key associated with proof.verificationMethod, retrieved as described in VC-DI §4 — Retrieve Verification Method and CID 1.0 §3.3. The dereferenced object MUST contain a type of JsonWebKey whose publicKeyJwk has kty: "EC" and crv: "P-256"; otherwise an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR.
  13. Verify signature. Let signedBytes be authenticatorData ‖ SHA-256(clientDataJSON). Verify the ECDSA signature against publicKey over signedBytes using ES256. If verification fails, an error MUST be raised and SHOULD convey an error type of PROOF_VERIFICATION_ERROR.
  14. Return a verification result with verified set to true and verifiedDocument set to unsecuredDocument.
  • DER vs. raw ECDSA. WebAuthn authenticators emit DER-encoded ECDSA signatures (ASN.1 SEQUENCE of two INTEGERs). Java’s Signature.getInstance("SHA256withECDSA") accepts DER directly. Node’s WebCrypto subtle.verify requires raw r ‖ s and needs an unwrap step.
  • clientDataJSON parsing. Be defensive about whitespace and field order in clientDataJSON. Per the WebAuthn spec, the authenticator produces a specific format, but extension fields may appear; only challenge is relied on by this cryptosuite.
  • WebAuthn ceremony validation is out of scope of this cryptosuite. Validation of rpId, clientDataJSON.origin, the authenticatorData flags (UV, AT, BE, BS), signature-counter monotonicity, and attestation chain are WebAuthn-layer concerns, not cryptosuite concerns. Verifiers operating inside a full WebAuthn ceremony context SHOULD perform the additional checks described in W3C WebAuthn Level 3 §7.2. The cryptosuite makes one exception: clientDataJSON.type MUST equal "webauthn.get" (enforced in the Verify Proof algorithm) because the check is cheap and prevents a well-known confusion-attack class.
  • proof.challenge and proof.domain. When present in options, these fields are folded into the canonical proof configuration by the Proof Configuration algorithm and therefore become cryptographically bound to the signature via hashData. The WebAuthn challenge is hashData itself, not a separate verifier-supplied nonce.

A small reference fixture is included with the TypeScript reference implementation: tests/fido4vc.test.ts. It captures a single end-to-end WebAuthn ceremony from the reference deployment — the holder DID-JWK, the WebAuthn assertion (authenticatorData, clientData, signature), and the full signed VP.

Cross-implementation determinism. The most likely interoperability hazard is non-RFC-8785-compliant JCS implementations. Implementers SHOULD use the Erdtman references on each platform. Subtle differences in number formatting (notably exponent handling) or escape sequences will silently produce mismatched hashes.

Replay protection. Replay protection is not provided by the cryptosuite. The verifier supplies a challenge in the proof options; replay is prevented by the verifier rejecting reused challenge values. If the verifier doesn’t track challenges, replay is possible.

WebAuthn ceremony validation. A standalone cryptosuite verifier doesn’t have access to the full WebAuthn ceremony context (origin, rpId, attestation chain). Verifiers wanting strict WebAuthn-grade assurance SHOULD add ceremony-level checks on top of cryptosuite verification.

Key compromise. If a user’s FIDO authenticator is compromised, the attacker can produce valid fido4vc-jcs-2026 signatures for any document. Revocation is delegated to: (a) the user revoking the Passkey in their OS account, (b) the wallet operator removing the DID, (c) issuers revoking issued credentials per their own revocation registry.

LanguageRepoNotes
TypeScriptfido-vc-cryptosuite-tsReference implementation; used by middleware and verifier sidecar
Kotlin / JVM(planned public release)Direct in-process integration for JVM verifier stacks; lives in source tree locally
  • Integration Guide — how to plug this into walt.id (or any other verifier)
  • Use Cases — where this cryptosuite is intended to be deployed