Skip to content

Run DIF did:webvh compliance test vectors; report divergences (#134) - #140

Merged
moisesja merged 1 commit into
mainfrom
feat/issue-134-webvh-compliance-vectors
Aug 22, 2026
Merged

Run DIF did:webvh compliance test vectors; report divergences (#134)#140
moisesja merged 1 commit into
mainfrom
feat/issue-134-webvh-compliance-vectors

Conversation

@moisesja

Copy link
Copy Markdown
Owner

Reconnaissance pass on #134: replay the DIF didwebvh-test-suite committed vectors through our resolver and report divergences. Building the Docker harness that would register NetDid as implementations/dotnet/ is deliberately deferred until these findings are triaged.

Suite pinned at f792ce4 (2026-07-30).

Why this was worth doing

Every did:webvh test in this repo is self-generated — NetDid writes a log, NetDid reads it back. That shape passes even when the wire format is wrong for everyone else, which is exactly how #95 survived a fully green suite. This replays logs authored by six independent implementations instead.

It immediately found the same class of bug again (#135).

What's in the PR

tools/NetDid.Tools.WebVhVectors — replays committed vectors offline through DidWebVhMethod via a file-backed IWebVhHttpClient, and writes tasks/webvh-vector-divergence.md.

No src/ changes, no test-count change, no version bump. Findings are filed as issues; fixes are separate work.

  • Suite cloned on demand to a gitignored path, not vendored — so its own CLAUDE.md and .claude/settings.json cannot be picked up as this repo's agent instructions, and its contents are treated as untrusted data (read as bytes, fed to the resolver, never executed).
  • IsPackable=false, verified absent from dotnet pack output — publish.yml packs the whole solution, so this matters.
  • Report carries no generation timestamp; re-running produces a byte-identical file (verified), avoiding the commit churn documented for w3c-conformance-report.md.
  • Zero PackageReferences — the script.yaml scanner is hand-rolled to avoid adding a YAML dependency to solution-wide CPM, and is guarded by a self-check that cross-references every negative scenario's parsed expectError against the committed result, throwing rather than letting a parser bug masquerade as a conformance finding.

Results

88 happy-path replays (13 scenarios × 6 implementations, including historical versions) and 21 negative vectors.

Confirmed positive — our wire format interoperates

63 foreign logs accepted, with zero rejections attributable to a hash or signature mismatch. Resolution recomputes the SCID from the genesis preimage and every entry hash in the chain (LogChainValidator.cs:136-145, :194) and verifies eddsa-jcs-2022 proofs — so accepting a log authored by ts/rust/java/java-eecc/dart is direct evidence our encoding matches theirs. First independent confirmation of the #95 fix; settles #35.

All 21 negative vectors rejected, including path traversal, percent-encoded traversal, and percent-encoded IP-literal SSRF probes against 127.0.0.1 and the 169.254.169.254 cloud metadata endpoint.

Issues filed

# Severity Finding
#135 Critical did-witness.json read/written with proofs; spec and all 6 implementations use proof
#136 High Implicit #files / #whois services neither materialized nor resolvable
#137 Medium didDocumentMetadata omits scid (unanimous) and versionNumber
#138 Medium didDocumentMetadata.updated omitted at version 1
#139 Medium Malformed/unsafe identifiers resolve as notFound instead of invalidDid

#135 is the #95 pattern repeating. WitnessValidator.cs:196 writes "proofs" and :272 reads it, so NetDid witness files round-trip against themselves while no witnessed DID interoperates in either direction. A blanket catch { return null; } at :263-266 swallows the KeyNotFoundException, which is why it stayed hidden. Two negative witness vectors currently pass vacuously — parsing fails before any witness security logic runs.

Evidence added to existing issues

Not our defect

All 15 python logs are rejected because their verification methods omit controller. Our strictness is deliberate (#121) and backed by W3C DID Core §5.3.1, and 5 of 6 implementations emit it — didwebvh-py is the outlier. Worth reporting upstream; not filed, as that's an outward-facing action on a third-party repo.

Verification

  • dotnet build netdid.sln -c Release → 0 warnings, 0 errors
  • dotnet test netdid.sln -c Release → 1709 passed, 0 failed, 7 skipped (Ethr integration, Docker-gated); counts unchanged from main
  • dotnet pack → 6 packages, tool absent
  • Samples run end-to-end; w3c-conformance-report.md unchanged
  • Two consecutive harness runs produce byte-identical reports

Reproduce:

git clone https://github.com/decentralized-identity/didwebvh-test-suite.git \
  tools/NetDid.Tools.WebVhVectors/suite
dotnet run --project tools/NetDid.Tools.WebVhVectors

Closes #134.

🤖 Generated with Claude Code

…ces (#134)

Adds tools/NetDid.Tools.WebVhVectors, which replays the DIF didwebvh-test-suite's
committed vectors through DidWebVhMethod offline via a file-backed IWebVhHttpClient,
and writes tasks/webvh-vector-divergence.md.

Every did:webvh test in this repo is self-generated — NetDid writes a log, NetDid
reads it back — a shape that passes even when the wire format is wrong for everyone
else. That is how #95 survived a green suite. This replays logs authored by six
independent implementations instead.

Reconnaissance only: no src/ changes, no test-count change, no version bump. The
suite is cloned on demand to a gitignored path rather than vendored, so its own
CLAUDE.md and .claude/settings.json cannot be picked up as repo instructions, and
its contents are treated as untrusted data. IsPackable=false, verified absent from
dotnet pack output. The report carries no generation timestamp, so re-running
produces a byte-identical file.

88 happy-path replays (13 scenarios x 6 impls) and 21 negative vectors.

Confirmed positive: 63 foreign logs accepted with zero rejections attributable to a
hash or signature mismatch. Resolution recomputes the SCID and every entry hash and
verifies eddsa-jcs-2022 proofs, so this is the first independent evidence our wire
format interoperates (settles #35, confirms the #95 fix). All 21 negative vectors
rejected, including traversal and percent-encoded IP-literal SSRF probes.

Filed: #135 (did-witness.json uses `proofs`, spec and all six impls use `proof` —
no witnessed DID interoperates either way; the #95 pattern repeating), #136 (implicit
#files/#whois services missing), #137 (metadata omits scid/versionNumber), #138
(updated omitted at v1), #139 (malformed identifiers reported as notFound).

Evidence added to #35, #123, #131. Notably #131 is NOT covered by the suite: the
committed negative-versiontime-future log is also non-monotonic, so our existing
monotonicity rule rejects it before any future-skew bound applies.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@moisesja moisesja self-assigned this Aug 22, 2026
@moisesja moisesja added this to net-did Aug 22, 2026
@moisesja moisesja added this to the 3.3.0 milestone Aug 22, 2026
@github-project-automation github-project-automation Bot moved this to Backlog in net-did Aug 22, 2026
@moisesja moisesja moved this from Backlog to In review in net-did Aug 22, 2026
@moisesja
moisesja merged commit f54c733 into main Aug 22, 2026
1 check passed
@moisesja
moisesja deleted the feat/issue-134-webvh-compliance-vectors branch August 22, 2026 20:06
@github-project-automation github-project-automation Bot moved this from In review to Done in net-did Aug 22, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

Run Compliance Test Vectors for the did:webvh Method

1 participant