SMART Trust HCERT library tools
SMART Trust Tools
Requirements · read from skills/requirements/

HCERT library requirements

95 requirements, one cat-harness req: node each. Every link below is an edge in the graph: library sections, external schemas, references and owner directives a requirement references; the broader requirement it is derived from; the smart-trust FHIR Requirements it satisfies; the skills that realize it; the WIT items and CDDL rules whose comments cite it; and the open issues (beans) that affect it.

GEN · Scope, architecture and language strategy

STT-GEN-001 · Issuance and verification of the full HCERT QR pipeline

SHALL scope derived kotlin rust open in explorer · node

The library SHALL implement issuance (steps 1 to 9) and verification (QR Code Generation section 11) of HCERT QR codes.

Engineering consequence of QR Code Generation, 1. The pipeline at a glance. QR Code Generation, 1. The pipeline at a glance: “Steps 3 to 9 never vary. A verifier runs the same list upwards.”.

References

Satisfies (smart-trust)

Realized by skills

Verified by tests

Test plans

Open issues

STT-GEN-002 · Every step callable on its own

SHALL architecture derived kotlin rust open in explorer · node

Each pipeline step SHALL be exposed as a separate public operation (payload codec, CBOR, COSE sign/parse, ZLIB, Base45, HC1 prefix) as well as through the end-to-end issuer and verifier, so that integrators can substitute or inspect any stage.

Engineering consequence of QR Code Generation, 1. The pipeline at a glance; QR Code Generation, Generating a QR Code. QR Code Generation, Generating a QR Code: “The first step below is therefore a selection step, and everything after it is mechanical.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-GEN-003 · Payload-agnostic core

SHALL architecture derived kotlin rust open in explorer · node

The modules for steps 3 to 9 SHALL NOT depend on any payload logical model. Payloads SHALL be plugged in through a PayloadCodec registered against a subclaim.

Engineering consequence of QR Code Generation, Generating a QR Code. QR Code Generation, Generating a QR Code: “The container, the signature and the transport encoding are the same for every GDHCN trust domain ... What changes from one use case to the next is only the payload”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-GEN-004 · Kotlin first, for JVM, Android and Node

SHALL non-functional directive kotlin open in explorer · node

The first implementation SHALL be a Kotlin Multiplatform library with all pipeline logic in common code. It SHALL ship for three targets: JVM (Java 11+), Android, and Node.js (Kotlin/JS IR, published to npm with an @JsExport facade and generated TypeScript declarations). Other KMP targets MAY follow.

Stakeholder directive, in the context of Stakeholder directive: Kotlin library first, Rust library second; Stakeholder directive: transpile targets and API-first.

References

Realized by skills

Verified by tests

Test plans

STT-GEN-005 · Rust second, for WebAssembly

SHALL non-functional directive rust open in explorer · node

The second implementation SHALL be a Rust library meeting this same requirement set. It SHALL build for wasm32 as a WebAssembly component implementing the WIT worlds in api/wit, and SHALL be transpiled with jco into an npm package usable from Node.js and browsers. A native crate SHALL also be published. The codec module SHOULD build without std (with alloc).

Stakeholder directive, in the context of Stakeholder directive: Kotlin library first, Rust library second; Stakeholder directive: transpile targets and API-first; WIT, WebAssembly Component Model IDL; jco: JavaScript toolchain for WebAssembly components.

References

Realized by skills

Verified by tests

Test plans

STT-GEN-006 · Kotlin and Rust behave the same

SHALL architecture directive kotlin rust open in explorer · node

The Kotlin and Rust libraries SHALL expose the same conceptual operations, the same configuration knobs and the same failure taxonomy, with idiomatic naming in each language, and SHALL pass the same conformance vectors.

Stakeholder directive, in the context of Stakeholder directive: Kotlin library first, Rust library second.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

Open issues

STT-GEN-007 · HCERT Specification is authoritative

SHALL non-functional direct kotlin rust open in explorer · node

Where the QR Code Generation walkthrough and the HCERT Specification differ, the library SHALL follow the HCERT Specification. Each release SHALL state which HCERT Specification version and which smart-trust IG version it implements (currently 1.1.1 and 1.7.5).

Restates QR Code Generation, Generating a QR Code; Change Log, v1.7.5. QR Code Generation, Generating a QR Code: “Where this page and the HCERT Specification appear to differ, the HCERT Specification is authoritative.”.

References

Realized by skills

STT-GEN-008 · CBOR is the wire format; JSON is optional

SHALL architecture direct kotlin rust open in explorer · node

The library SHALL work entirely on in-memory structures and CBOR. It SHALL NOT need a JSON representation to issue or verify. A JSON view of payloads MAY be provided for display and debugging.

Restates QR Code Generation, 5. Step 4 — Serialise as CBOR; HCERT Specification, 3.2.7 Health Certificate Claim. QR Code Generation, 5. Step 4 — Serialise as CBOR: “the wire format is CBOR, and an implementation need never produce the JSON form at all.”.

References

Realized by skills

STT-GEN-009 · Raw transport besides QR

SHALL functional derived kotlin rust open in explorer · node

The issuer SHALL be able to return the signed COSE_Sign1 bytes without the barcode encodings, and the verifier SHALL accept COSE_Sign1 bytes directly, for 8-bit-safe transports (NFC, Bluetooth, app-to-app).

Engineering consequence of HCERT Specification, 4.1 Raw. HCERT Specification, 4.1 Raw: “the HCERT container and its payloads may be transferred as-is, utilising any underlying, 8 bit safe, reliable data transport”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-GEN-010 · API defined first, in WIT

SHALL architecture directive kotlin rust open in explorer · node

The library API SHALL be defined in WIT (api/wit) and reviewed before implementation. The WIT SHALL be the contract both implementations follow: Rust exports it from its WebAssembly component, and Kotlin follows it through documented mapping rules. Each WIT item SHALL cite the requirement(s) it implements. Breaking WIT changes SHALL bump the package version.

Stakeholder directive, in the context of Stakeholder directive: transpile targets and API-first; WIT, WebAssembly Component Model IDL.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

Open issues

STT-GEN-011 · Same JavaScript surface from Kotlin/JS and Rust/WASM

SHALL architecture derived kotlin rust open in explorer · node

The Node.js packages built from Kotlin/JS and from the Rust WebAssembly component SHALL expose the same TypeScript surface: the declarations jco generates from the WIT (api/generated/ts). The Kotlin/JS declarations SHALL type-check against it.

Engineering consequence of Stakeholder directive: transpile targets and API-first; jco: JavaScript toolchain for WebAssembly components.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

STT-GEN-012 · Synchronous core with no I/O

SHALL architecture derived kotlin rust open in explorer · node

The pipeline core SHALL do no network or file I/O and SHALL NOT read an ambient clock. Trust material SHALL be loaded into a trust store by the host, and the current time SHALL be passed as an argument. Fetching, caching and refreshing trust lists SHALL live in separate, platform-specific modules.

Engineering consequence of WIT, WebAssembly Component Model IDL; QR Code Generation, 2.2 Choosing between a DVC and a VHL. QR Code Generation, 2.2 Choosing between a DVC and a VHL: “Works fully offline — Yes — verifier needs only the trust list”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-GEN-013 · Types and schemas trace to open standards

SHALL architecture directive kotlin rust open in explorer · node

Every data type in the API contract, wire schema and payload models SHALL trace to an openly available standard: IETF CBOR/CDDL (RFC 8949, RFC 8610) for the wire format, HL7 FHIR data types and W3C XSD datatypes for payload elements, W3C JSON-LD and SHACL for payload semantics, ITU-T X.680 for certificates, and I-JSON (RFC 7493) for JSON files. Paywalled standards SHALL NOT be used as type or schema references.

