{"id":48150171,"url":"https://github.com/sovereignbase/cryptosuite","last_synced_at":"2026-04-17T17:01:42.734Z","repository":{"id":330729380,"uuid":"1115899091","full_name":"sovereignbase/cryptosuite","owner":"sovereignbase","description":"JS/TS runtime-agnostic, quantum-safe, and agile cryptography toolkit with a declarative API for cipher messaging, message authentication, digital signatures, key agreement, and identifiers.","archived":false,"fork":false,"pushed_at":"2026-03-28T11:22:17.000Z","size":793,"stargazers_count":0,"open_issues_count":1,"forks_count":0,"subscribers_count":0,"default_branch":"master","last_synced_at":"2026-04-04T18:28:33.848Z","etag":null,"topics":["browser","bun","bytecodec","crypto","cryptography","decryption","deno","encryption","hmac","jwk","node","opaque-identifiers","signing","sovereignbase","typescript","verification","webcrypto"],"latest_commit_sha":null,"homepage":"https://www.npmjs.com/package/@sovereignbase/cryptosuite","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/sovereignbase.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2025-12-13T19:27:44.000Z","updated_at":"2026-03-28T11:20:31.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/sovereignbase/cryptosuite","commit_stats":null,"previous_names":["jortsupetterson/zeyra","z-base/zeyra","z-base/cryptosuite","sovereignbase/cryptosuite"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/sovereignbase/cryptosuite","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sovereignbase%2Fcryptosuite","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sovereignbase%2Fcryptosuite/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sovereignbase%2Fcryptosuite/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sovereignbase%2Fcryptosuite/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/sovereignbase","download_url":"https://codeload.github.com/sovereignbase/cryptosuite/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/sovereignbase%2Fcryptosuite/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31937732,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-17T12:37:54.787Z","status":"ssl_error","status_checked_at":"2026-04-17T12:37:25.095Z","response_time":62,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["browser","bun","bytecodec","crypto","cryptography","decryption","deno","encryption","hmac","jwk","node","opaque-identifiers","signing","sovereignbase","typescript","verification","webcrypto"],"created_at":"2026-04-04T17:10:13.757Z","updated_at":"2026-04-17T17:01:42.710Z","avatar_url":"https://github.com/sovereignbase.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"[![npm version](https://img.shields.io/npm/v/@sovereignbase/cryptosuite)](https://www.npmjs.com/package/@sovereignbase/cryptosuite)\n[![CI](https://github.com/sovereignbase/cryptosuite/actions/workflows/ci.yaml/badge.svg?branch=master)](https://github.com/sovereignbase/cryptosuite/actions/workflows/ci.yaml)\n[![codecov](https://codecov.io/gh/sovereignbase/cryptosuite/branch/master/graph/badge.svg)](https://codecov.io/gh/sovereignbase/cryptosuite)\n[![license](https://img.shields.io/npm/l/@sovereignbase/cryptosuite)](LICENSE)\n\n# cryptosuite\n\nJS/TS runtime-agnostic, quantum-safe, and agile cryptography toolkit with a declarative API for cipher messaging, message authentication, digital signatures, key agreement, and identifiers.\n\n## Compatibility\n\n- Runtimes: Tested on browsers, bun, deno, node, edge-runtimes.\n- Module format: ESM or CJS\n- Required globals / APIs: `crypto`, `crypto.subtle`, `crypto.getRandomValues`\n- Types: bundled `.d.ts`\n\n## Goals\n\n- Runtime-agnostic across modern JavaScript and TypeScript environments\n- Post-quantum by default\n- Crypto-agile, with room to add or replace algorithms as recommendations evolve while keeping already issued keys backwards compatible\n- Declarative API surface that expresses cryptographic intent clearly\n\n## Current algorithms\n\n- Identifier: `SHA-384` or 48 random bytes, encoded as a fixed-length base64url string\n- Cipher messaging: `AES-GCM-256`\n- Message authentication: `HMAC-SHA-256`\n- Key agreement: `X25519-ML-KEM-768`\n- Digital signatures: `Ed25519-ML-DSA-65`\n\n## Installation\n\n```sh\nnpm install @sovereignbase/cryptosuite\n# or\npnpm add @sovereignbase/cryptosuite\n# or\nyarn add @sovereignbase/cryptosuite\n# or\nbun add @sovereignbase/cryptosuite\n# or\ndeno add jsr:@sovereignbase/cryptosuite\n# or\nvlt install jsr:@sovereignbase/cryptosuite\n```\n\n## Usage\n\n### Identifiers\n\n```ts\nimport { Cryptographic } from '@sovereignbase/cryptosuite'\nimport { Bytes } from '@sovereignbase/bytecodec'\n\nconst discoveryHook = Bytes.fromString('resource discovery hook') // Uint8Array\n\nconst newResourceId = await Cryptographic.identifier.generate() // \"64xb64urlchars...\"\nconst discoveryId = await Cryptographic.identifier.derive(discoveryHook) // \"64xb64urlchars...\"\nconst ingressId = Cryptographic.identifier.validate(discoveryId) // \"64xb64urlchars...\" | false\n```\n\n### Cipher messages\n\n```ts\nimport { Cryptographic } from '@sovereignbase/cryptosuite'\nimport { Bytes } from '@sovereignbase/bytecodec'\n\nconst messageBytes = Bytes.fromString('hello world') // Uint8Array\n\nconst cipherKey = await Cryptographic.cipherMessage.generateKey() // JsonWebKey\n\nconst sourceKeyMaterial = Bytes.fromString('deterministic key source') // Uint8Array\nconst salt = Bytes.fromString('deterministic salt source') // Uint8Array\nconst { cipherKey } = await Cryptographic.cipherMessage.deriveKey(\n  sourceKeyMaterial,\n  { salt }\n) // {cipherKey: JsonWebKey, salt: Uint8Array}\n\nconst cipherMessage = await Cryptographic.cipherMessage.encrypt(\n  cipherKey,\n  messageBytes\n) // {ciphertext: ArrayBuffer, iv: Uint8Array}\nconst roundtrip = await Cryptographic.cipherMessage.decrypt(\n  cipherKey,\n  cipherMessage\n) // Uint8Array\n\nconst plainMessage = Bytes.toString(roundtrip) // 'hello world'\n```\n\n### Message authentication\n\n```ts\nimport { Cryptographic } from '@sovereignbase/cryptosuite'\nimport { Bytes } from '@sovereignbase/bytecodec'\n\nconst messageBytes = Bytes.fromString('authenticated payload') // Uint8Array\n\nconst generatedMessageAuthenticationKey =\n  await Cryptographic.messageAuthentication.generateKey() // JsonWebKey\n\nconst sourceKeyMaterial = Bytes.fromString('deterministic key source') // Uint8Array\nconst salt = Bytes.fromString('deterministic salt source') // Uint8Array\n\nconst { messageAuthenticationKey } =\n  await Cryptographic.messageAuthentication.deriveKey(sourceKeyMaterial, {\n    salt,\n  }) // {messageAuthenticationKey: JsonWebKey, salt: Uint8Array}\n\nconst tag = await Cryptographic.messageAuthentication.sign(\n  generatedMessageAuthenticationKey,\n  messageBytes\n) // ArrayBuffer\n\nconst verified = await Cryptographic.messageAuthentication.verify(\n  generatedMessageAuthenticationKey,\n  messageBytes,\n  tag\n) // boolean\n```\n\n### Key agreement\n\n```ts\nimport { Cryptographic } from '@sovereignbase/cryptosuite'\nimport { Bytes } from '@sovereignbase/bytecodec'\n\nconst sourceKeyMaterial = Bytes.fromString('k'.repeat(32)) // Uint8Array, exactly 32 bytes\n\nconst { encapsulateKey, decapsulateKey } =\n  await Cryptographic.keyAgreement.generateKeypair() // {encapsulateKey: JsonWebKey, decapsulateKey: JsonWebKey}\n\nconst deterministicKeypair =\n  await Cryptographic.keyAgreement.deriveKeypair(sourceKeyMaterial) // {encapsulateKey: JsonWebKey, decapsulateKey: JsonWebKey}\n\nconst { keyOffer, cipherKey: senderCipherKey } =\n  await Cryptographic.keyAgreement.encapsulate(encapsulateKey) // {keyOffer: {ciphertext: ArrayBuffer}, cipherKey: JsonWebKey}\n\nconst { cipherKey: receiverCipherKey } =\n  await Cryptographic.keyAgreement.decapsulate(keyOffer, decapsulateKey) // {cipherKey: JsonWebKey}\n```\n\n### Digital signatures\n\n```ts\nimport { Cryptographic } from '@sovereignbase/cryptosuite'\nimport { Bytes } from '@sovereignbase/bytecodec'\n\nconst sourceKeyMaterial = Bytes.fromString('s'.repeat(64)) // Uint8Array, exactly 64 bytes\nconst bytes = Bytes.fromString('signed payload') // Uint8Array\nconst { signKey, verifyKey } =\n  await Cryptographic.digitalSignature.generateKeypair() // {signKey: JsonWebKey, verifyKey: JsonWebKey}\n\nconst deterministicKeypair =\n  await Cryptographic.digitalSignature.deriveKeypair(sourceKeyMaterial) // {signKey: JsonWebKey, verifyKey: JsonWebKey}\n\nconst signature = await Cryptographic.digitalSignature.sign(signKey, bytes) // Uint8Array\nconst verified = await Cryptographic.digitalSignature.verify(\n  verifyKey,\n  bytes,\n  signature\n) // boolean\n```\n\n## Runtime behavior\n\n- `identifier.generate()` requires `crypto.getRandomValues`\n- symmetric operations use WebCrypto\n- key agreement and digital signatures use `noble` hybrid primitives\n- unsupported crypto primitives throw typed `CryptosuiteError` codes\n\n## Security notes\n\n- `AES-GCM` provides confidentiality and message integrity for each ciphertext\n- authenticate peers and session setup at the protocol layer\n- never reuse a `(key, iv)` pair\n- treat JWKs and derived key material as secrets\n- sign a canonical byte representation, not loosely structured objects\n\n## Tests\n\nLatest local `npm run test` run on `2026-04-17` with Node `v22.14.0 (win32 x64)`:\n\n- `65/65` tests passed\n- Coverage passed at `100%` for statements, branches, functions, and lines\n- End-to-end runtime suites all passed in:\n  - Node ESM\n  - Node CJS\n  - Bun ESM\n  - Bun CJS\n  - Deno ESM\n  - Edge Runtime ESM\n  - Cloudflare Workers ESM\n  - Chromium\n  - Firefox\n  - WebKit\n  - Mobile Chrome emulation\n  - Mobile Safari emulation\n- The runtime suite currently exercises `20/20` public API scenarios per runtime:\n  - 1 static wiring check\n  - 19 public methods\n\n## Benchmarks\n\nLatest local `npm run bench` run on `2026-04-17` with Node `v22.14.0 (win32 x64)`.\n\n| Benchmark                           | ops |      ms |   ms/op |   ops/sec |\n| ----------------------------------- | --: | ------: | ------: | --------: |\n| `identifier.generate`               | 100 |    2.91 |  0.0291 |  34389.08 |\n| `identifier.derive`                 | 100 |   34.97 |  0.3497 |   2859.53 |\n| `identifier.validate`               | 100 |    0.41 |  0.0041 | 243961.94 |\n| `cipherMessage.generateKey`         | 100 |   48.77 |  0.4877 |   2050.38 |\n| `cipherMessage.deriveKey`           | 100 |   67.84 |  0.6784 |   1474.02 |\n| `cipherMessage.encrypt`             | 100 |   36.80 |  0.3680 |   2717.03 |\n| `cipherMessage.decrypt`             | 100 |   36.02 |  0.3602 |   2776.57 |\n| `messageAuthentication.generateKey` | 100 |   44.84 |  0.4484 |   2230.24 |\n| `messageAuthentication.deriveKey`   | 100 |   75.64 |  0.7564 |   1322.07 |\n| `messageAuthentication.sign`        | 100 |   29.09 |  0.2909 |   3437.31 |\n| `messageAuthentication.verify`      | 100 |   25.33 |  0.2533 |   3947.69 |\n| `keyAgreement.generateKeypair`      | 100 |  827.02 |  8.2702 |    120.92 |\n| `keyAgreement.deriveKeypair`        | 100 |  842.11 |  8.4211 |    118.75 |\n| `keyAgreement.encapsulate`          | 100 | 1669.17 | 16.6917 |     59.91 |\n| `keyAgreement.decapsulate`          | 100 | 1240.95 | 12.4095 |     80.58 |\n| `digitalSignature.generateKeypair`  | 100 |  808.57 |  8.0857 |    123.67 |\n| `digitalSignature.deriveKeypair`    | 100 |  612.56 |  6.1256 |    163.25 |\n| `digitalSignature.sign`             | 100 | 3478.93 | 34.7893 |     28.74 |\n| `digitalSignature.verify`           | 100 | 2574.33 | 25.7433 |     38.85 |\n\nResults vary by machine and Node version.\n\n## Credits\n\nPost-quantum primitives are built on top of [noble](https://paulmillr.com/noble/).\n\nThanks to Paul Miller for an unusually clear, well-engineered, and genuinely awesome project.\n\n## License\n\nApache-2.0\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsovereignbase%2Fcryptosuite","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsovereignbase%2Fcryptosuite","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsovereignbase%2Fcryptosuite/lists"}