PasskeyBridge

Engineering · 2026-07-20

mDoc and mDL Interoperability with W3C VC 2.0: Bridging the ISO 18013-5 and JSON-LD Worlds

By J. W. Bouckaert

mDoc and mDL Interoperability with W3C VC 2.0: Bridging the ISO 18013-5 and JSON-LD Worlds

Two standards, one wallet, one verifier

A driver in Phoenix hands a phone to a bartender. A traveler in Rome taps a phone against a hotel reader. A jobseeker in Berlin presents credentials to a Dutch employer through a browser. Three interactions, three continents, and three encoding stacks that do not natively speak to one another.

The identity industry has spent five years building two mobile credential formats in parallel. The ISO/IEC 18013-5 mobile driving license (mDL) is a CBOR-encoded, COSE-signed artifact designed for offline device-to-device presentation. The W3C Verifiable Credentials Data Model 2.0 is a JSON-LD document, typically signed as a JWS or as an SD-JWT-VC, designed for online exchange through OpenID4VP and similar protocols. Both formats will exist for the foreseeable future. Any relying party operating across mobility (DMV, TSA, age verification), employment, healthcare, and cross-border eIDAS use cases will encounter both, sometimes in the same session.

Comparisons of the two have been written; this article is an engineering specification for what a bridging layer must actually do, and an honest account of the one problem the specifications have not solved: how a verifier proves that the presenter of an mDoc and the presenter of a W3C VC are the same subject when the holder-binding key is generated under different cryptographic disciplines.

The divergence in one table

The two stacks agree on almost nothing at the wire level. What follows is the substantive difference matrix rather than the marketing one.

PropertyISO/IEC 18013-5 mDoc/mDLW3C VC 2.0
EncodingCBOR (RFC 8949)JSON-LD (JSON with @context)
Signature envelopeCOSE_Sign1 (RFC 9052)JWS (RFC 7515) or Data Integrity Proofs
Selective disclosureSalted hashes over CBOR elementsSD-JWT-VC salted claims, or BBS+ signatures
Primary transportBLE / NFC / QR (18013-5); HTTPS (18013-7)HTTPS via OpenID4VP
Retrieval modelDevice retrieval or server retrievalServer retrieval (holder-side wallet)
Trust anchorIACA root certificate (X.509 chain)did:web, did:jwk, or issuer JWKS
Device bindingDeviceKey in MSO, signed by issuercnf claim in credential or SD-JWT
Namespace modelFlat mDoc namespaces (org.iso.18013.5.1)Nested JSON-LD terms under @context
Freshness proofSessionTranscript with reader noncePresentation with verifier nonce + audience

Two observations. First, both stacks have a retrieval model, a signature model, and a selective disclosure model—they simply chose different primitives at every layer. Second, the CBOR/COSE side is optimized for offline low-latency exchange over constrained transports; the JSON-LD/JWS side is optimized for online exchange over HTTPS with a browser-adjacent wallet. Neither is wrong for its target environment. Both are inadequate for the environment the other was designed for.

Four levels of interoperability

The word "interoperable" gets used loosely. There are at least four levels at which mDL and VC 2.0 can be said to interoperate, and each is a different engineering problem.

Level 1: Semantic equivalence. The claim family_name in mDL and the term familyName in the W3C schema are understood to represent the same attribute. This is a data dictionary problem and has been largely solved by cross-referencing ISO 23220 and the eIDAS Person Identification Data schema.

Level 2: Format transcoding. A wallet holds an mDoc and produces a W3C VC-shaped presentation for a verifier that speaks only JSON-LD, or vice versa. This is technically possible but destroys the original signature; the transcoded credential is by definition a re-issuance, and the trust anchor changes from the mDoc issuer to whoever performed the transcoding.

Level 3: Dual presentation. A wallet presents both formats in the same session, allowing the verifier to choose which to accept. This is the OpenID4VP path and the direction ISO/IEC 18013-7 took for online mDL presentation. The verifier accepts whichever it can validate.

