Skip to content

Implement mdoc CBOR encoding conformance tests#170

Open
manuraf wants to merge 21 commits into
mainfrom
feat/WLEO-1263-mdoc-cbor-encoding
Open

Implement mdoc CBOR encoding conformance tests#170
manuraf wants to merge 21 commits into
mainfrom
feat/WLEO-1263-mdoc-cbor-encoding

Conversation

@manuraf

@manuraf manuraf commented Jun 10, 2026

Copy link
Copy Markdown
Contributor

Motivation and Context

This change introduces a suite of conformance tests for mdoc CBOR encoding, ensuring compliance with relevant standards such as RFC 8949 and ISO/IEC 18013-5. The tests validate the structure and integrity of mdoc credentials, addressing the need for rigorous verification in credential issuance.

How Has This Been Tested?

The changes have been tested by implementing multiple conformance tests that check various aspects of mdoc credentials, including:

  • CI_137: Validates CBOR encoding of mdoc credentials.
  • CI_138: Verifies the structure of mdoc components.
  • CI_139: Ensures integrity validation of MSO digests.
  • CI_140 to CI_144: Additional tests for required attributes and structure.
  • CI_146 to CI_150: Tests for protected header and signature algorithm validations.

All tests gracefully skip when no mdoc credentials are present, maintaining compatibility with existing functionality.

manuraf and others added 20 commits May 29, 2026 12:41
Implements CI_137 in a new mdoc-data-model.issuance.spec.ts file,
mirroring the structure of sd-jwt-data-model.issuance.spec.ts.

The test validates that mdoc credential bytes returned by a Credential
Issuer conform to RFC 8949 and ISO/IEC 18013-5:

1. Raw bytes decode as well-formed CBOR (RFC 8949 §3)
2. Top-level CBOR map contains mandatory issuerAuth and nameSpaces keys
   (ISO 18013-5 §8.3.2.1)
3. nameSpaces contains at least one namespace entry
4. Each IssuerSignedItemBytes is a CBOR Tag 24 item (RFC 8949 §3.4 /
   ISO 18013-5 §9.1.2)
5. Tag 24 payloads re-decode to valid IssuerSignedItem maps with the
   four mandatory fields: digestID, random, elementIdentifier,
   elementValue (ISO 18013-5 Table 2)
6. issuerAuth[0] (protected header) is a CBOR byte string that decodes
   to a COSE map containing alg label 1 (ISO 18013-5 §9.1.2.4 /
   RFC 9052 §3.1)

The test skips vacuously when the configured credential_types[] returns
no mdoc credentials (same pattern as getSdJwtCredentials() in the
SD-JWT data-model spec).
…_137 to CBOR encoding scope

- Refactor CI_137: remove issuerAuth/nameSpaces key-presence assertions,
  nameSpaces >=1 namespace check, and COSE_Sign1 4-element array check;
  narrow scope to CBOR encoding only (RFC 8949, Tag 24, IssuerSignedItem map,
  protected header alg label)
- Add CI_138: verifies the two distinct mdoc components per IT-Wallet spec
  Layer A - top-level nameSpaces (map, >=1 ns) and issuerAuth (4-element array)
  Layer B - issuerAuth[1] unprotected header contains x5chain (label 33, RFC 9360),
            issuerAuth[2] payload is Tag 24 wrapping MSO map with all 6 mandatory
            fields (docType, version, validityInfo.validUntil, digestAlgorithm,
            valueDigests, deviceKeyInfo.deviceKey), issuerAuth[3] signature bstr
  Layer C - cross-link: every nameSpaces key appears in MSO valueDigests

Refs: IT-Wallet credential-data-model.rst, ISO 18013-5 §9.1.2.4, RFC 9052, RFC 9360
Implements conformance test CI_139 which performs the Relying Party
digest-validation algorithm (ISO 18013-5 §9.3.1) on every mdoc
credential returned by the issuer.

Four assertions are verified per IssuerSignedItem:
  A. random field is ≥ 16 bytes (ISO 18013-5 §9.1.2.5)
  B. digestID values are unique per namespace (§9.1.2.5)
  C. every (ns, digestID) pair in nameSpaces has a matching entry in
     valueDigests[ns][digestID] (§9.1.2.5 + §9.3.1)
  D. Hash(IssuerSignedItemBytes) matches valueDigests[ns][digestID],
     verified with timingSafeEqual (§9.1.2.4 + §9.3.1)

The hash is computed over the raw Tag 24 bstr content (taggedItem.value)
— never a re-encoding of the decoded map. The digestAlgorithm string
(e.g. "SHA-256") is normalised to Node crypto format ("sha256").

Test gracefully skips when no mdoc credentials are present in the
response, following the same pattern as CI_137/CI_138.

No new dependencies added — only the node:crypto built-in is used.
Adds CI_141 to the MdocDataModel suite, asserting that
IssuerSigned.nameSpaces:
  1. contains one or more entries;
  2. has no duplicate CBOR-map keys (detected via the cbor package's
     built-in preventDuplicateKeys decode option, per RFC 8949 §3.1);
  3. each key is a non-empty text string (ISO 18013-5 §7.1).

The reverse-domain naming heuristic from ISO 18013-5 §7.1 is logged as
an advisory only to avoid false negatives on legacy issuers.

Follows the existing CI_137–CI_140 harness: getMdocCredentials() skip
path, try/finally with log.testCompleted, and no orchestrator/step
changes (purely additive assertion on the issuance response).
… test

Add a new Credential Issuer conformance test CI_142 to the MdocDataModel
suite. The test validates that within each nameSpace, one or more
IssuerSignedItemBytes are correctly encoded as CBOR byte strings with
Tag 24 (#6.24(bstr .cbor)), appearing as 24(<<...>>) in diagnostic
notation per RFC 8949 §3.4.5.1 and ISO/IEC 18013-5 §9.1.2.5 /
§8.3.2.1.2.2.

CI_142 isolates the encoding contract from the schema contract
(intentionally narrower than CI_137): asserts per element
instanceof cbor.Tagged, tag === 24, content is a bstr, and that the
embedded bytes are themselves well-formed CBOR (the .cbor constraint).

Pure additive change — no orchestrator/step modifications.
…test

Validates the IT-Wallet compliance-table requirement that each
IssuerSignedItem embedded inside the Tag 24 IssuerSignedItemBytes
contains all four required attributes with the correct CBOR types
per ISO 18013-5 §9.1.2.5 / §8.3.2.1.2.3:

- digestID: uint (non-negative integer)
- random: bstr (byte string)
- elementIdentifier: tstr (non-empty text string)
- elementValue: any (key presence only)

Out of scope (covered elsewhere):
- Tag 24 wrapping → CI_142
- random length ≥16 / digestID uniqueness / digest integrity → CI_139
- nameSpaces map shape / unique keys → CI_140, CI_141
…ng test

Assert that the issuerAuth COSE_Sign1 protected header contains COSE
label 1 (alg parameter, RFC 9052 §3.1) encoded as a CBOR integer
whose value identifies the algorithm per RFC 9053.

- Skip guard when no mdoc credentials are present
- Map/plain-object dual-access pattern consistent with CI_138
- Asserts presence of integer key 1 and that value is a CBOR integer

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…bileSecurityObject required attributes validation
@manuraf manuraf requested a review from a team as a code owner June 10, 2026 15:18
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