@cognoshift/sanad-verify
v0.1.0
Published
Offline verification library for Sanad Sign attestations — Ed25519 + CycloneDX + SHA-256 hash chain. Zero dependencies, works in Node 18+.
Maintainers
Readme
@cognoshift/sanad-verify
Offline verification library for Sanad Sign attestations. Zero dependencies. Node 18+. Works anywhere (CI, auditor laptops, air-gapped environments).
Any Sanad Sign attestation can be verified with only the stored row — no network, no license key, no trust in Sanad servers. This package is 3 KB of Node's built-in crypto module plus the canonicalization rules.
Install
npm install @cognoshift/sanad-verifyVerify a single attestation
Given an attestation row fetched from GET /api/sign/registry/<id>:
const { verifyAttestation } = require("@cognoshift/sanad-verify");
const row = await fetch("https://sanad.cognoshift.in/api/sign/registry/<id>").then(r => r.json());
const result = verifyAttestation({
sbom: row.sbom,
signature: row.signature,
public_key: row.public_key,
sbom_hash: row.sbom_hash, // optional — recomputed anyway
});
console.log(result.valid); // true | false
console.log(result.signature_valid); // Ed25519 check
console.log(result.hash_match); // claimed vs recomputed SHA-256
console.log(result.computed_hash); // "a3f5..."Replay the hash chain
Given all attestations for a tenant (ordered ascending by created_at, id):
const { verifyChain } = require("@cognoshift/sanad-verify");
const result = verifyChain({ attestations: rows });
console.log(result.valid); // true when chain is intact
console.log(result.break_at); // id of first broken row, or null
console.log(result.first_mismatch); // detailed diagnosticThe genesis constant is SANAD_SIGN_GENESIS_2026 (exported as GENESIS). Every per-tenant chain starts from this value; if you operate a private deployment with a different genesis, pass it explicitly:
verifyChain({ attestations: rows, genesis: "MY_PRIVATE_GENESIS" });How verification works
- Canonicalize the SBOM — recursively sort object keys, keep arrays in order. Matches RFC 8785 JCS for the subset we use.
- Hash the canonical string with SHA-256. This is the
sbom_hash. - Verify the Ed25519 signature over the hex-encoded
sbom_hash, using the public key stored on the row (SPKI format, base64). - Chain replay: for every row in a tenant's ledger, recompute
SHA-256(event_hash || previous_chain_hash)and match against the storedchain_hash.previous_chain_hashisGENESISfor the first entry.
All four steps are deterministic and reproducible in any language. This library is the reference Node implementation.
Port to other languages
The verification protocol is open. A Go or Python implementation is ~40 lines — read the source for reference. Contributions welcome.
TypeScript
Types ship in the package. import { verifyAttestation, verifyChain } from "@cognoshift/sanad-verify"; works out of the box.
License
Apache-2.0 — use freely including in commercial audits.