Level 4: True bridging. The verifier accepts either format natively, evaluates them against a common policy, and binds them to the same subject regardless of which format arrived. This is the level relying parties actually want. It is also the level nobody has fully shipped.

The rest of this article is about Level 4.

The bridging architecture

A verifier operating at Level 4 has four pipelines running behind a single policy engine.

                    Presentation Request (verifier-owned)
                                  │
             ┌────────────────────┼────────────────────┐
             ▼                    ▼                    ▼
       mDL Reader          OID4VP Endpoint       Age-only Endpoint
     (BLE/NFC/QR/HTTPS)     (HTTPS callback)      (audience-scoped)
             │                    │                    │
             ▼                    ▼                    ▼
       CBOR/COSE Parser    JWS/SD-JWT Parser     Claim-set Extractor
             │                    │                    │
             └────────────────────┼────────────────────┘
                                  ▼
                       Canonical Claim Set (internal)
                                  │
                                  ▼
                        Policy Engine + Binding
                                  │
                                  ▼
                          Trust Decision + Audit

Every arrow above hides a specification. The three that matter most are the parser normalizations, the canonical claim set, and the policy binding.

Parser normalization

The two envelopes carry different metadata. An mDoc arrives with a Mobile Security Object (MSO) that binds a DeviceKey and lists the digest algorithms and value digests for each disclosed element. A W3C VC arrives with a JWS header (or a Data Integrity Proof block) and an issuer JWKS reference. The verifier needs to extract, from both, the same four facts:

  1. Which issuer signed this credential, and does the verifier trust that issuer?
  2. Which subject key, if any, is bound to the credential (holder binding)?
  3. Which claims were disclosed in this presentation, and were the undisclosed claims cryptographically bound to the same credential?
  4. What is the freshness proof, and does it bind this presentation to this specific verifier request?

For mDL, (1) is the IACA X.509 chain, (2) is the DeviceKey COSE_Key in the MSO, (3) is the SessionTranscript digest comparison against the MSO's ValueDigests, and (4) is the SessionTranscript itself with the reader-issued nonce. For W3C VC, (1) is the JWS iss resolved to a JWKS or DID document, (2) is the cnf claim, (3) is the SD-JWT disclosure digest match, and (4) is a proof-of-possession JWT (kb-jwt) audience-scoped to the verifier.

At the level of the canonical claim set, these four facts must be represented uniformly. The rest of the verifier's code should not care which envelope produced them.

Canonical claim set

We settled on a JSON object with fixed top-level keys and namespace-scoped claims. Neither the mDL name nor the JSON-LD term is preserved verbatim inside the policy engine; both are mapped to a stable internal identifier. A minimal example after ingesting either an mDL or a VC 2.0 age-over presentation:

{
  "envelope": "mdoc" | "vc-2.0-sd-jwt",
  "issuer": {
    "id": "did:web:dmv.example.gov" | "cn=DMV Root, o=Example State",
    "trust_anchor": "iaca:ExampleStateDMV" | "did:web:dmv.example.gov",
    "trust_status": "trusted" | "unknown" | "revoked"
  },
  "subject_binding": {
    "key_thumbprint": "sha256:...",
    "key_type": "P-256" | "ML-DSA-65",
    "proof_of_possession": "verified" | "absent" | "failed"
  },
  "claims": {
    "iso.18013.5.age_over_21": true,
    "iso.18013.5.portrait_hash": "sha256:...",
    "eidas.pid.given_name": "MARIA"
  },
  "freshness": {
    "nonce": "...",
    "audience": "https://verifier.example.com/oid4vp",
    "verified_at": "2026-07-20T15:04:11Z"
  }
}

The policy engine runs against this object. The verifier's application code never touches CBOR, never touches JSON-LD @context resolution, and never touches a JWS header. Everything below the canonical claim set is a translation problem.

Policy binding

