Skip to content

conformance: tests.lock pins the case census, not the assertions; vendored Wycheproof vectors have no provenance pin #83

Description

@lann

conformance/README.md:32-36 says the lockfile means "case changes land intentionally via just update-conformance-lock with a reviewable diff." A reader reasonably concludes the suite's assertions are pinned. They are not.

render_lock (adapters/wasmtime/src/main.rs:233-250) emits exactly { name, features } per case, and the runner validates exactly name-set and tag equality (runner/src/main.rs:186-197). The lock is an inventory, and it is a good one — missing/extra/duplicate case, tag drift and missing-features drift are all caught in both directions. What it does not pin:

1. Vector bytes have no provenance. conformance/vectors/*.json are plain checked-in files. There is no checksum, no upstream Wycheproof commit pin, and no verification anywhere in the repo. Editing a tag field in hmac_sha256_test.json to match a buggy implementation produces zero lockfile diff and a green run. For a suite whose entire authority derives from being Wycheproof, that is the load-bearing gap.

2. Assertion strength is unpinned. Weakening Err(Error::AuthenticationFailed) => Ok(()) to Err(_) => Ok(()) anywhere in vectors.rs/probes.rs changes no case name and no tag; the lock is byte-identical. The suite's real strength today is that it discriminates specific error variants (vectors.rs:67, :140, :149, :169, :222, :248, :281; probes.rs:204, :257, :294, :427, :706, :891) — and nothing protects that property.

3. Hardcoded criteria are unpinned. SPECCHECK_VALID_CASE = 3 (translate.rs:690) is a magic index governing the entire Ed25519 verification-criterion claim.

Related, and worth stating because conformance/matrix.md publishes the counts: the ivSize != 96 ⇒ invalid-nonce rule (vectors/README.md:58) converts 80 of the 371 AES-GCM cases — including all 36 CounterWrap vectors — from cryptographic assertions into a nonce-length check. Correct per the WIT, honestly documented, but the matrix cell reads as more cryptographic coverage than it is.

Also: the deserialization structs (translate.rs:393-430, :715-746) declare no flags field and no deny_unknown_fields, so every Wycheproof flag (SmallIv, CounterWrap, EdgeCasePoly1305, SignatureMalleability, EdgeCasePublicKey, …) is silently discarded. Consequences: a failure message like aes-gcm/wycheproof/tc42/bytes carries no vector semantics, and a vector refresh introducing a new flag class needing different treatment is adopted with no policy review. Contrast is_valid's explicit panic on an unknown result (translate.rs:436-442) — that is the right pattern.

  • Pin vector provenance: record the upstream Wycheproof commit and per-file digests, and verify them in the build (or vendor via a lockfile).
  • Either extend the lock to cover assertion shape, or correct conformance/README.md:32-36 to say the lock pins the case inventory only.
  • Carry flags through into case metadata so failures are self-describing, and reject unknown flags loudly the way unknown results already are.

From the 2026-07 adversarial review.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions