# @suaveplan/crypto-core - Universal cryptographic primitives (formerly @suaveplan/crypto) — AEAD (AES-256-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305), Ed25519 / X25519 / ECDSA-P256, sealed-box, auth-box, constant-time compare, zeroize. Engine-pluggable via noble defaults. Consumed by @suaveplan/crypto (the opinionated keychain/vault/packer layer); other packages must go through @suaveplan/crypto, not this package directly. ## Summary Universal cryptographic primitives — engine-pluggable AEAD, Ed25519/ECDSA-P256 signing, X25519 key agreement, sealed-box, and auth-box. Built on audited [@noble/*](https://paulmillr.com/noble/) libraries with zero WASM. ## Documentation tree - [aead](./src/aead/aead.md) — _(no submodules)_ - noble-aead-engine — _(no su… - [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_ - [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_ - [aead](./src/aead/aead.md) — _(no submodules)_ - noble-aead-engine — _(no su… - [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_ - [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_ - [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_ - [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_ - [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_ - noble-auth-box-engine — _(n… - [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_ - [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_ - [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_ - noble-auth-box-engine — _(n… - [codes](./src/errors/codes/codes.md) — _(no submodules)_ - [codes](./src/errors/codes/codes.md) — _(no submodules)_ - [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_ - [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_ - [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_ - [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_ - [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_ - [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_ - [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_ - [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_ - [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_ - noble-sealed-box-engine — _… - [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_ - [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_ - [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_ - noble-sealed-box-engine — _… - [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_ - [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_ - [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_ - [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_ - [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_ - [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_ - [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [aead](./src/aead/aead.md) — _(no submodules)_- [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_- [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- [codes](./src/errors/codes/codes.md) — _(no submodules)_- [codes](./src/errors/codes/codes.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_- [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- noble-aead-engine — _(no submodules)_- [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [aead](./src/aead/aead.md) — _(no submodules)_- noble-aead-engine — _(no submodules)_- [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- noble-auth-box-engine — _(no submodules)_- [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- noble-auth-box-engine — _(no submodules)_- [codes](./src/errors/codes/codes.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- noble-sealed-box-engine — _(no submodules)_- [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- noble-sealed-box-engine — _(no submodules)_- [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- noble-aead-engine — _(no s… - [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [aead](./src/aead/aead.md) — _(no submodules)_- noble-aead-engine — _(no s… - [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- noble-auth-box-engine — _(… - [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_- [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- noble-auth-box-engine — _(… - [codes](./src/errors/codes/codes.md) — _(no submodules)_- [codes](./src/errors/codes/codes.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- noble-sealed-box-engine — … - [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_- [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- noble-sealed-box-engine — … - [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_- [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_ - [noble-aead-engine](./src/aead/noble-aead-engine/noble-aead-engine.md) — _(no submodules)_- [aead](./src/aead/aead.md) — _(no submodules)_- [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_ - [noble-agree-engine](./src/agree/noble-agree-engine/noble-agree-engine.md) — _(no submodules)_- [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_ - [x25519](./src/agree/x25519/x25519.md) — _(no submodules)_- [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_ - [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_ - [noble-auth-box-engine](./src/auth-box/noble-auth-box-engine/noble-auth-box-engine.md) — _(no submodules)_ - [auth-box](./src/auth-box/auth-box.md) — _(no submodules)_- [codes](./src/errors/codes/codes.md) — _(no submodules)_ - [codes](./src/errors/codes/codes.md) — _(no submodules)_- [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_ - [crypto-error](./src/errors/crypto-error/crypto-error.md) — _(no submodules)_- [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_ - [constant-time-equal](./src/primitives/constant-time-equal/constant-time-equal.md) — _(no submodules)_- [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_ - [random-bytes](./src/primitives/random-bytes/random-bytes.md) — _(no submodules)_- [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_ - [zeroize](./src/primitives/zeroize/zeroize.md) — _(no submodules)_- [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_ - [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_ - [noble-sealed-box-engine](./src/sealed-box/noble-sealed-box-engine/noble-sealed-box-engine.md) — _(no submodules)_ - [sealed-box](./src/sealed-box/sealed-box.md) — _(no submodules)_- [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_ - [ecdsa-p256](./src/sign/ecdsa-p256/ecdsa-p256.md) — _(no submodules)_- [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_ - [ed25519](./src/sign/ed25519/ed25519.md) — _(no submodules)_- [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_ - [noble-sign-engine](./src/sign/noble-sign-engine/noble-sign-engine.md) — _(no submodules)_ ## Install ```bash bun add @suaveplan/crypto ``` ## Primitives | Surface | What | |---|---| | `randomBytes(n, { byteSource? })` | CSPRNG bytes (WebCrypto default; injectable for tests) | | `constantTimeEqual(a, b)` | Constant-time byte comparison | | `zeroize(buf)` | Fill a `Uint8Array` with zeros | | `aeadEncrypt` / `aeadDecrypt` | AES-256-GCM, ChaCha20-Poly1305, XChaCha20-Poly1305 | | `ed25519.{generateKeypair, sign, verify}` | Ed25519 signing | | `ecdsaP256.{generateKeypair, sign, verify}` | ECDSA-P256 signing | | `x25519.{generateKeypair, agree}` | X25519 ECDH key agreement | | `sealedBox.{seal, open}` | Anonymous-sender sealed box (X25519 + XChaCha20-Poly1305) | | `authBox.{seal, open}` | Authenticated box (X25519 + XChaCha20-Poly1305) | > Hashing (SHA-2, HMAC, HKDF, PBKDF2, Argon2id) lives in [`@suaveplan/crypto-hash`](../hash). ## Engine interfaces Every operation accepts an optional `{ engine?: }` argument. The default engines use `@noble/ciphers` and `@noble/curves`. Swap in a WebCrypto-backed or libsodium-backed engine without changing call sites. ```ts import type { AeadEngine } from "@suaveplan/crypto-core/engines"; const webCryptoEngine: AeadEngine = { /* ... */ }; aeadEncrypt("AES-256-GCM", key, plaintext, { nonce }, { engine: webCryptoEngine }); ``` ## ByteSource `ByteSource = (n: number) => Uint8Array` — the injected randomness type used for key generation. Replaces the old `Rng` alias (which conflicted with `@suaveplan/types`'s `Rng`). ```ts import { randomBytes, type ByteSource } from "@suaveplan/crypto-core"; // Production const key = randomBytes(32); // Tests — deterministic const seeded: ByteSource = (n) => new Uint8Array(n).fill(0x42); const kp = ed25519.generateKeypair({ byteSource: seeded }); ``` ## AEAD ```ts import { aeadEncrypt, aeadDecrypt, randomBytes } from "@suaveplan/crypto-core"; const key = randomBytes(32); const nonce = randomBytes(12); // 12 for AES-GCM/ChaCha20, 24 for XChaCha20 const plaintext = new TextEncoder().encode("hello"); const ct = aeadEncrypt("AES-256-GCM", key, plaintext, { nonce }); const pt = aeadDecrypt("AES-256-GCM", key, ct, { nonce }); ``` ## Signing ```ts import { ed25519, ecdsaP256 } from "@suaveplan/crypto-core"; const kp = ed25519.generateKeypair(); const sig = ed25519.sign(kp.secretKey, message); ed25519.verify(kp.publicKey, sig, message); // true ``` ## Key Agreement + Sealed Box ```ts import { x25519, sealedBox, authBox, randomBytes } from "@suaveplan/crypto-core"; const alice = x25519.generateKeypair(); const bob = x25519.generateKeypair(); // Anonymous sender (sealed box) const sealed = sealedBox.seal(bob.publicKey, plaintext); const opened = sealedBox.open(bob.secretKey, bob.publicKey, sealed); // Authenticated (auth box) const nonce = randomBytes(24); const ct = authBox.seal(alice.secretKey, bob.publicKey, plaintext, nonce); const pt = authBox.open(alice.publicKey, bob.secretKey, ct, nonce); ``` ## Error surface Every failure surfaces as `CryptoError` (extends `SuaveplanError`) with a stable `CRYPTO.*` code. Error context never includes raw key bytes, plaintext, or authentication tags. | Code | When | |---|---| | `CRYPTO.KEY.INVALID_LENGTH` | Key is not 32 bytes | | `CRYPTO.AEAD.INVALID_NONCE` | Nonce length wrong for algorithm | | `CRYPTO.AEAD.DECRYPT_FAILED` | Authentication tag mismatch | | `CRYPTO.SIGN.INVALID_KEY` | Key is malformed | | `CRYPTO.SIGN.VERIFY_FAILED` | Signature verification failed | | `CRYPTO.RNG.INVALID_LENGTH` | `randomBytes(n)` called with negative/non-integer n | | `CRYPTO.RNG.UNAVAILABLE` | No CSPRNG available | ## Subpath exports ```ts import { randomBytes } from "@suaveplan/crypto-core/primitives/random-bytes"; import { constantTimeEqual } from "@suaveplan/crypto-core/primitives/constant-time-equal"; import { zeroize } from "@suaveplan/crypto-core/primitives/zeroize"; import { aeadEncrypt, aeadDecrypt } from "@suaveplan/crypto-core/aead"; import { ed25519, ecdsaP256 } from "@suaveplan/crypto-core/sign"; import { x25519 } from "@suaveplan/crypto-core/agree"; import { sealedBox } from "@suaveplan/crypto-core/sealed-box"; import { authBox } from "@suaveplan/crypto-core/auth-box"; import type { AeadEngine, SignEngine } from "@suaveplan/crypto-core/engines"; import { CryptoError, CRYPTO_CODES } from "@suaveplan/crypto-core/errors"; ``` ## Co-located docs - [`src/primitives/random-bytes.md`](src/primitives/random-bytes.md) - [`src/primitives/constant-time-equal.md`](src/primitives/constant-time-equal.md) - [`src/primitives/zeroize.md`](src/primitives/zeroize.md) - [`src/aead/aead.md`](src/aead/aead.md) - [`src/sign/ed25519.md`](src/sign/ed25519.md) - [`src/sign/ecdsa-p256.md`](src/sign/ecdsa-p256.md) - [`src/agree/x25519.md`](src/agree/x25519.md) - [`src/sealed-box/sealed-box.md`](src/sealed-box/sealed-box.md) - [`src/auth-box/auth-box.md`](src/auth-box/auth-box.md) ## Foundation rules - Runtime deps: `@noble/ciphers`, `@noble/curves`, `@suaveplan/crypto-hash`, `@suaveplan/error`, `@suaveplan/types`, `tslib`, `zod` - `sideEffects: false`, per-primitive subpath exports - Zero `Math.random`, `Date.now`, or `crypto.getRandomValues` outside `src/primitives/random-bytes.ts` (guarded by `tests/no-disallowed.test.ts`) - 100% statement / branch / function / line coverage (Layer 1/2 foundation rule) - fast-check property tests for every primitive ## Testing ```bash bunx turbo lint typecheck test:coverage --filter=@suaveplan/crypto ```