The policy engine's job is to evaluate whether the canonical claim set satisfies the presentation request. A request for "age over 21, plus a portrait match" is satisfied identically whether the underlying credential arrived as an mDL over BLE or as a VC 2.0 SD-JWT over OpenID4VP. This is where the bridge earns its cost: policy is written once, applied to both.

Two mistakes commonly appear in first-generation bridges. One: writing the policy against the envelope-native claim names, which forces every policy to be duplicated. Two: failing to preserve the envelope field, which loses the ability to prove that a particular decision was made against a signed mDoc rather than an inferior format. Both are avoidable if the canonical claim set is designed as a first-class type and every parser targets it.

The unresolved binding key

Now to the honest part. The specifications do not solve the following case.

A subject holds an mDL issued by a state DMV, bound to a DeviceKey k_mDL generated inside a hardware-backed keystore under COSE_Key encoding, alg ES256. The same subject holds an eIDAS PID issued by a national identity provider, bound to a JWK k_PID generated inside a different wallet, under an alg parameter of ML-DSA-65 for post-quantum resilience. Both credentials are legitimately issued to the same natural person. Both are presented to the same verifier in the same session.

The verifier can validate each credential in isolation. What the verifier cannot do—without an out-of-band signal—is prove that the presenter of the mDL is the same subject as the presenter of the PID. k_mDL and k_PID are independent keys with different algorithms in different key stores.

Three approaches exist. None is fully satisfying.

Cryptographic key linking. The subject presents a link certificate—a small signed object stating "these two public keys belong to the same subject", signed by a mutually trusted authority. This works when such an authority exists (a national eID root, for example) and fails when it does not (cross-jurisdiction, cross-issuer).

Biometric or attribute cross-match. The verifier compares the portrait_hash in the mDL against the portrait_hash in the PID, or extracts a live biometric and matches against both credentials' embedded portraits. This works when both credentials carry the same attribute and both agree on hash canonicalization. It fails when they carry different attributes or when one is selectively disclosed and the other is not.

Zero-knowledge same-subject proof. The wallet, holding both credentials, produces a zero-knowledge proof that the two credentials belong to the same subject without revealing either subject identifier. Cryptographically feasible under BBS+ signatures and under some post-quantum ZK constructions; not yet supported by either the ISO 18013 family or the W3C VC 2.0 specifications as a normative feature. Draft work exists (BBS+ VC integrations and various OpenID4VP high-assurance profiles), but implementations are early.

Until the third approach lands as an interoperable specification, relying parties operating at Level 4 must decide, per use case, whether cryptographic key linking or attribute cross-match is acceptable. For age verification with a portrait, the attribute cross-match is usually sufficient. For high-assurance identity chaining (credential chaining under our attestation model), it is not.

We flag this openly because pretending otherwise is what has produced the current generation of verifiers that quietly accept "the same holder presented both" as an assumption. That is credulity dressed in structured JSON.

OpenID4VP as the wire layer

A subtle point that is often lost: OpenID4VP is a protocol for requesting and delivering presentations. It is agnostic about the underlying credential format. A verifier can request an mDL over OpenID4VP (ISO/IEC 18013-7 does exactly this) or a W3C VC 2.0 SD-JWT over the same channel. The wire looks similar; the payload does not.

The consequence is that "adds OpenID4VP support" is not the same as "adds mDL support" and not the same as "adds W3C VC 2.0 support". A verifier can implement OpenID4VP without ever accepting an mDoc, and vice versa. This distinction matters for procurement conversations and for interoperability testing: shipping an OpenID4VP endpoint is a necessary condition for online mobile-credential exchange, but on its own it does not tell a relying party which envelopes will actually validate.

Our own verifier implementation treats OpenID4VP as one of three ingress paths (BLE/NFC, HTTPS OpenID4VP, and QR-code-triggered HTTPS) and treats mDL and W3C VC 2.0 as two orthogonal envelope parsers, either of which can arrive over the HTTPS path. Configuration by policy.

A staged posture for verifiers