Stakeholder directive, in the context of Stakeholder directive: open standards for types and schemas; RFC 8610 Concise Data Definition Language (CDDL); HL7 FHIR R5 Data Types; W3C XML Schema 1.1 Part 2: Datatypes; W3C JSON-LD 1.1; W3C Shapes Constraint Language (SHACL); ITU-T X.680 ASN.1 (free); RFC 7493 I-JSON.

References

Realized by skills

STT-GEN-014 · Kotlin API skeleton generated from the WIT

SHALL architecture directive kotlin rust open in explorer · node

The Kotlin API (types and interfaces, no behaviour) SHALL be generated from the WIT contract by tools/wit2kotlin/wit2kotlin.py, following the WIT to Kotlin mapping rules in api/README.md, into a Kotlin Multiplatform module targeting the JVM and Node.js. The generated API SHALL NOT expose the contract language: no Kotlin type, member or KDoc names WIT or a WIT world, and worlds are not generated. Kotlin implementations SHALL implement the generated interfaces. CI SHALL fail when the generated skeleton is out of date with the WIT or does not compile.

Stakeholder directive, in the context of Stakeholder directive: transpile targets and API-first; WIT, WebAssembly Component Model IDL.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

SEL · Step 1: select payload type

STT-SEL-001 · Built-in subclaim registry

SHALL functional direct kotlin rust open in explorer · node

The library SHALL ship a subclaim registry containing 1 (HCertDCC), 3 (DDCCCoreDataSetVS), 4 (DDCCCoreDataSetTR), 5 (VHL URI), -6 (DVCMin) and -7 (MedicationTreatmentLineMin), each with its status (Assigned, Proposed, Development range) and logical-model canonical URL.

Restates QR Code Generation, 2.1 Subclaims defined in this guide; HCERT Specification, 3.2.7.1 Subclaims; HCert logical model. QR Code Generation, 2.1 Subclaims defined in this guide: “These are the subclaims enumerated by the HCert logical model.”. HCERT Specification, 3.2.7.1 Subclaims: “While the logical model enlists assigned sub claims, the ones listed in this specification are considered authoritative”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-SEL-002 · Registry is extensible

SHALL api derived kotlin rust open in explorer · node

Integrators SHALL be able to register more subclaims and their codecs, and override the defaults, at runtime, without forking the library.

Engineering consequence of HCERT Specification, 3.2.7.1 Subclaims. HCERT Specification, 3.2.7.1 Subclaims: “subclaims for negative integer values are for development purposes and are free to use”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-SEL-003 · Development-range subclaims need explicit opt-in

SHALL functional derived kotlin rust open in explorer · node

The registry SHALL classify subclaims >= 0 as WHO-reserved and negative subclaims as development range. The issuer SHALL refuse to issue a development-range subclaim unless the issuer policy explicitly allows it. The subclaim number used for a payload type SHALL be configuration, not a constant in a codec, so that DVC can move from -6 to an assigned number without an API break.

Engineering consequence of QR Code Generation, 2.1 Subclaims defined in this guide; HCERT Specification, 3.2.7.1 Subclaims. QR Code Generation, 2.1 Subclaims defined in this guide: “Do not treat a negative subclaim as a production allocation.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-SEL-004 · One or more payloads per HCERT

SHALL functional derived kotlin rust open in explorer · node

The issuer SHALL accept one or more payloads and emit an hcert (-260) map with one entry per payload, refusing a request with no payload (NoPayload) or two payloads under the same subclaim (DuplicateSubclaim). The verifier SHALL accept an hcert map with any number of entries and return each valid entry with its subclaim, in wire order. It SHALL fail with HcertMissing when there is none, and with MultipleSubclaims when there are more entries than the caller's optional maxPayloads allows. An entry whose payload fails its model check SHALL be left out of the result and reported as a diagnostic; verification SHALL fail with that entry's own reason only when no valid entry remains.

Engineering consequence of QR Code Generation, 2. Step 1 — Select the payload type; QR Code Generation, 4. Step 3 — Build the CWT claim set; HCERT Specification, 3.2.7 Health Certificate Claim; the CWT payload logical model (-260 1..*). QR Code Generation, 2. Step 1 — Select the payload type: “Carrying exactly one is an implementation choice, not a constraint of the container, and it is the choice made throughout this page.”. HCERT Specification, 3.2.7 Health Certificate Claim: “Several different types of health certificate MAY exist under the same claim”. How a verifier treats several entries is the owner's decision of 2026-10-22 (bean OI-02), pending upstream text.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-SEL-006 · Trust domain codes come from the value set

SHALL functional derived kotlin rust open in explorer · node

Trust domain codes (currently DDCC, IPS-PILGRIMAGE, PH4H, ICVP) SHALL be treated as data loaded from, or checked against, the GDHCN Trust Domain value set. They SHALL NOT be a closed enum.

Engineering consequence of QR Code Generation, 2.1 Subclaims defined in this guide; Trust Domains; GDHCN Trust Domain value set. QR Code Generation, 2.1 Subclaims defined in this guide: “The registered production trust domains are DDCC, IPS-PILGRIMAGE, PH4H and ICVP.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

PAY · Step 2: assemble payload

STT-PAY-001 · Typed DVCMin model

SHALL functional direct kotlin rust open in explorer · node

The library SHALL provide a typed DVCMin payload with the short element names and cardinalities of the logical model: n 1..1, dob 1..1, s 1..1, nt 1..1, id 0..1, dt 0..1, gn 0..1, vx 1..1, v 1..1, and a PayloadCodec for it.

Restates QR Code Generation, 3. Step 2 — Assemble the payload; DVCMin logical model (trust-phw). QR Code Generation, 3. Step 2 — Assemble the payload: “The DVC payload uses short element names because every byte is paid for twice”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-PAY-002 · Typed DVCMinVaccineDetails model

SHALL functional direct kotlin rust open in explorer · node

The library SHALL provide the vx structure with vp 1..1, dv 1..1, cn 0..1, is 0..1 and bo 1..1.

Restates QR Code Generation, 3. Step 2 — Assemble the payload; DVCMinVaccineDetails logical model (trust-phw). QR Code Generation, 3. Step 2 — Assemble the payload: “Populate the logical model you selected.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-PAY-003 · One immunization event per DVC

SHALL functional direct kotlin rust open in explorer · node

A DVCMin SHALL carry exactly one vx. The library SHALL provide a helper that turns one visit with N administered vaccines into N payloads, one per vaccine, and so N certificates.

Restates QR Code Generation, 3. Step 2 — Assemble the payload; QR Code Generation, 12.1 Scenario. QR Code Generation, 3. Step 2 — Assemble the payload: “vx is 1..1. One DVC documents one immunization event. A visit at which two vaccines are administered produces two certificates and two QR codes.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-PAY-004 · Issuer-or-clinician invariant

SHALL functional direct kotlin rust open in explorer · node

Validation SHALL fail a DVCMin whose vx has neither is nor cn (invariant must-have-issuer-or-clinician-name). The issuer SHALL NOT sign a payload that fails validation.

Restates QR Code Generation, 3. Step 2 — Assemble the payload. QR Code Generation, 3. Step 2 — Assemble the payload: “At least one of is or cn must be present”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-PAY-005 · Subjects with no identity document

SHALL functional direct kotlin rust open in explorer · node

The DVCMin model SHALL allow id and dt to be absent and SHALL support gn (parent or guardian) for subjects such as newborns.

