ISO mdoc / mDL (ISO 18013-5)

Verify the issuer-signed data of an ISO/IEC 18013-5 mdoc — the credential format behind mobile driving licences (mDL) and the ISO track of the EU Digital Identity Wallet. This is the relying-party side: confirm that the data elements a holder presents were signed by the issuer and have not been altered.

An mdoc's IssuerSigned structure carries the disclosed data elements (nameSpaces) and an issuerAuth that is a COSE_Sign1 (b.cose) over a Mobile Security Object (MSO). The MSO holds, per namespace, a SHA-256/384/512 digest of every issued element. b.mdoc.verifyIssuerSigned verifies the COSE signature with the issuer certificate carried in the COSE x5chain (label 33), parses the MSO, enforces its validityInfo window, and — the integrity check that makes selective disclosure trustworthy — recomputes the digest of every disclosed element (the full Tag-24 IssuerSignedItemBytes) and matches it against the MSO, constant-time. A disclosed element whose digest is absent or mismatched is refused.

Signing algorithms follow b.cose verification: the classical ES256 / 384 / 512 and EdDSA that real mDL issuers use are accepted (consume-what-exists; the caller names the allowlist). opts.trustAnchorsPem additionally verifies the issuer certificate chain and its validity at the asserted time.

Scope. Two halves are verified: issuer-data authentication (ISO 18013-5 §9.1.2.4 — the data is genuine and issuer-signed, via verifyIssuerSigned) and mdoc device authentication (§9.1.3 — holder binding over the verifier's SessionTranscript, via verifyDeviceAuth). Device auth covers the COSE_Sign1 signature variant; the COSE_Mac0 (deviceMac) variant is refused rather than mis-verified. Composes b.cose + b.cbor; no new runtime dependency. Distinct from W3C VCDM (b.vc) and IETF SD-JWT VC (b.auth.sdJwtVc) — the three credential ecosystems.

b.mdoc.verifyIssuerSigned(issuerSigned, opts) #

experimental0.12.40gdprsoc2
{
  {
    algorithms:      string[],  // required — accepted COSE alg names (ES256/384/512, EdDSA)
    trustAnchorsPem: string|string[], // optional issuer roots — enables chain + validity verification
    expectedDocType: string,    // require the MSO docType to match (e.g. "org.iso.18013.5.1.mDL")
    at:              Date,      // validity instant (default now); must be a valid Date
    maxBytes:        number,    // forwarded to b.cbor.decode
    maxDepth:        number,
  }
}

Verify the issuer-signed data of an ISO 18013-5 mdoc and return the disclosed elements. issuerSigned is the CBOR IssuerSigned map (the operator extracts it from the device response / QR). The COSE_Sign1 issuerAuth is verified with the issuer certificate from its x5chain header against the mandatory opts.algorithms allowlist; the MSO validityInfo window is enforced; and every disclosed element's digest is matched against the Mobile Security Object (a mismatch or absence is refused). Pass opts.trustAnchorsPem to also verify the issuer certificate chain.

var out = await b.mdoc.verifyIssuerSigned(issuerSignedBytes, {
  algorithms: ["ES256"], expectedDocType: "org.iso.18013.5.1.mDL",
});
// → { docType, validityInfo, namespaces: { "org.iso.18013.5.1": { family_name, age_over_18, … } }, signerCert, alg }

b.mdoc.verifyDeviceAuth(opts) #

experimental0.12.46gdprsoc2
{
  {
    deviceKey:         object,   // COSE_Key (from verifyIssuerSigned().deviceKey) or a KeyObject / PEM
    deviceSigned:      object,   // the DeviceSigned structure (CBOR bytes or decoded)
    docType:           string,   // the document type (must match the issuer-signed docType)
    sessionTranscript: any,      // the SessionTranscript (CBOR bytes or decoded) bound by the protocol
    algorithms:        string[], // required — accepted COSE alg names (ES256/384/512, EdDSA)
    maxBytes:          number,   // forwarded to b.cbor.decode
    maxDepth:          number,
  }
}

Verify the device-authentication half of an ISO 18013-5 mdoc (§9.1.3, signature variant) — the proof that the holder controls the device key the issuer bound into the MSO, which stops a captured issuer-signed document from being replayed by anyone else. The device's COSE_Sign1 (deviceSigned.deviceAuth.deviceSignature) is verified over the detached DeviceAuthentication structure (["DeviceAuthentication", SessionTranscript, DocType, DeviceNameSpacesBytes]) with the device key from the issuer-signed MSO (verifyIssuerSigned(...).deviceKey). The sessionTranscript binds the proof to this exact exchange and is supplied by the operator (the presentation protocol — e.g. OpenID4VP — defines it). The MAC variant (deviceMac / COSE_Mac0, used in proximity flows with a reader ephemeral key) is not yet supported and is refused with mdoc/device-mac-unsupported.

var issuer = await b.mdoc.verifyIssuerSigned(issuerSignedBytes, { algorithms: ["ES256"] });
var dev = await b.mdoc.verifyDeviceAuth({ deviceKey: issuer.deviceKey, deviceSigned: deviceSignedBytes, docType: issuer.docType, sessionTranscript: transcript, algorithms: ["ES256"] });
// → { docType, alg, deviceNamespaces }

Last updated 2026-08-08T16:39:49.652Z by seeder.