Skip to content

Testing & evidence

How quality is measured, and where the evidence behind every claim lives.

Testing & software quality

Every important failure mode becomes a test.

699/701 Rust workspace tests and 173 Swift shell tests pass against real aws-lc-rs cryptography (2 existing Rust tests ignored), run 2026-08-10. Organised below by assurance purpose, not framework.

Unit & state-machine tests

Tested

Domain rules, guard functions, transitions, encoding/decoding, boundary conditions — every protocol guard maps 1:1 to a named abort reason and has a test.

  • transitions.rs (per-machine transition tables)crates/oid4vp · oid4vci · payment · qes · w2w · iso18013-5
  • rejects_non_canonical / rejects_duplicate_map_keys / rejects_trailing_bytescrates/cose
  • binding_is_specific_to_receiver_and_credentialcrates/w2w

Model-conformance (oracle replay)

Verified

The six core Lean models emit transition traces; six Rust suites replay them against the production machines, byte-for-byte. The seventh model (app-shell navigation) is bound to the shipped Swift by an exhaustive (state × event) conformance test. Android has hosted shell tests but still needs its own formal shell model and exhaustive Kotlin conformance suite. CI regenerates traces and fails on staleness — no platform may claim formal parity without that binding.

  • conformance.rs × 6crates/{oid4vp,payment,iso18013-5,oid4vci,qes,w2w}/tests

Integration & end-to-end

Tested

Full flows through the sans-IO core with real aws-lc-rs cryptography: issue → hold → present over live TCP sockets; payment SCA; QES; wallet-to-wallet; status blocking; export integrity; FFI JSON contract.

  • e2e_live_presentation.rs / e2e_live_lifecycle.rs (real sockets, RP verifies)crates/shell-io/tests
  • e2e_flow / e2e_issuance / e2e_payment / e2e_status / qes_flow / w2w_flow / add_credentialcrates/wallet-core/tests
  • 177 Swift tests: effects, QR, native journeys, identity transports, recovery, Lean conformanceios/Tests/WalletShellTests

Adversarial & negative tests

Tested

51named cases

Every important failure mode becomes a test: replay, wrong nonce/audience, untrusted issuer, revoked/expired credentials, tampered transactions, forged codes, malformed input, misdirected transfers, alg:none.

Distinct test functions whose names encode a rejection/attack case (rejects_*, *replay*, *tamper*, *forged*, *mismatch*, *untrusted*, *revoked*, *invalid*, *malformed*), counted by grep — a subset of the workspace tests.

  • rts_art4_code_cannot_be_forgedcrates/crypto-backend/tests/regulatory_sca.rs
  • rejects_a_misdirected_transfer_in_corecrates/wallet-core/tests/w2w_flow.rs
  • export_round_trips_and_detects_tamperingcrates/wallet-core/tests/export.rs
  • rejects_alg_nonecrates/sdjwt

Fuzzing & bounded proofs

Tested

4fuzz targets

Coverage-guided fuzzing of the four codec attack surfaces; Kani bounded model checking on the mdoc codec. Both run in CI (bounded time per run).

cargo-fuzz targets in fuzz/fuzz_targets/.

  • cose_cbor · mdoc_cbor · sdjwt_parse · x509_parsefuzz/fuzz_targets/
  • Kani bounded proofs (cargo kani -p mdoc).github/workflows/ci.yml (tier1-fuzz-kani)

Static analysis & supply chain

Tested

clippy -D warnings across all targets/features (PASS, 2026-08-10); rustfmt gate; cargo-deny (licenses/bans/sources/advisories) with a written dependency budget; cargo-audit; and a PUBLISHED CycloneDX SBOM (21 crates; wallet-core = 111 runtime components).

  • clippy PASS — verification reportdocs/certification-evidence/verification-report.md
  • deny.toml (2 documented build-time-only ignores)deny.toml
  • Published SBOM (CycloneDX 1.3)docs/certification-evidence/sbom/

Mutation testing

Tested

73viable mutants caught

cargo-mutants mutates the security-critical OpenID4VP presentation crate and checks the tests catch each change — measuring whether tests constrain behaviour, not just execute it. The initial run caught 52/73 viable mutants (71%); the 21 misses were real test gaps, closed by adding crate-local tests. Re-run: 73/73 viable caught, 0 missed.

Viable mutants caught / total viable, cargo-mutants v27.1.0 on crates/oid4vp with the crate-local test suite (2026-07-19). 8 unviable (non-compiling) mutants excluded per standard practice. Scope: oid4vp only — a test-adequacy measure, not a correctness proof.

  • cargo mutants -p oid4vpdocs/certification-evidence/mutation-testing.md

Performance benchmarks

Tested