Restates QR Code Generation, 3. Step 2 — Assemble the payload; QR Code Generation, 12.1 Scenario. QR Code Generation, 3. Step 2 — Assemble the payload: “id is optional precisely because many certificate subjects have no identification document.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-PAY-007 · Nationality is ISO 3166-1 alpha-3

SHALL functional direct kotlin rust open in explorer · node

Validation SHALL check that nt is an ISO 3166-1 alpha-3 code, including user-assigned codes such as XXA, which is used for testing.

Restates QR Code Generation, 3. Step 2 — Assemble the payload; QR Code Generation, 12.3 Test issuer. QR Code Generation, 3. Step 2 — Assemble the payload: “Nationality, ISO 3166-1 alpha-3”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-PAY-008 · Preserve unknown payload elements

SHALL functional derived kotlin rust open in explorer · node

Decoding a typed payload SHALL keep unknown elements and SHALL NOT fail because of them, so that newer template versions (v) can still be read and displayed.

Engineering consequence of HCERT Specification, 3.2.7 Health Certificate Claim. HCERT Specification, 3.2.7 Health Certificate Claim: “Decoding applications SHOULD however be permissive and robust in these aspects”.

References

Realized by skills

Verified by tests

Test plans

STT-PAY-009 · Other payloads

SHALL functional derived kotlin rust open in explorer · node

Subclaim 5 SHALL have a typed codec holding a URI string. Subclaims 1, 3, 4 and -7 SHALL at least be issuable and verifiable as raw CBOR payloads. Typed codecs for them MAY follow.

Engineering consequence of QR Code Generation, 2.1 Subclaims defined in this guide; HCert logical model; MedicationTreatmentLineMin logical model (ph4h); DDCC Implementation Guide.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-PAY-010 · Payload models derived from FHIR logical models

SHALL architecture directive kotlin rust open in explorer · node

Payload schemas SHALL be derived mechanically from the published FHIR logical models, not written by hand: a JSON-LD 1.1 context that maps the payload's short element names to IRIs, SHACL shapes for cardinality, primitive patterns, invariants and bindings, and CDDL for the CBOR encoding. Inputs SHALL be pinned to a source commit. Typed payload codecs SHALL be checked against these derived schemas.

Stakeholder directive, in the context of Stakeholder directive: open standards for types and schemas; DVCMin logical model (trust-phw); DVCMinVaccineDetails logical model (trust-phw); W3C JSON-LD 1.1; W3C Shapes Constraint Language (SHACL). DVCMin logical model (trust-phw): “Minimal DVC payload for use within an HCERT Payload”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

CWT · Step 3: CWT claim set

STT-CWT-001 · Integer claim keys

SHALL functional direct kotlin rust open in explorer · node

The CWT claim set SHALL use the integer keys 1 (iss), 6 (iat), 4 (exp) and -260 (hcert). The hcert map SHALL use integer subclaim keys. The quoted keys in the documentation ("1", "-260", "-6") are JSON notation only and SHALL NOT be emitted as text keys.

Restates QR Code Generation, 4. Step 3 — Build the CWT claim set; QR Code Generation, 5. Step 4 — Serialise as CBOR; QR Code Generation, 12.4 The payload; HCERT Specification, 3.2.1 CWT Structure Overview. QR Code Generation, 4. Step 3 — Build the CWT claim set: “Place the payload under its subclaim, inside the hcert claim, inside a CWT claim set”. QR Code Generation, 5. Step 4 — Serialise as CBOR: “The JSON shown throughout this page and in the logical models is schema notation only”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-CWT-002 · NumericDate integers

SHALL functional direct kotlin rust open in explorer · node

iat and exp SHALL be encoded as integer NumericDate (seconds since the epoch). The decoder SHOULD accept floating-point NumericDate and truncate it.

Restates HCERT Specification, 3.2.5 Expiration Time; HCERT Specification, 3.2.6 Issued At; RFC 8392 CBOR Web Token (CWT). HCERT Specification, 3.2.5 Expiration Time: “SHALL hold a timestamp in the integer NumericDate format”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-CWT-003 · Expiry is mandatory on issuance

SHALL functional derived kotlin rust open in explorer · node

The issuer API SHALL require an explicit validity (exp, or a validity period) and SHALL have no default of "never expires". exp is the only revocation mechanism.

Engineering consequence of QR Code Generation, 4. Step 3 — Build the CWT claim set; HCERT Specification, 6.1 HCERT Signature Validity Time. QR Code Generation, 4. Step 3 — Build the CWT claim set: “exp is the only revocation mechanism this specification offers”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-CWT-004 · Validity inside the DSC validity

SHALL functional direct kotlin rust open in explorer · node

Before signing, the issuer SHALL check that iat is not before the DSC notBefore and exp is not after the DSC notAfter, and SHALL refuse to sign otherwise.

Restates QR Code Generation, 4. Step 3 — Build the CWT claim set; HCERT Specification, 3.2.5 Expiration Time; HCERT Specification, 3.2.6 Issued At. QR Code Generation, 4. Step 3 — Build the CWT claim set: “MUST NOT predate the DSC validity period ... MUST NOT exceed the DSC validity period”.

References

Realized by skills

Verified by tests

Test plans

STT-CWT-005 · iss is an optional alpha-2 string

SHALL functional derived kotlin rust open in explorer · node

iss SHALL be optional and SHALL be encoded as a text string. When present, the issuer SHALL require two upper-case letters, the alpha-2 form used in iss and in the DSC's X.509 C attribute.

Engineering consequence of HCERT Specification, 3.2.4 Issuer; QR Code Generation, 12.3 Test issuer. QR Code Generation, 12.3 Test issuer: “GDHCN participant codes are ISO 3166-1 alpha-3, while the CWT iss claim and the X.509 country attribute take the alpha-2 form”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-CWT-006 · Preserve unknown claims

SHALL functional derived kotlin rust open in explorer · node

The decoder SHALL keep CWT claims it does not recognise and expose them to the caller.

Engineering consequence of RFC 8392 CBOR Web Token (CWT); HCERT Specification, 3.2.7 Health Certificate Claim.

References

Realized by skills

Verified by tests

Test plans

CBR · Step 4: CBOR

STT-CBR-001 · Deterministic CBOR on encode

SHALL functional derived kotlin rust open in explorer · node

The encoder SHALL produce deterministic CBOR (RFC 8949 section 4.2.1 core deterministic encoding). For the HCERT key sets this matches the RFC 7049 canonical (length-first) ordering. Same input gives same bytes, in both languages.

Engineering consequence of QR Code Generation, 5. Step 4 — Serialise as CBOR; RFC 8949 CBOR (obsoletes RFC 7049).

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-CBR-002 · NFC normalisation

SHOULD functional direct kotlin rust open in explorer · node

The encoder SHOULD NFC-normalise text strings. The decoder SHALL accept strings that are not normalised, and comparison helpers SHALL behave as if both sides were normalised to NFC.

Restates QR Code Generation, 5. Step 4 — Serialise as CBOR; HCERT Specification, 3.2.7 Health Certificate Claim. QR Code Generation, 5. Step 4 — Serialise as CBOR: “Strings SHOULD be NFC-normalised. Decoders SHOULD be permissive about input that is not.”.

References

Realized by skills

Verified by tests

Test plans

STT-CBR-003 · Bounded CBOR decoding

SHALL security derived kotlin rust open in explorer · node

The CBOR decoder SHALL enforce configurable limits on nesting depth, item count and string/byte-string length, and SHALL reject malformed or truncated input with a typed error, without panicking or throwing unchecked exceptions.

Engineering consequence of HCERT Specification, 6.3 Input Data Validation. HCERT Specification, 6.3 Input Data Validation: “all input fields MUST be properly validated by data types, lengths and contents”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-CBR-004 · Wire format specified in CDDL

SHALL functional derived kotlin rust open in explorer · node

The CBOR wire format (COSE_Sign1, protected header, CWT claim set, hcert map and payloads) SHALL be specified in CDDL (RFC 8610), and encoders SHALL produce data that validates against it. The envelope SHALL be hand-written from the HCERT Specification; payload rules SHALL be generated from the logical models (STT-PAY-010).

Engineering consequence of Stakeholder directive: open standards for types and schemas; QR Code Generation, 5. Step 4 — Serialise as CBOR; HCERT Specification, 3.2.1 CWT Structure Overview; RFC 8610 Concise Data Definition Language (CDDL); RFC 9052 COSE: Structures and Process. QR Code Generation, 5. Step 4 — Serialise as CBOR: “The JSON shown throughout this page and in the logical models is schema notation only — the wire format is CBOR”.

References

Realized by skills

Verified by tests

Test plans

Open issues

COS · Step 5: COSE_Sign1

STT-COS-001 · COSE_Sign1 structure

SHALL functional direct kotlin rust open in explorer · node

The issuer SHALL emit 18([protected bstr, unprotected map, payload bstr, signature bstr]). The verifier SHALL accept the tagged form and SHOULD also accept an untagged COSE_Sign1 array.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; RFC 8152 CBOR Object Signing and Encryption (COSE). QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “The resulting structure is 18([protected, unprotected, payload, signature]).”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-COS-002 · Both ES256 and PS256

SHALL functional direct kotlin rust open in explorer · node

The library SHALL implement signing and verification for ES256 (alg -7, ECDSA P-256 with SHA-256) and PS256 (alg -37, RSASSA-PSS with SHA-256, MGF1 SHA-256, salt length 32, modulus at least 2048 bits).

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; HCERT Specification, 3.2.2 Signature Algorithm; RFC 8230 RSA algorithms with COSE (PS256). QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “Both algorithms MUST be implemented, even though only one is used to sign any given certificate”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-COS-003 · kid placement

SHALL functional direct kotlin rust open in explorer · node

The issuer SHALL put alg and kid in the protected header. The verifier SHALL accept a kid in either the protected or the unprotected header, and SHALL use the protected one when both are present.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; HCERT Specification, 3.2.3 Key Identifier. QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “verifiers MUST accept both placements — but when it appears in both, the protected one wins.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-COS-004 · Sig_structure

SHALL functional direct kotlin rust open in explorer · node

The signature input SHALL be the CBOR encoding of ["Signature1", protected header bytes exactly as transmitted, empty external_aad, payload bytes]. The verifier SHALL use the received protected bytes and SHALL NOT re-encode the decoded header.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; RFC 8152 CBOR Object Signing and Encryption (COSE). QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “serialise it as CBOR, and sign those bytes.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-COS-005 · Fixed-width r||s ECDSA signatures

SHALL functional direct kotlin rust open in explorer · node

