Skip to content
CredenShare
SDKs

Conformance vectors

The fixture every CredenShare client must reproduce — what it covers, how to run it, and what it deliberately does not test.

The CredenShare application and the four SDKs share no code. That is deliberate: a package the production application depended on would be a supply-chain surface, and for a product whose claim is that we cannot read your data, it is the wrong surface to open.

The cost of that decision is drift between five independent implementations. Drift here does not produce a test failure — it produces content that can never be decrypted. The mitigation is a specification plus a fixture that every implementation must reproduce byte for byte.

What ships

Every client embeds the same fixture inside the installed artifact, so you can check the exact build you installed rather than a copy in a repository you are trusting to match.

ClientPathMechanism
Nodeconformance-vectors.jsonimported with with { type: 'json' }, so it works where there is no filesystem
Pythonvectors.v1.jsonpackage data
Govectors.v1.json//go:embed, so it travels inside the compiled binary
Rustsrc/vectors.v1.jsoninclude_str!, re-exported as conformance::VECTORS_JSON
The Node package names its copy conformance-vectors.json; the other three use vectors.v1.json. The bytes are identical — 7,849 bytes, SHA-256 91e70661be51edbc4522d202c533292d1eac92691d1fbb02e9eaa13eb23a582c in all four. Only the filename differs, which matters if you are hashing the path in CI.

Each repository pins that digest in its own test suite, so editing the fixture in one client fails that client's build rather than silently redefining the standard.

Running it

Node
npx --package @credenshare/sdk credenshare-conformance
node dist/conformance-cli.js          # from a built clone
Python
python -m credenshare.conformance
Go
go run github.com/CredenShare/credenshare-sdk-go/cmd/credenshare-conformance@latest
Rust
cargo run --bin credenshare-conformance     # from a clone only

All four take -v for one line per check — under cargo write -- -v, since a bare -v is consumed by cargo itself. None needs a test runner, and all exit non-zero on failure, so they work as a deployment gate. Run one in the environment that will actually do the encrypting — a client that fails these produces content nothing else can read, and the failure is otherwise invisible until somebody opens a link.

Cargo does not expose a dependency's binary targets, so Rust projects that merely depend on the crate should call credenshare::conformance::run from their own test suite instead. All four expose the checks programmatically for that purpose — Node from its ./conformance subpath export, Python as credenshare.conformance, Go as ConformanceChecks(), and Rust as conformance::run.

The 24 checks

hkdf/empty salt, short info
hkdf/16-byte salt
hkdf/output longer than one SHA-256 block, exercising the counter
fragment/encode
fragment/decode
fragment/rejects/0/missing-key
fragment/rejects/1/malformed-key
fragment/rejects/2/malformed-key
access_token
passcode_verifier/0
passcode_verifier/1
passcode_verifier/2
content/no passcode/encrypt
content/no passcode/decrypt
content/passcode mixed into info, not into the salt/encrypt
content/passcode mixed into info, not into the salt/decrypt
content/characters a JSON serialiser may mangle: HTML escapes, quotes, unicode/encrypt
content/characters a JSON serialiser may mangle: HTML escapes, quotes, unicode/decrypt
seed_keypair/recipient seed
seed_keypair/ephemeral seed
custody_keypair
ecdh_wrap/wrap
ecdh_wrap/unwrap
ecdh_wrap/roundtrip

Five of those — fragment/decode, the three content/*/decrypt cases and ecdh_wrap/unwrap — read material the fixture generator produced, rather than checking a client against itself. Those are the ones that actually demonstrate interoperability: passing them means this client can read what another one wrote. The remaining nineteen catch drift earlier and more precisely, which is why both directions are there.

Three cases are worth singling out. The first two were written into the original fixture as pins on the parts of the spec easiest to get wrong; the third was added later, and caught a live bug immediately:

  • hkdf/empty salt, short info — an empty salt is a zero-length byte string, not a block of zero bytes. An implementation that pads an absent salt produces different output. Pinned preemptively.
  • content/passcode mixed into info, not into the salt — the passcode belongs in the HKDF info, never in the salt. Also pinned preemptively.
  • characters a JSON serialiser may mangle — HTML-escapable characters, quotes and non-ASCII text. The fixture was all ASCII until this case was added, and it immediately caught a real divergence: one client was escaping every non-ASCII character on the wire.

What it does not cover

Worth stating plainly, because a green run is easy to over-read:

  • No webhook signatures. Signature verification is tested in each repository's own suite, not by this fixture.
  • No HTTP surface — no endpoints, status codes, error codes, pagination or idempotency behaviour.
  • No field-member preservation. The fixture's field arrays carry only key, value and type, so nothing here would notice a client that dropped a member a newer sender had added. Go and Rust used to drop them and all 24 checks still passed; both preserve them now, and the gap in the fixture is unchanged.

A passing run means the cryptography and the wire encodings are right. It does not mean the client is complete.

The generator

The fixture is produced by a script that implements the specification from scratch against node:crypto and imports nothing from the application. That independence is the point: a generator built on the app's own crypto would merely restate whatever the app does, and an app bug would become the standard every SDK is held to.

Vectors are data. They are vendored and compared, never imported — data crosses the boundary between public and private code, and code does not.


If you are implementing a client for a language we do not ship, get in touch — we would rather know about it than have it drift.