Dependency-free micro-benchmarks of the hot paths against the real aws-lc-rs backend (ES256 sign ~28.7µs / verify ~83.6µs, SHA-256 ~58ns, SD-JWT parse ~430ns; release, Apple Silicon, 2026-07-19). A flow is dominated by one or two ES256 ops plus one TLS round trip.

  • cargo run -p benches --releasedocs/certification-evidence/perf-benchmarks.md

Interoperability probe

Partial

A reproducible probe of the EUDI reference environment over platform TLS: the reference issuer's live OpenID4VCI metadata (HTTP 200, 27 configurations — 11 in the SD-JWT VC format this wallet implements) and reference-verifier reachability. Reachability + wire-shape, NOT a conformance pass.

  • tools/interop/probe.shdocs/certification-evidence/interop.md

Try it yourself — the presentation routes

Partial

The wallet presents over three channels: OpenID4VP by QR / deep link, in-person over Bluetooth (ISO 18013-5), and — the primary route for most users — the W3C Digital Credentials API, where a website calls navigator.credentials.get({ digital: … }) and the OS routes the request to the wallet. The DC-API core is implemented and tested (the byte-exact OpenID4VPDCAPIHandover binds the response to the browser-verified Origin, and an end-to-end test drives request → consent → a minimised mdoc vp_token). The iOS provider that surfaces this to Safari is being wired next; until it lands you can still see the OS route a request to the wallet.

  • Digital Credentials API — open a DC-API verifier page in a supporting browser (Safari on iOS 26, or Chrome on Android) and request an mdoc/PID. Neutral test verifiers: the eudiw.dev reference verifier and Google's Digital Credentials API test pages both drive navigator.credentials.get({ digital }).
  • OpenID4VP by QR / deep link — works today: build a request on the VCVerifier page and scan its openid4vp:// link with the wallet.
  • In person (Bluetooth, ISO 18013-5) — tap “Present in person”, show the engagement QR to a compatible mdoc reader; the wallet returns a minimised, encrypted DeviceResponse.

The PID is issued and held in BOTH mdoc and SD-JWT VC form (per the ARF), so a verifier can ask for either format over any of these channels.

Coverage, honestly Code coverage (line/branch/function) has not been measured yet — the number is deliberately absent rather than implied. Coverage also would not prove correctness: the complementary assurance here is machine-checked invariants (Lean), symbolic protocol analysis (Tamarin), oracle-replay conformance binding model to code, adversarial tests, and fuzzing.

Requires an external party (not closeable by our code)

  • Independent penetration test (accredited security assessor)
  • Accessibility audit of the mobile app (the landing page itself targets WCAG 2.2 AA; a formal audit of the iOS app is external)
  • OpenID Foundation conformance certification (their harness; self-certification program opened 2026-02-26)
  • CAB certification under CIR (EU) 2024/2981 + EUCC, and wallet-provider / Member-State registration

Planned (codeable) — not yet done, no result implied

  • Completed live issuance / presentation round trip against issuer.eudiw.dev / verifier.eudiw.dev — automatable as an XCUITest driving the app (pre-authorized-code issuance + direct_post presentation) with the reference trust anchors pinned as fixtures; the interactive browser-eID grant is the one step that stays manual or mocked, so it runs as a nightly integration job, not a gating unit test
  • Live mso_mdoc issuance against an external issuer
  • Endurance / soak (hours-long) load tests and device-hardware latency numbers
  • Line/branch coverage report

Future interoperability role

A conformant wallet becomes a known-good test counterpart.

Conditional future capability: every applicable local gate is green, the tested build is version-pinned, and official OpenID/EUDI conformance results for the exercised profiles are published.

At that point this wallet becomes a known-good protocol counterpart for teams building the other side in parallel. They can test against a stable, independently evidenced participant instead of waiting for every issuer, verifier and peer wallet to mature at the same time.

Issuer under test

Drive a conformant wallet through metadata discovery, PAR/PKCE, proof, credential response, portrait/profile validation and status handling; attribute a failure to the issuer-facing boundary.

Verifier under test

Send signed OpenID4VP/DCQL requests to the wallet and verify its SD-JWT or mdoc response, including encrypted direct_post.jwt, nonce, certificate binding and minimised disclosure.

Wallet or transport peer under test

Exercise wallet-to-wallet and proximity handshakes against pinned happy-path and hostile traces, including replay, substitution, interruption and recovery cases.

Reproducible cross-implementation loop

  1. 1.Pin the wallet commit, profile versions, trust anchors, fixtures and expected capabilities.
  2. 2.Run the same happy-path and negative corpus against every parallel implementation.
  3. 3.Capture redacted wire transcripts, hashes, state transitions and exact pass/fail reasons.
  4. 4.Compare outcomes differentially: conformant peers should agree; divergence becomes a small, reproducible case.
  5. 5.Promote every resolved interoperability defect into a permanent cross-implementation regression test.