ES256 signatures SHALL be carried as 64-byte r||s. The library SHALL convert from and to the ASN.1 DER form returned or expected by platform crypto APIs (JCA, Android Keystore, PKCS#11, RustCrypto). The verifier SHALL reject an ES256 signature that is not exactly 64 bytes.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1. QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “For ES256 the COSE signature value is the fixed-width concatenation r || s, 32 bytes each — not the ASN.1 DER encoding”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-COS-006 · No private keys required in-process

SHALL security derived kotlin rust open in explorer · node

The issuer SHALL NOT need access to private key material: issuance is prepare, then the caller signs the returned bytes by any means (HSM, KMS, PKCS#11, Android Keystore, WebCrypto), then finish (STT-ISS-004). A software ES256 and PS256 signer SHALL be provided for tests only, in the testkit module.

Engineering consequence of HCERT Specification, 6.2 Key Management. HCERT Specification, 6.2 Key Management: “Use of HSMs for signing health certificates is highly encouraged.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

KID · Key identifier

STT-KID-001 · kid derivation

SHALL functional direct kotlin rust open in explorer · node

kid SHALL be the first 8 bytes of SHA-256 over the DER-encoded DSC. The issuer SHALL derive it from the Signer's DSC, not take it from the caller.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; HCERT Specification, A.1 The Key Identifier (`kid`s). QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “take the DER encoding of the DSC and use the first 8 bytes of its SHA-256 fingerprint”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-KID-002 · kid is a lookup hint, not unique

SHALL functional direct kotlin rust open in explorer · node

Trust-list lookup by kid SHALL return every matching DSC, and the verifier SHALL try each candidate until one verifies. A kid match alone SHALL NOT count as authentication.

Restates QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1; HCERT Specification, 3.2.3 Key Identifier. QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “a verifier that finds several DSCs with the same kid MUST try all of them”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-KID-003 · kid text forms

SHALL api derived kotlin rust open in explorer · node

Kid SHALL convert to and from hex, standard base64 and URL-safe base64. Trust-list matching SHALL compare the kid bytes, not their text form.

Engineering consequence of QR Code Generation, 12.6 The certificates; GDHCN Trustlists (DID), Example DID documents. QR Code Generation, 12.6 The certificates: “Both certificates carry the key identifier kW/6VnwPRKI= (hex 916ffa567c0f44a2).”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

ZIP · Step 6: compression

STT-ZIP-001 · ZLIB/Deflate after signing

SHALL functional direct kotlin rust open in explorer · node

The issuer SHALL compress the COSE_Sign1 bytes with ZLIB (RFC 1950) using Deflate (RFC 1951) after signing. The verifier SHALL decompress before parsing COSE.

Restates QR Code Generation, 7. Step 6 — Compress; HCERT Specification, 4.2.1 Payload (CWT) Compression. QR Code Generation, 7. Step 6 — Compress: “Compression comes after signing, so the verifier decompresses before it verifies.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-ZIP-002 · Decompression bomb protection

SHALL security derived kotlin rust open in explorer · node

Inflation SHALL stop with a typed error once the output exceeds a configurable limit (DecodeLimits.maxInflatedBytes).

Engineering consequence of HCERT Specification, 6.3 Input Data Validation.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

B45 · Step 7: Base45

STT-B45-001 · Base45 per RFC 9285

SHALL functional direct kotlin rust open in explorer · node

Base45 encoding and decoding SHALL follow RFC 9285, the published form of the draft the IG cites.

Restates QR Code Generation, 8. Step 7 — Encode as Base45; HCERT Specification, 4.2.2 QR 2D Barcode; RFC 9285 The Base45 Data Encoding. QR Code Generation, 8. Step 7 — Encode as Base45: “its 45-character alphabet is exactly the QR Alphanumeric character set”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-B45-002 · Strict Base45 decoding

SHALL security derived kotlin rust open in explorer · node

The decoder SHALL reject characters outside the 45-character alphabet, a dangling single character, and triplets or pairs whose value overflows (above 65535, or above 255 for a trailing pair).

Engineering consequence of RFC 9285 The Base45 Data Encoding; HCERT Specification, 6.3 Input Data Validation.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

HC1 · Step 8: context identifier

STT-HC1-001 · HC1 prefix

SHALL functional direct kotlin rust open in explorer · node

The issuer SHALL prefix the Base45 string with "HC1:". The verifier SHALL require a context identifier matching HC[1-9A-Z]: and strip it. It SHALL report any identifier other than HC1 as an unsupported version, distinct from malformed input.

Restates QR Code Generation, 9. Step 8 — Prefix the context identifier; HCERT Specification, 4.2.2 QR 2D Barcode. QR Code Generation, 9. Step 8 — Prefix the context identifier: “A future backwards-incompatible revision will use a new identifier, incrementing through [1-9] and then [A-Z].”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

QR · Step 9: QR code

STT-QR-001 · QR-ready HC1 output; no QR encoding in the library

SHALL functional derived kotlin rust open in explorer · node

The HC1 string SHALL consist only of characters in the QR Alphanumeric set (the Base45 alphabet and the "HC1:" prefix), so that integrators can encode it as a single Alphanumeric-mode segment with error correction Q using any QR encoder. The libraries SHALL NOT encode or render QR symbols.

Restates QR Code Generation, 10. Step 9 — Render the QR code; HCERT Specification, 4.2.2 QR 2D Barcode; ISO/IEC 18004:2015 QR Code. HCERT Specification, 4.2.2 QR 2D Barcode: “The Alphanumeric (Mode 2/QR Code symbols 0010) MUST be used in conjunction with Base45.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-QR-005 · Camera scanning out of scope

SHALL scope derived kotlin rust open in explorer · node

Decoding QR images from a camera or file is out of scope for the core libraries. The verifier entry point SHALL be the scanned text (HC1 string) or raw COSE bytes.

Engineering consequence of QR Code Generation, 11. Verifying: the same steps upwards. QR Code Generation, 11. Verifying: the same steps upwards: “Confirm the HC1: prefix and strip it.”.

References

Realized by skills

VER · Verification

STT-VER-001 · Verification order

SHALL functional direct kotlin rust open in explorer · node

The verifier SHALL run the QR Code Generation section 11 sequence in order: prefix, Base45, inflate, COSE parse, header validation, trust lookup by kid, signature verification, EKU check, CWT decode and time checks, subclaim dispatch, business rules.

Restates QR Code Generation, 1. The pipeline at a glance; QR Code Generation, 11. Verifying: the same steps upwards. QR Code Generation, 1. The pipeline at a glance: “A verifier runs the same list upwards.”.

References

Satisfies (smart-trust)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VER-002 · No payload access before the signature is verified

SHALL security derived kotlin rust open in explorer · node

The API SHALL make the payload and claims reachable only through a successful verification result type. Code that inspects an unverified payload SHALL go through an explicitly named unsafe or diagnostic call.

Engineering consequence of QR Code Generation, 11. Verifying: the same steps upwards; HCERT Specification, 6.3 Input Data Validation. QR Code Generation, 11. Verifying: the same steps upwards: “Verify the signature before processing the payload.”. HCERT Specification, 6.3 Input Data Validation: “The Issuer Signature SHALL also be verified before any processing of the contents of the HCERT takes place.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VER-003 · Defensive protected-header parsing

SHALL security direct kotlin rust open in explorer · node

Before the signature is checked, the verifier SHALL validate the types and lengths in the protected header: alg is a supported integer, kid is a byte string of the expected length, and no duplicate labels. It SHALL fail closed on anything unexpected.

Restates QR Code Generation, 11. Verifying: the same steps upwards; HCERT Specification, 6.3 Input Data Validation. QR Code Generation, 11. Verifying: the same steps upwards: “Parsing the protected header necessarily happens before signature verification, which makes it an attack surface”.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

STT-VER-004 · EKU check against certificate type

SHALL functional derived kotlin rust open in explorer · node

If the matching DSC has an Extended Key Usage extension with any of 1.3.6.1.4.1.1847.2021.1.1 (test), .1.2 (vaccinations) or .1.3 (recovery), the verifier SHALL check it against the certificate type of each hcert entry: DVC (-6) and DDCC VS (3) are vaccinations, DDCC TR (4) is test, and a DCC (1) is the type of its single v, t or r entry. An entry whose type the EKU does not permit SHALL be returned marked as not authorised (keyUsageAuthorised false), with a diagnostic; verification SHALL fail with KeyUsageMismatch only when no entry is authorised. A DSC with no such extension SHALL be accepted for any type.

Engineering consequence of QR Code Generation, 11. Verifying: the same steps upwards; HCERT Specification, A.2 Extended Key Usage Identifiers. HCERT Specification, A.2 Extended Key Usage Identifiers: “If one or more are present, the verifiers SHALL verify the key usage against the stored HCERT.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VER-005 · Time checks with an injected clock

SHALL functional direct kotlin rust open in explorer · node

The verifier SHALL reject a certificate whose exp is in the past, and one whose iat is in the future beyond a configurable clock-skew tolerance. The current time SHALL be passed in by the caller (now), never read from an ambient clock.

Restates QR Code Generation, 11. Verifying: the same steps upwards; HCERT Specification, 3.2.5 Expiration Time; HCERT Specification, 3.2.6 Issued At. QR Code Generation, 11. Verifying: the same steps upwards: “Decode the CBOR payload and check exp and iat against the current time.”. HCERT Specification, 3.2.5 Expiration Time: “after which a Verifier MUST reject the Payload as expired”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VER-006 · Trust-list presence is the DSC validity check

SHALL functional direct kotlin rust open in explorer · node

The verifier SHALL NOT need CRL or OCSP access. A DSC is valid if it is on the current trust list. The verifier SHOULD also check the DSC's X.509 validity period against the injected clock.

Restates HCERT Specification, 5.1 Simplified SCA/DSC. HCERT Specification, 5.1 Simplified SCA/DSC: “the primary validity mechanism is the presence of the certificate on the most recent version of that certificate list.”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VER-007 · Dispatch to logical model, then business rules

SHALL functional direct kotlin rust open in explorer · node

After verification the verifier SHALL decode each hcert entry with the codec registered for its subclaim, falling back to RawPayload. It SHALL then run any registered BusinessRuleHooks and attach their outcomes to the result.

Restates QR Code Generation, 11. Verifying: the same steps upwards. QR Code Generation, 11. Verifying: the same steps upwards: “Read the hcert claim, dispatch on the subclaim to the right logical model, and only then apply business rules.”.

References

Satisfies (smart-trust)

Realized by skills

Verified by tests

Test plans

STT-VER-008 · Typed verification result and failure taxonomy

SHALL api derived kotlin rust open in explorer · node

A certificate failing verification SHALL produce a Failed result, not an exception or panic. Failed results SHALL name the step that failed and a reason from a closed set shared by both languages: BadPrefix, UnsupportedContextVersion, Base45Invalid, InflateFailed, LimitExceeded, CoseMalformed, HeaderInvalid, UnsupportedAlgorithm, KidNotFound, SignatureInvalid, KeyUsageMismatch, Expired, NotYetValid, HcertMissing, MultipleSubclaims, PayloadInvalid. Exceptions or errors SHALL be reserved for programming errors and infrastructure failures.

Engineering consequence of QR Code Generation, 11. Verifying: the same steps upwards; Stakeholder directive: Kotlin library first, Rust library second.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VER-009 · Fully offline verification

SHALL functional derived kotlin rust open in explorer · node

Given a cached or static trust list, verification SHALL need no network access.

Engineering consequence of QR Code Generation, 2.2 Choosing between a DVC and a VHL; QR Code Generation, 12.1 Scenario. QR Code Generation, 2.2 Choosing between a DVC and a VHL: “Works fully offline — Yes — verifier needs only the trust list”.

References

Realized by skills

Verified by tests

Test plans

STT-VER-011 · Unverified read for triage

SHALL functional directive kotlin rust open in explorer · node

The verifier SHALL offer an explicitly named unverified read (read-unverified) that decodes an HC1 string up to and including the payload without any trust lookup or signature check, for display and triage where verification is not possible, such as a community health worker offline at a household (journey J2). Its result type SHALL be distinct from a verified result, SHALL carry the kid, the claims and every hcert entry it found, and SHALL state that nothing in it is verified. It SHALL apply the same decode limits as verify.

Stakeholder directive, in the context of Stakeholder directive: the library must carry the workshop user journeys; QR Code Generation, 11. Verifying a certificate. Stakeholder directive: the library must carry the workshop user journeys: “lightweight QR scan reads basic vaccine metadata without requiring full cryptographic verification at the household edge”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

TRU · Trust list

STT-TRU-001 · Trust store

SHALL api derived kotlin rust open in explorer · node

Trust material SHALL be held in a trust store that the host fills with DSCs it has obtained, and that returns every trusted DSC matching a kid. The store SHALL do no network or file I/O (STT-GEN-012).

Engineering consequence of HCERT Specification, Appendix A - Trust Management; QR Code Generation, 11. Verifying: the same steps upwards. HCERT Specification, Appendix A - Trust Management: “Ultimately, every Verifier needs to have a list of all public keys it is willing to trust”.

References

Satisfies (smart-trust)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-TRU-002 · GDHCN DID trustlist v2 documents

SHALL functional direct kotlin rust open in explorer · node

The trust store SHALL load GDHCN DID trustlist v2 documents, embedded and reference variants, reading DSCs from verificationMethod publicKeyJwk (x5c) with the kid as the fragment. Fetching and caching the documents are the host's; checking their proofs is the host's choice, with the library's proof verification (STT-TRU-006) or its own.

Restates GDHCN Trustlists (DID), Trustlists 2.0.0; GDHCN Trustlists (DID), DID trustlists structure; GDHCN Trustlists (DID), Example DID documents; Endpoints, Trustlist Distribution Endpoints; W3C DID Core. GDHCN Trustlists (DID), DID trustlists structure: “Note that "-" character can be used as a wildcard on each sublevel of root.”. GDHCN Trustlists (DID), Example DID documents: “Specific keys use base64 encoded key id (kid) as identifier, It is represented as fragments (#) in verification method id”.

References

Satisfies (smart-trust)

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-TRU-005 · Static trust lists

SHALL functional direct kotlin rust open in explorer · node

The trust store SHALL load DSCs from PEM and DER, for tests, pinned bilateral or national lists, and fully offline deployments.

Restates HCERT Specification, Appendix A - Trust Management; RFC 7517 JSON Web Key. HCERT Specification, Appendix A - Trust Management: “the file format of this trusted list may vary, e.g., it can be a signed JWKS”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-TRU-006 · Trust list DID document proofs

SHALL functional derived kotlin rust open in explorer · node

Both libraries SHALL verify a GDHCN trust list DID document's JsonWebSignature2020 proof against the did:web document of its verificationMethod, which the host supplies: the URDNA2015 canonical N-Quads of the document without its proof and of the proof options (the proof without jws, with the security v3 context) are each hashed with SHA-256, and the detached b64:false ES256 JWS is verified over ASCII(header) || '.' || SHA-256(options) || SHA-256(document), accepting a DER or a 64-byte r||s signature. Canonicalization SHALL use pinned copies of the contexts the signer uses (did/v1, jws-2020/v1, the tng-context for DEV, UAT or production, security v3) and SHALL fail closed on any term, key or value shape the profile does not model, so that an unmodelled term can never be silently dropped from the signed bytes. The trust store SHALL offer an add that refuses a document whose proof does not verify, adding nothing.

Engineering consequence of the GDHCN trust list as served (DID trustlist v2 documents carrying a JsonWebSignature2020 proof by WHO's signing DID) and of the owner's decision of 2026-10-23 to verify those proofs in the libraries. The signing profile is that of WorldHealthOrganization/tng-key-distribution (ld-signatures-java 1.11.0); vectors in vectors/gdhcn-dev-trustlist.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

VHL · Verifiable Health Link

STT-VHL-001 · VHL payload in the SMART Health Links payload format

SHALL functional direct kotlin rust open in explorer · node

The library SHALL model the VHL payload as the SMART Health Links payload format that IHE VHL reuses: url (required, https), key (required, 43 base64url characters encoding 32 bytes), exp (optional integer, epoch seconds), flag (optional; the letters L and P in alphabetical order), label (optional, at most 80 characters), v (optional integer, default 1) and extension.fhirBaseUrl (optional). Validation SHALL report each violation. The single-file U flag is out of scope and SHALL be reported as unsupported.

Restates SMART Health Links; IHE VHL ITI-YY3, 2:3.YY3.4.1.3 Expected Actions; VerifiableHealthLinkPayload logical model (trust-phw); Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5. IHE VHL ITI-YY3 Generate VHL, 2:3.YY3.4.1.3 Expected Actions: “Create the VHL payload (using the SHL payload format)”. Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5: “Single file - ignore”.

References

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-002 · VHL payload at HCERT key 5 as a CBOR map

SHALL functional directive kotlin rust open in explorer · node

The issuer SHALL carry the VHL payload at HCERT claim -260 key 5 as a CBOR map with text keys, exp and v as CBOR integers. The verifier SHALL read key 5 as that map, and SHALL also accept a vhlink:/ text string there, so that VHLs built to IHE ITI-YY3 still verify.

Stakeholder directive, in the context of Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5; HCERT Specification, 3.2.7.1.5; IHE VHL ITI-YY3, 2:3.YY3.4.1.3 Expected Actions; IHE VHL ITI-YY4 Provide VHL. Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5: “CBOR map primary”. IHE VHL ITI-YY3 Generate VHL, 2:3.YY3.4.1.3 Expected Actions: “SHALL contain the VHL payload embedded in HCERT claim structure (claim key -260, key 5)”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-003 · vhlink:/ text form

SHALL functional direct kotlin rust open in explorer · node

The library SHALL convert a VHL payload to and from its text form: the minified JSON, base64url-encoded without padding (RFC 4648), prefixed with vhlink:/.

Restates IHE VHL ITI-YY3, 2:3.YY3.4.1.3 Expected Actions; RFC 4648. IHE VHL ITI-YY3 Generate VHL, 2:3.YY3.4.1.3 Expected Actions: “Prefixed with vhlink:/”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VHL-004 · VHL key generation

SHALL security derived kotlin rust open in explorer · node

The library SHALL generate a VHL key as 32 bytes from the platform's cryptographically secure random generator, encoded as 43 base64url characters.

Engineering consequence of IHE VHL ITI-YY3, 2:3.YY3.4.1.3 Expected Actions; SMART Health Links.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VHL-005 · ITI-YY5 manifest request

SHALL functional direct kotlin rust open in explorer · node

The library SHALL build the ITI-YY5 Retrieve Manifest request from a VHL payload: POST to <base>/List/_search (accepting a manifest URL of either the List?... or the List/_search?... form), Content-Type application/x-www-form-urlencoded, body holding the manifest URL's search parameters (_id, code, status and patient.identifier required; _include=List:item optional) plus recipient (required), passcode (required when, and only when, the payload's flag has P) and embeddedLengthMax (optional). It SHALL refuse a payload whose URL lacks a required parameter. Sending the request is the host's.

Restates IHE VHL ITI-YY5, 2:3.YY5.4.1.2 Message Semantics; Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5. IHE VHL ITI-YY5 Retrieve Manifest, 2:3.YY5.4.1.2 Message Semantics: “The VHL Receiver performs an HTTP POST to the _search endpoint with the manifest URL parameters from the VHL payload”. Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5: “Manifest -> go with IHE VHL”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-006 · ITI-YY5 manifest response

SHALL functional direct kotlin rust open in explorer · node

The library SHALL read the ITI-YY5 response, a FHIR searchset Bundle: the List with search.mode match and, when the Include DocumentReference option was used, the DocumentReferences with search.mode include. For each document it SHALL return the attachment URL and the decrypted content type (attachment.contentType); for List items whose DocumentReference is not included it SHALL return the reference for the host to read.

Restates IHE VHL ITI-YY5, 2:3.YY5.4.2.2 Message Semantics; IHE VHL ITI-YY5, 2:3.YY5.4.2.4 Document Content Retrieval. IHE VHL ITI-YY5 Retrieve Manifest, 2:3.YY5.4.2.2 Message Semantics: “the Retrieve Manifest response is — at the FHIR level — an MHD ITI-66 response message”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-007 · Document encryption: JWE dir / A256GCM

SHALL security direct kotlin rust open in explorer · node

Document binaries SHALL be encrypted and decrypted as JWE Compact Serialization (RFC 7516) with alg dir and enc A256GCM (RFC 7518), the VHL key as the content key and the protected header as additional authenticated data, following SMART Health Links. Decryption SHALL refuse any other alg or enc, SHALL accept zip DEF, and SHALL fail on a bad tag. Encryption SHALL set cty to the document's content type.

Restates IHE VHL ITI-YY5, 2:3.YY5.4.2.5 Document Encryption; RFC 7516; RFC 7518; Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5. IHE VHL ITI-YY5 Retrieve Manifest, 2:3.YY5.4.2.5 Document Encryption: “VHL document binaries are encrypted following the SMART Health Links — Encrypting and Decrypting Files convention”. Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5: “Encryption as VHL”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-008 · Receiver authentication: HTTP Message Signatures

SHALL security direct kotlin rust open in explorer · node

For the ITI-YY5 HTTP Message Signatures option the library SHALL compute Content-Digest (RFC 9530, sha-256, Structured Field byte sequence) and the RFC 9421 signature base over @method, @path, @authority, content-type and content-digest with created, keyid and alg parameters, return the bytes for the host to sign with the receiver's key, and assemble the Signature-Input and Signature headers from the result. For ecdsa-p256-sha256 the signature SHALL be the 64-byte r||s; a DER signature SHALL be converted.

Restates IHE VHL ITI-YY5, 2:3.YY5.4.1.3 HTTP Message Signatures; RFC 9421; RFC 9530; Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5. IHE VHL ITI-YY5 Retrieve Manifest, 2:3.YY5.4.1.3 Authentication Option - HTTP Message Signatures: “Implementations MAY authenticate using HTTP Message Signatures per RFC 9421”. Stakeholder directive: Verifiable Health Links follow IHE VHL, CBOR map at key 5: “Trust - IHE VHL”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

STT-VHL-009 · VHL expiry

SHALL functional direct kotlin rust open in explorer · node

Validating a VHL payload against a caller-supplied now SHALL report an expired link when the payload's exp has passed, in addition to the HCERT exp check made by the verifier.

Restates IHE VHL ITI-YY4 Provide VHL. IHE VHL ITI-YY4 Provide VHL: “Check the payload's exp (if present) - reject if current time > expiration”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

STT-VHL-010 · VHL with no I/O in the library

SHALL architecture derived kotlin rust open in explorer · node

The VHL functions SHALL do no network, file or clock access: the host sends the ITI-YY5 request and the ITI-68 document GETs over mutual TLS, holds the receiver's signing key, and logs AuditEvents. The library builds, parses, signs-by-proxy and decrypts.

Engineering consequence of IHE VHL ITI-YY5, 2:3.YY5.4.2.4 Document Content Retrieval; Stakeholder directive: the library must carry the workshop user journeys. IHE VHL ITI-YY5 Retrieve Manifest, 2:3.YY5.4.2.4 Document Content Retrieval: “Transport: Same ATNA secure channel used for ITI-YY5”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

ISS · Issuance

STT-ISS-003 · Test keys never in production artifacts

SHALL NOT security derived kotlin rust open in explorer · node

Fixed test keys and test PKI (such as the XXA SCA and DSC) SHALL only ship in the testkit module. Production modules SHALL NOT contain or accept them by default.

Engineering consequence of QR Code Generation, 12.3 Issuer. QR Code Generation, 12.3 Issuer: “The private key is held in the build environment and never enters this repository”.

References

Realized by skills

Verified by tests

Test plans

STT-ISS-004 · Two-phase signing

SHALL api derived kotlin rust open in explorer · node

Issuance SHALL be two-phase: prepare returns the exact bytes to sign (the Sig_structure), the caller signs them by any means, including asynchronous ones such as WebCrypto or a remote KMS, and finish assembles the certificate and returns the HC1 string and the COSE bytes. For ES256, finish SHALL accept r||s or DER and SHALL always emit r||s.

Engineering consequence of HCERT Specification, 6.2 Key Management; W3C Web Cryptography API; QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1. QR Code Generation, 6. Step 5 — Sign as a COSE_Sign1: “Converting between the two is the single most common interoperability bug in HCERT implementations.”.

References

Derived from (broader requirement)

Realized by skills

Constrains (WIT, CDDL)

Verified by tests

Test plans

Open issues

SEC · Security

STT-SEC-001 · No payload content in logs

SHALL NOT security derived kotlin rust open in explorer · node

The libraries SHALL NOT log payload content, HC1 strings or decoded claims at any default log level. A QR code holds readable personal data.

Engineering consequence of QR Code Generation, 2.2 Choosing between a DVC and a VHL; HCERT Specification, 6.1 HCERT Signature Validity Time. QR Code Generation, 2.2 Choosing between a DVC and a VHL: “All data is in the QR and is readable by anyone who scans it”.

References

Realized by skills

Verified by tests

Test plans

STT-SEC-002 · Vetted crypto backends

SHALL security derived kotlin rust open in explorer · node

Cryptography SHALL use platform or vetted libraries: JCA/BouncyCastle or Android Keystore for Kotlin JVM/Android, RustCrypto (p256, rsa, sha2) or ring/aws-lc-rs for Rust. Signature verification SHALL be constant-time where the backend allows. The libraries SHALL NOT contain their own implementations of cryptographic primitives.

Engineering consequence of HCERT Specification, 6.2 Key Management.

References

Realized by skills

Verified by tests

Test plans

NFR · Non-functional

STT-NFR-001 · Versioning and spec traceability

SHALL non-functional derived kotlin rust open in explorer · node

Releases SHALL follow semantic versioning. A change to the wire format or to subclaim defaults SHALL be a major or minor version change, as documented in a changelog that cites the smart-trust change (changes.md entry) it implements.

Engineering consequence of Change Log, v1.7.5; QR Code Generation, 9. Step 8 — Prefix the context identifier.

References

Derived from (broader requirement)

Realized by skills

STT-NFR-002 · Distribution

SHALL non-functional directive kotlin rust open in explorer · node

The Kotlin library SHALL be published to Maven Central (JVM, Android AAR) and to npm (Kotlin/JS). The Rust library SHALL be published to crates.io, and its jco-transpiled WebAssembly component to npm. All coordinates are still to be chosen.

Stakeholder directive, in the context of Stakeholder directive: Kotlin library first, Rust library second; Stakeholder directive: transpile targets and API-first.

References

Realized by skills

Open issues

TST · Conformance testing

STT-TST-001 · PR 129 example as a conformance vector

SHALL test derived kotlin rust open in explorer · node

Both libraries SHALL verify the two HC1 strings in dvc-qr-example-XXA.json against dvc-qr-example-XXA-dsc.pem, with the clock fixed inside 2026-09-29T08:30Z to 2027-09-29T08:30Z. Each SHALL give kid WHM+BYqqZiE=, subclaim -6, and payloads equal to those in the JSON. The committed files SHALL be used as-is; they SHALL NOT be regenerated.

Engineering consequence of the DVC QR example as published on PR 129's branch (WHO 50bcb25, test participant XXA): its machine-readable summary and test DSC. The merged guide (af75be2) reissued the example under DEV participant XXR without publishing the DSC (STT-TST-011), so this pair remains the end-to-end verification vector.

References

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-002 · Byte-exact CBOR vector

SHALL test derived kotlin rust open in explorer · node

Encoding a published example's claim set with deterministic CBOR SHALL give exactly the payload bytes inside its COSE_Sign1: 222 bytes for the XXR hepb1 certificate and 221 for bcg, 210 and 209 for the XXA pair. The merged guide's XXR certificates are published as fixed strings, so their COSE_Sign1, deflated and HC1 sizes SHALL equal those the vector records, which for hepatitis B are the 307, 293 and 444 that section 12.5 states. Sizes of newly issued certificates SHALL NOT be asserted exactly, because signatures are randomized.

Engineering consequence of QR Code Generation, 12.5 Size at each stage, as merged (WHO af75be2), and of the PR 129 branch's machine-readable summary (test participant XXA).

References

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-003 · Cross-implementation interoperability

SHALL test derived kotlin rust open in explorer · node

Certificates issued by each library (ES256 and PS256) SHALL verify in the other library, and the two libraries SHALL build the same Sig_structure and COSE_Sign1 bytes from the same request and signature.

Engineering consequence of the stakeholder directive: Kotlin library first, Rust library second. The reference-script clause was dropped on 2026-10-22 (owner's decision): WHO PR 129 removed generate_dvc_qr.py from the guide, and its certificates are already covered by the PR 129 vector (STT-TST-001).

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-004 · Negative vectors

SHALL test derived kotlin rust open in explorer · node

The shared vector set SHALL include failing cases with the expected failure reason for each: tampered payload, tampered protected header, wrong or unknown kid, duplicate kid with only the second DSC valid, a DER-encoded ES256 signature, expired, iat before the DSC validity, EKU mismatch, missing or unsupported prefix (HC2:), invalid Base45, decompression bomb, deeply nested CBOR, multiple subclaims, and a DVC without is or cn.

Engineering consequence of QR Code Generation, 11. Verifying: the same steps upwards; HCERT Specification, 6.3 Input Data Validation.

References

Realized by skills

Verified by tests

Test plans

STT-TST-005 · Language-neutral vector format

SHALL test derived kotlin rust open in explorer · node

Conformance vectors SHALL be stored once, in a language-neutral format (JSON plus PEM), in this repository, and loaded by both test suites.

Engineering consequence of DVC QR example, machine-readable summary (test participant XXA); Stakeholder directive: Kotlin library first, Rust library second.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-006 · Fuzzing of decoders

SHALL test derived kotlin rust open in explorer · node

The Base45, ZLIB, CBOR, COSE and full verifier entry points SHALL be fuzzed in CI. No input may crash them, hang them, or exceed DecodeLimits. Rust SHALL use a coverage-guided fuzzer (cargo-fuzz). Kotlin SHALL use a seeded mutation fuzzer in its own test suite, which needs no dependency; a coverage-guided JVM fuzzer (Jazzer) MAY replace it once it can be pinned under the dependency policy.

Engineering consequence of HCERT Specification, 6.3 Input Data Validation. HCERT Specification, 6.3 Input Data Validation: “a potential attacker may attempt to inject carefully crafted information designed to compromise the security of the system”.

References

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-007 · One TypeScript conformance suite for both Node packages

SHALL test derived kotlin rust open in explorer · node

A single TypeScript test suite, type-checked against api/generated/ts and driven by the shared vectors, SHALL run in CI against both the Kotlin/JS npm package and the jco-transpiled Rust component, and SHALL pass on both.

Engineering consequence of Stakeholder directive: transpile targets and API-first; DVC QR example, machine-readable summary (test participant XXA).

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

Open issues

STT-TST-008 · Schema checks against the vectors

SHALL test derived kotlin rust open in explorer · node

CI SHALL validate every conformance vector against the CDDL (COSE_Sign1, protected header, CWT claims) and its payload against the SHACL shapes through the JSON-LD context. It SHALL also check that mutated payloads (invariant broken, bad FHIR date, missing required element, bad FHIR id) are rejected and that valid variants (cn only, partial date) are accepted, by both CDDL and SHACL.

Engineering consequence of DVC QR example, machine-readable summary (test participant XXA); RFC 8610 Concise Data Definition Language (CDDL); W3C Shapes Constraint Language (SHACL).

References

Realized by skills

Verified by tests

Test plans

STT-TST-009 · Agreement with the WHO Interoperability Test Bed

SHALL test derived kotlin rust open in explorer · node

Each library SHALL reach the same result as the WHO-ITB gdhcn-helper on every WHO-ITB HCERT track's test data: the same COSE bytes, kid, issuer and signature verdict, and, for the VHL track, the same link, manifest and decrypted content. FHIR profile validation and StructureMap transforms stay with the ITB; the library's part ends at the payload claim it hands over.

Engineering consequence of the WHO-ITB HCERT test suites (Tracks 1 to 3), the conformance tests WHO runs for GDHCN trust network participants. A library that disagrees with the ITB on a track's decode or verification would fail participants the ITB passes.

References

Derived from (broader requirement)

Realized by skills

Test plans

Open issues

STT-TST-010 · Coverage floor

SHALL test derived kotlin rust open in explorer · node

Each library's CI SHALL measure the coverage of its core by its own tests, line and branch coverage where its stable toolchain measures branches and line and region coverage where it does not, and SHALL fail when any measure falls below a recorded floor. A floor SHALL be raised as tests are added and SHALL NOT be lowered to let a change pass.

Engineering consequence of the test plan hcert-library-conformance: a named test per requirement shows that each requirement is exercised, not that each branch of the decoders is. Coverage measured in CI, with a floor that only rises, keeps new code from arriving untested.

References

Derived from (broader requirement)

Realized by skills

Verified by tests

Test plans

STT-TST-011 · The merged guide's example decodes as published

SHALL test derived kotlin rust open in explorer · node

Both libraries SHALL decode the two HC1 strings of QR Code Generation 12.6 without verifying them, and SHALL read kid kW/6VnwPRKI=, iss XR, subclaim -6 and the payloads of 12.4, with the PreQual product identifiers in vp. Without the XXR DSC, verification SHALL stop at TRUST_LOOKUP with KID_NOT_FOUND. With XXR's DSC DID document from the GDHCN DEV trust list in the trust store, both certificates SHALL verify, with the payload's key usage authorised and no diagnostics.

Engineering consequence of QR Code Generation, 12.6 The certificates, as merged (WHO af75be2): two DVC certificates issued by GDHCN DEV participant XXR, whose DSC is on the DEV trust list. QR Code Generation, 12.6 The certificates: “To verify them, resolve that kid against the GDHCN trust list rather than against any copy of the certificate shipped beside the QR code.”.

References

Realized by skills

Verified by tests

Test plans

Open issues