For a relying party building a bridge today, we recommend a specific staged posture rather than trying to ship everything at once.

StageShipsSkipsVerifier maturity
1mDL over BLE/NFC (18013-5) OR OpenID4VP with SD-JWT-VCCross-envelope bindingSingle-format
2Both envelopes behind a canonical claim set; independent policyCross-envelope subject bindingDual-format
3Above, plus attribute cross-match binding for common attributesZK same-subject proofsLevel-4 with caveats
4Above, plus BBS+ or PQ-ZK same-subject proofs when specs landFull Level-4

Most verifiers in production today sit at Stage 1. A small number are at Stage 2. Very few have Stage 3 running honestly (as opposed to "we assume it works"). Stage 4 is a 2027 conversation at the earliest.

The reason to publish this ladder is that "we support mDL and VC" is being marketed at Stage 2 as if it were Stage 4. It is not. A relying party procuring a bridge should ask, in writing, "What binds the subject of one envelope to the subject of the other?"—and treat any answer that resolves to "the wallet asserts they are the same" as a Stage-2 answer well short of Stage 4.

Trust anchors

A final wrinkle. The mDL trust model is a PKI: IACA root certificates, issuer certificates chained under them, and an out-of-band trust list distributed to verifiers. The W3C VC 2.0 trust model is a mix: DID documents (did:web, did:key, did:jwk), issuer JWKS URIs, and increasingly, status-list mechanisms for revocation.

The two trust models can be reconciled but not merged. A bridging verifier maintains two trust configurations, one per envelope, and stamps every canonical claim set with which anchor validated the envelope. This is why the trust_anchor field in the canonical claim set above is not a string but a namespaced identifier: iaca:ExampleStateDMV vs did:web:dmv.example.gov are different assertions about the same DMV, and losing that distinction downstream would collapse the audit story.

For zero-PII architectures this discipline matters more: the audit trail preserves which trust chain accepted a decision even when the underlying subject identifier has been hashed and discarded. If a national root is later distrusted, every decision anchored to it can be revisited without unearthing PII.

Consequences for the ITRaaS layer

An identity trust and reliability service that sits in front of an application—the ITRaaS attestation layer we operate—does not want its customers writing envelope-specific glue code. The bridge belongs one level below the application, in the attestation layer, so that a merchant, employer, or hospital can express its policy in terms of claims alone, and the layer beneath handles the encoding gymnastics.

In practice, that means the ITRaaS surface exposes:

  • A presentation request builder that produces an mDL device-request, an OpenID4VP request, and a QR fallback from a single high-level "please prove age over 21 and show a portrait" description.
  • A response evaluator that ingests whichever envelope arrives, produces the canonical claim set, and hands the application a boolean policy result plus a signed attestation summarizing the trust chain, the freshness proof, and the envelope used.
  • A binding decision surface that lets the application declare, per policy, whether cross-envelope same-subject inference is allowed and, if so, under which rule (link certificate, attribute cross-match, or none).

The last of these is the honest surface. Every ITRaaS provider that omits it is quietly making the decision on the customer's behalf, and getting it wrong at least some of the time.

Outlook

Two encoding stacks will continue to coexist for the rest of the decade. ISO 18013-5 has too much regulatory momentum in mobility and identity documents to be displaced, and the eIDAS 2.0 architecture has cemented W3C VC 2.0 (via SD-JWT-VC) as the online exchange primitive for European Digital Identity Wallets. Rather than pick a side, the right response is to build a bridge that treats both as equally legitimate, keeps the audit story honest about which envelope was used, and refuses to conflate "we support both formats" with "we can prove they belong to the same subject".

Every claim verified inside the request that asked. Every envelope validated on its own terms. Every audit trail preserving which standard did the validating.

That is what interoperability at the wire actually costs, and what it actually delivers.

Read the credential chaining analysis → See how eIDAS 2.0 handles zero-PII binding → Explore SD-JWT-VC and holder binding → Get started with PasskeyBridge →

Start free · Test the API