{"_id":"@aroha-sdk/bbs","name":"@aroha-sdk/bbs","dist-tags":{"latest":"0.1.0"},"versions":{"0.1.0":{"name":"@aroha-sdk/bbs","version":"0.1.0","description":"BBS signatures (draft-irtf-cfrg-bbs-signatures) over BLS12-381 — selective disclosure primitives. UNAUDITED: see README.","type":"module","main":"./dist/index.js","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","types":"./dist/index.d.ts"}},"scripts":{"build":"tsc -p tsconfig.json","test":"vitest run --passWithNoTests","dev":"tsc -p tsconfig.json --watch"},"dependencies":{"@noble/curves":"^1.9.7","@noble/hashes":"^1.4.0"},"devDependencies":{"typescript":"^5.4.5","vitest":"^1.6.0","@types/node":"^20.14.0"},"license":"MIT","engines":{"node":">=18.0.0"},"gitHead":"ae0fd13ad7bb9c5332b32a894a4e329779247590","_id":"@aroha-sdk/bbs@0.1.0","_nodeVersion":"24.19.0","_npmVersion":"11.17.0","dist":{"integrity":"sha512-ZvDJWV/VeSypzrCpzsNt06XDKKYvzJxhZ+qOMxdfn2LhOt6tBZ5iDozTpJPJvpU5zRlGhB3CwsEJ2a7P8R2G0A==","shasum":"3537e9ba3db85923c8943de2f416614273aa3e89","tarball":"https://registry.npmjs.org/@aroha-sdk/bbs/-/bbs-0.1.0.tgz","fileCount":25,"unpackedSize":106508,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQCj9/POC/w63aXRkF2ARfB0/qG0TKZvKiRlDjRRj5GC0wIhAO4gQvwrBXuVwoKXCUlkGNO4UdBqMqtIuWTAfvjE9s/g"}]},"_npmUser":{"name":"aroha-labs","email":"projectmed99@gmail.com"},"directories":{},"maintainers":[{"name":"aroha-labs","email":"projectmed99@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/bbs_0.1.0_1787165716412_0.13456375581145807"},"_hasShrinkwrap":false}},"time":{"created":"2026-08-19T18:55:16.265Z","0.1.0":"2026-08-19T18:55:16.557Z","modified":"2026-08-19T18:55:16.750Z"},"maintainers":[{"name":"aroha-labs","email":"projectmed99@gmail.com"}],"description":"BBS signatures (draft-irtf-cfrg-bbs-signatures) over BLS12-381 — selective disclosure primitives. UNAUDITED: see README.","license":"MIT","readme":"# @aroha-sdk/bbs\n\nBBS signatures over BLS12-381, following\n[draft-irtf-cfrg-bbs-signatures](https://datatracker.ietf.org/doc/draft-irtf-cfrg-bbs-signatures/),\nciphersuite `BLS12381-SHA-256`.\n\nBBS produces one short signature over many messages, which is the property\nselective disclosure is built on: a holder can later prove they hold a valid\nsignature over *all* the messages while revealing only some of them. This is\nthe primitive the consult design's §11.2 named as missing.\n\n## Read this before using it\n\n**This is a from-scratch implementation and it has not been audited.** It is\ncorrect against every published reference vector — which is a real bar, and\nnot the same bar as an audit. Nothing in Aroha makes a trust decision on it\ntoday, and it should not be the only thing standing between an attacker and\nsomething that matters until that changes.\n\nTwo specific limitations, stated rather than implied:\n\n- **Not constant-time.** It inherits whatever `@noble/curves` guarantees for\n  scalar multiplication and adds no protection of its own. Assume a local\n  attacker who can time operations learns something.\n- **A proof says nothing about who is presenting it.** It proves an issuer\n  signed a message set containing the revealed values — not that the party\n  showing it is the party it was issued to. Without separate holder binding, a\n  proof that leaks is a bearer token. BBS has no opinion on this and neither\n  does this package.\n- **Randomness is the whole security of a proof.** `prove` draws its blinding\n  scalars from `@noble/hashes`' CSPRNG. The `unsafeRandomScalars` option\n  exists only so the published fixtures can be reproduced byte for byte;\n  supplying it in production destroys zero-knowledge and unlinkability.\n\n## Why implement it rather than depend on one\n\nThe runtime ships as a single compiled binary via `bun build --compile`, which\ncannot load native addons. The maintained BBS libraries are WASM or native\nbindings. A pure-JavaScript implementation over `@noble/curves` — already a\ndependency of `@aroha-sdk/trust` — is the only option that survives the build,\nso the choice was to implement it against the published vectors or not to have\nit.\n\n## Verification\n\nThe test suite runs the\n[DIF/IETF reference fixtures](https://github.com/decentralized-identity/bbs-signature)\ncommitted under `vectors/`:\n\n- Every generator in `generators.json` is re-derived and compared, so the\n  ciphersuite id, all five domain separation tags, `expand_len` and\n  hash-to-curve are pinned to the standard rather than to our reading of it.\n- Every `valid` signature fixture is reproduced **byte for byte** — signing is\n  deterministic, so anything short of an exact match is a divergence.\n- Every `invalid` fixture is rejected: modified, missing, extra, reordered and\n  shuffled messages, a wrong public key, and a changed header.\n- Every valid **proof** fixture is reproduced byte for byte by replaying the\n  blinding scalars the fixture publishes — including partial disclosure, where\n  4 of 10 messages are revealed — and both invalid proof fixtures are\n  rejected. This matters more than it does for signing: a zero-knowledge proof\n  has no observable output to eyeball, so a self-consistent wrong\n  implementation would produce proofs only its own verifier accepts.\n- `expand_message_xmd` is cross-checked against `@noble/curves`' independent\n  implementation of the same RFC, rather than against constants typed in by\n  hand.\n\n```bash\nnpm test --workspace @aroha-sdk/bbs\n```\n\n## Usage\n\n```ts\nimport { sign, verify, skToPk, utf8 } from \"@aroha-sdk/bbs\";\n\nconst sk = 0x60e55110f76883a13d030b2f6bd11883422d5abde717569fc0731f51237169fcn;\nconst pk = skToPk(sk);\n\nconst messages = [utf8(\"name=Dana\"), utf8(\"dob=1990-04-12\"), utf8(\"member=true\")];\nconst signature = sign(sk, pk, messages, { header: utf8(\"credential-v1\") });\n\nverify(pk, signature, messages, { header: utf8(\"credential-v1\") }); // true\n```\n\n`header` is bound into the signature but is not a message: use it for context\nthat must not be swappable, such as a credential type or issuance epoch.\n\n`verify` returns `false` for every rejection, including malformed input, so\n\"invalid\" and \"unparseable\" are not two different call sites for the same\nsecurity answer.\n\n### Selective disclosure\n\n```ts\nimport { prove, verifyProof, utf8 } from \"@aroha-sdk/bbs\";\n\n// The holder reveals only message 2 (\"over_18=true\"), proving the issuer\n// signed a set containing it — and revealing nothing about name, date of\n// birth, or national id.\nconst proof = prove(pk, signature, messages, [2], {\n  header: utf8(\"credential-v1\"),\n  presentationHeader: verifierNonce,\n});\n\nverifyProof(pk, proof, [messages[2]], [2], {\n  header: utf8(\"credential-v1\"),\n  presentationHeader: verifierNonce,\n}); // true\n```\n\n`disclosedIndexes` are the positions in the **original signed set**. The\nverifier must know which attribute it is being shown, or a value proven at one\nposition could be presented as a different attribute — which is why the index\nis bound into the challenge alongside the value.\n\n`presentationHeader` is where a verifier's nonce goes. Without one, a proof\nthat verifier has seen once can be replayed to them forever.\n","readmeFilename":"README.md","_rev":"1-abeddd1d184178ddabbe95effe12e3c9"}