Initial Commit

This commit is contained in:
2026-05-27 23:29:53 +01:00
commit fbc70f96e6
10000 changed files with 1102152 additions and 0 deletions

View File

@@ -0,0 +1,33 @@
# Capability: crypto-browser-engine
## ADDED Requirements
### Package identity
- REQ-BRW-001: Package name `@suaveplan/crypto-browser`, `packages/universal/crypto-browser`.
- REQ-BRW-002: Runtime deps: `@suaveplan/crypto`, `@suaveplan/error`, `tslib`. Zero Node-only deps.
- REQ-BRW-003: All exports are async (return `Promise<...>`) — `crypto.subtle` is async.
### Engine implementations
- REQ-BRW-010: Exports `webCryptoAeadEngine: AeadEngine` — AES-256-GCM via `crypto.subtle.encrypt`/`decrypt` (hardware-accelerated in supporting browsers/runtimes).
- REQ-BRW-011: Exports `webCryptoSignEngine: SignEngine` — Ed25519 and ECDSA-P256 via `crypto.subtle`.
- REQ-BRW-012: Exports `webCryptoAgreeEngine: AgreeEngine` — X25519 and ECDH-P256 via `crypto.subtle`.
### Browser-exclusive primitives
- REQ-BRW-020: Exports `rsa` namespace: `{ generateKeypair(algorithm, modulusLength?), sign(algorithm, privateKey, message), verify(algorithm, publicKey, signature, message), encrypt(publicKey, plaintext), decrypt(privateKey, ciphertext) }`. Algorithms: `"RSA-PSS"`, `"RSA-OAEP"`, `"RSASSA-PKCS1-v1_5"`.
- REQ-BRW-021: Exports `aesKw` namespace: `{ wrapKey(wrappingKey, keyToWrap), unwrapKey(wrappingKey, wrappedKey) }`.
- REQ-BRW-022: Exports `ecdsaP384` namespace: sign/verify/generateKeypair on P-384 curve.
- REQ-BRW-023: Exports `ecdhP384` namespace: agree/generateKeypair on P-384 curve.
- REQ-BRW-024: Exports `nonExtractableKey(algorithm, usages): Promise<CryptoKey>` — generates a non-extractable `CryptoKey` handle. Key material is never accessible to JS code after generation.
### Availability check
- REQ-BRW-030: Exports `isWebCryptoAvailable(): boolean` — returns `typeof crypto !== 'undefined' && !!crypto.subtle`.
- REQ-BRW-031: All operations throw `BrowserCryptoError("BROWSER_CRYPTO.NOT_AVAILABLE")` if called in an environment without `crypto.subtle`.
### Error surface
- REQ-BRW-040: Exports `BrowserCryptoError extends SuaveplanError<BrowserCryptoCode>`.
- REQ-BRW-041: Codes: `BROWSER_CRYPTO.NOT_AVAILABLE`, `BROWSER_CRYPTO.KEY.GENERATION_FAILED`, `BROWSER_CRYPTO.OPERATION.FAILED`.

View File

@@ -0,0 +1,22 @@
# Capability: crypto-engine-interfaces
## ADDED Requirements
### Engine interface types
- REQ-ENG-001: `@suaveplan/crypto` exports `AeadEngine` interface: `{ encrypt(key, plaintext, nonce, aad?): Uint8Array; decrypt(key, ciphertext, nonce, aad?): Uint8Array }`.
- REQ-ENG-002: `@suaveplan/crypto` exports `SignEngine` interface: `{ generateKeypair(byteSource?): Keypair; sign(secretKey, message): Uint8Array; verify(publicKey, signature, message): boolean }`.
- REQ-ENG-003: `@suaveplan/crypto` exports `AgreeEngine` interface: `{ generateKeypair(byteSource?): Keypair; agree(mySecret, theirPublic): Uint8Array }`.
- REQ-ENG-004: `@suaveplan/crypto` exports `SealedBoxEngine` interface: `{ seal(recipientPublic, plaintext): Uint8Array; open(recipientSecret, recipientPublic, ciphertext): Uint8Array }`.
- REQ-ENG-005: `@suaveplan/crypto` exports `AuthBoxEngine` interface: `{ seal(senderSecret, recipientPublic, plaintext, nonce): Uint8Array; open(senderPublic, recipientSecret, ciphertext, nonce): Uint8Array }`.
### Algorithm type identifiers
- REQ-ENG-010: `AeadAlgorithm = "AES-256-GCM" | "ChaCha20-Poly1305" | "XChaCha20-Poly1305"`.
- REQ-ENG-011: `SignAlgorithm = "Ed25519" | "ECDSA-P256"`.
- REQ-ENG-012: `AgreeAlgorithm = "X25519" | "ECDH-P256"`.
### Engine interfaces are types only
- REQ-ENG-020: Engine interfaces are exported as TypeScript `interface` declarations — no runtime class, no factory function, no registry. Consumers receive an engine instance as a plain object.
- REQ-ENG-021: All engine interface methods that may fail throw `CryptoError` with appropriate codes.

View File

@@ -0,0 +1,32 @@
# Capability: crypto-keys
## ADDED Requirements
### Package identity
- REQ-KEYS-001: Package name `@suaveplan/crypto-keys`, `packages/universal/crypto-keys`.
- REQ-KEYS-002: Runtime deps: `@suaveplan/crypto`, `@suaveplan/error`, `@suaveplan/types`, `tslib`, `zod`.
### Keypair generation
- REQ-KEYS-010: Exports `generateKeypair(algo: SignAlgorithm | AgreeAlgorithm, opts?: { byteSource?: ByteSource; engine?: SignEngine | AgreeEngine }): Keypair`.
- REQ-KEYS-011: `Keypair = { publicKey: Uint8Array; secretKey: Uint8Array; algorithm: string }`.
- REQ-KEYS-012: Delegates to the appropriate `@suaveplan/crypto` façade based on `algo`.
### Keypair validation
- REQ-KEYS-020: Exports `validateKeypair(keypair: Keypair): ValidationResult`.
- REQ-KEYS-021: Checks: key lengths match algorithm constants, public key is derivable from secret key (for Ed25519 and X25519).
- REQ-KEYS-022: Returns `{ valid: boolean; errors: string[] }`.
### Keychain store
- REQ-KEYS-030: Exports `Keychain` class: `{ add(id: string, keypair: Keypair): void; get(id: string): Keypair | undefined; remove(id: string): void; list(): string[]; clear(): void }`.
- REQ-KEYS-031: In-memory by default. Accepts an optional `KeychainAdapter` for persistence.
- REQ-KEYS-032: Exports `KeychainAdapter` interface: `{ load(): Promise<Map<string, Keypair>>; save(entries: Map<string, Keypair>): Promise<void> }`.
- REQ-KEYS-033: Exports `MemoryKeychainAdapter` (default) and `SerializedKeychainAdapter` (serialises to/from JSON string via a provided read/write pair).
### Error surface
- REQ-KEYS-040: Exports `CryptoKeysError extends SuaveplanError<CryptoKeysCode>`.
- REQ-KEYS-041: Codes: `CRYPTO_KEYS.KEYPAIR.INVALID`, `CRYPTO_KEYS.KEYCHAIN.NOT_FOUND`, `CRYPTO_KEYS.ADAPTER.LOAD_FAILED`, `CRYPTO_KEYS.ADAPTER.SAVE_FAILED`.

View File

@@ -0,0 +1,42 @@
# Capability: crypto-noble-defaults
## ADDED Requirements
### AEAD façade
- REQ-NOBLE-010: Exports `aeadEncrypt(algo: AeadAlgorithm, key: Uint8Array, plaintext: Uint8Array, options: AeadEncryptOptions, opts?: { engine?: AeadEngine }): Uint8Array`.
- REQ-NOBLE-011: Exports `aeadDecrypt(algo: AeadAlgorithm, key: Uint8Array, ciphertext: Uint8Array, options: AeadDecryptOptions, opts?: { engine?: AeadEngine }): Uint8Array`.
- REQ-NOBLE-012: `AeadEncryptOptions = { nonce: Uint8Array; aad?: Uint8Array }`. `AeadDecryptOptions` is the same shape.
- REQ-NOBLE-013: Default engine is noble: `@noble/ciphers` AES-256-GCM and ChaCha20-Poly1305; `@noble/ciphers` XChaCha20-Poly1305.
- REQ-NOBLE-014: Key must be 32 bytes; throws `CryptoError("CRYPTO.KEY.INVALID_LENGTH")` otherwise.
- REQ-NOBLE-015: Nonce must be 12 bytes for AES-256-GCM and ChaCha20-Poly1305, 24 bytes for XChaCha20-Poly1305; throws `CryptoError("CRYPTO.AEAD.INVALID_NONCE")` otherwise.
- REQ-NOBLE-016: Exports `AEAD_KEY_LENGTH = 32`, `AEAD_NONCE_LENGTH = 12`, `AEAD_XCHACHA_NONCE_LENGTH = 24`.
### Sign façade
- REQ-NOBLE-020: Exports `ed25519` namespace: `{ generateKeypair(opts?), sign(secretKey, message), verify(publicKey, signature, message) }`.
- REQ-NOBLE-021: Exports `ecdsaP256` namespace: same shape as `ed25519`.
- REQ-NOBLE-022: All functions accept `{ engine?: SignEngine }` as a trailing options arg.
- REQ-NOBLE-023: Noble defaults: `@noble/curves` ed25519 and P-256.
- REQ-NOBLE-024: `verify` always returns `boolean`, never throws on a bad signature.
- REQ-NOBLE-025: `ecdsaP256.generateKeypair` retries until `noble.utils.isValidSecretKey(secretKey)` is true.
### Agree façade
- REQ-NOBLE-030: Exports `x25519` namespace: `{ generateKeypair(opts?), agree(mySecret, theirPublic) }`.
- REQ-NOBLE-031: Accepts `{ engine?: AgreeEngine }`.
- REQ-NOBLE-032: Noble default: `@noble/curves` x25519.
### Sealed-box façade
- REQ-NOBLE-040: Exports `sealedBox` namespace: `{ seal(recipientPublic, plaintext, opts?), open(recipientSecret, recipientPublic, ciphertext, opts?) }`.
- REQ-NOBLE-041: Noble default: X25519 key agreement + XChaCha20-Poly1305 encryption (crypto_box_seal semantics).
### Auth-box façade
- REQ-NOBLE-050: Exports `authBox` namespace: `{ seal(senderSecret, recipientPublic, plaintext, nonce, opts?), open(senderPublic, recipientSecret, ciphertext, nonce, opts?) }`.
- REQ-NOBLE-051: Noble default: X25519 key agreement + XChaCha20-Poly1305 (crypto_box semantics).
### Constants
- REQ-NOBLE-060: Exports `ED25519_SECRET_KEY_LENGTH = 32`, `ED25519_PUBLIC_KEY_LENGTH = 32`, `ED25519_SIGNATURE_LENGTH = 64`, `X25519_KEY_LENGTH = 32`, `ECDSA_P256_SIGNATURE_LENGTH = 64`.

View File

@@ -0,0 +1,33 @@
# Capability: crypto-primitives
## ADDED Requirements
### ByteSource type
- REQ-PRIM-001: `@suaveplan/crypto` exports `ByteSource = (n: number) => Uint8Array` — the byte-producing injection type used for key generation.
- REQ-PRIM-002: `Rng` is exported as a type alias for `ByteSource` for backwards compatibility during the main→genesis port. New code uses `ByteSource`.
- REQ-PRIM-003: `ByteSource` is explicitly documented as distinct from `@suaveplan/types`'s `Rng` interface (which produces numbers, not bytes).
### randomBytes
- REQ-PRIM-010: Exports `randomBytes(n: number, opts?: { byteSource?: ByteSource }): Uint8Array`.
- REQ-PRIM-011: Default `byteSource` calls `crypto.getRandomValues` — this is the **only** callsite for `crypto.getRandomValues` in the package.
- REQ-PRIM-012: Throws `CryptoError("CRYPTO.RNG.INVALID_LENGTH")` if `n` is not a non-negative integer.
- REQ-PRIM-013: Exports `defaultByteSource: ByteSource`.
### constantTimeEqual
- REQ-PRIM-020: Exports `constantTimeEqual(a: Uint8Array, b: Uint8Array): boolean`.
- REQ-PRIM-021: Uses XOR-accumulate pattern — branch-free for content bytes. Length mismatch short-circuits (length is not secret).
- REQ-PRIM-022: Returns `true` iff `a.length === b.length` and all bytes are identical.
### zeroize
- REQ-PRIM-030: Exports `zeroize(buf: Uint8Array): void` — fills buffer with zeros in-place.
- REQ-PRIM-031: Best-effort (JS GC may have copied); intent is to minimise sensitive-data lifetime on the heap.
### Error surface
- REQ-PRIM-040: Exports `CryptoError extends SuaveplanError<CryptoCode>`.
- REQ-PRIM-041: Error codes: `CRYPTO.AEAD.DECRYPT_FAILED`, `CRYPTO.AEAD.INVALID_NONCE`, `CRYPTO.KEY.INVALID_LENGTH`, `CRYPTO.SIGN.VERIFY_FAILED`, `CRYPTO.SIGN.INVALID_KEY`, `CRYPTO.RNG.INVALID_LENGTH`, `CRYPTO.RNG.UNAVAILABLE`.
- REQ-PRIM-042: No error message or context field contains raw key bytes, plaintext, or authentication tags.

View File

@@ -0,0 +1,36 @@
# Capability: crypto-sodium-engine
## ADDED Requirements
### Package identity
- REQ-SOD-001: Package name `@suaveplan/crypto-sodium`, `packages/universal/crypto-sodium`.
- REQ-SOD-002: Runtime deps: `libsodium-wrappers-sumo`, `@suaveplan/crypto`, `@suaveplan/error`, `tslib`.
- REQ-SOD-003: Peer dep: `@suaveplan/crypto` at the same genesis version.
### Initialisation
- REQ-SOD-010: Exports `sodiumReady(): Promise<void>` — resolves when `libsodium-wrappers-sumo` WASM is initialised. Idempotent; resolves immediately on subsequent calls.
- REQ-SOD-011: All engine implementations call `await sodiumReady()` internally; callers do not need to call it explicitly.
### Engine implementations
- REQ-SOD-020: Exports `sodiumAeadEngine: AeadEngine` — implements AES-256-GCM (where available via hardware), ChaCha20-Poly1305, XChaCha20-Poly1305 via `libsodium`.
- REQ-SOD-021: Exports `sodiumSignEngine: SignEngine` — Ed25519 sign/verify/generateKeypair via `libsodium`.
- REQ-SOD-022: Exports `sodiumAgreeEngine: AgreeEngine` — X25519 agree/generateKeypair via `libsodium`.
- REQ-SOD-023: Exports `sodiumSealedBoxEngine: SealedBoxEngine` — `crypto_box_seal` / `crypto_box_seal_open` semantics.
- REQ-SOD-024: Exports `sodiumAuthBoxEngine: AuthBoxEngine` — `crypto_box_easy` / `crypto_box_open_easy` semantics.
### Sodium-exclusive primitives
- REQ-SOD-030: Exports `cryptoKdf(key: Uint8Array, subkeyId: number, context: string, length: number): Uint8Array` — deterministic subkey derivation via `crypto_kdf_derive_from_key`. Context must be exactly 8 bytes when encoded.
- REQ-SOD-031: Exports `cryptoAuth(key: Uint8Array, message: Uint8Array): Uint8Array` — BLAKE2b-based MAC via `crypto_auth`.
- REQ-SOD-032: Exports `cryptoAuthVerify(key: Uint8Array, message: Uint8Array, mac: Uint8Array): boolean` — verifies BLAKE2b MAC.
- REQ-SOD-033: Exports `cryptoPwhashStr(password: Uint8Array, opts?: { opsLimit?, memLimit? }): string` — Argon2id encoded hash string (includes salt, ready to store).
- REQ-SOD-034: Exports `cryptoPwhashStrVerify(hash: string, password: Uint8Array): boolean`.
- REQ-SOD-035: Exports `ristretto255` namespace: `{ add, sub, mul, fromHash, toBytes, fromBytes, random }` — group operations.
### Error surface
- REQ-SOD-040: Exports `SodiumError extends SuaveplanError<SodiumCode>`.
- REQ-SOD-041: Codes: `SODIUM.INIT.FAILED`, `SODIUM.KDF.INVALID_CONTEXT`, `SODIUM.KDF.INVALID_LENGTH`, `SODIUM.AUTH.VERIFY_FAILED`.

View File

@@ -0,0 +1,31 @@
# Capability: crypto-vault
## ADDED Requirements
### Package identity
- REQ-VAULT-001: Package name `@suaveplan/crypto-vault`, `packages/universal/crypto-vault`.
- REQ-VAULT-002: Runtime deps: `@suaveplan/crypto`, `@suaveplan/crypto-keys`, `@suaveplan/hash`, `@suaveplan/events`, `@suaveplan/error`, `@suaveplan/types`, `@scure/bip39`, `tslib`, `zod`.
### BIP39 mnemonic
- REQ-VAULT-010: Exports `generateMnemonic(wordCount?: 12 | 15 | 18 | 21 | 24, opts?: { byteSource? }): string`.
- REQ-VAULT-011: Exports `validateMnemonic(mnemonic: string): boolean`.
- REQ-VAULT-012: Exports `mnemonicToSeed(mnemonic: string, passphrase?: string): Uint8Array` — 64-byte seed via PBKDF2-SHA512 per BIP39.
### Vault lifecycle
- REQ-VAULT-020: Exports `Vault` class: `{ unlock(mnemonic, passphrase?), lock(), isLocked(): boolean, sign(keyId, message), derive(path), getPublicKey(keyId) }`.
- REQ-VAULT-021: Unlock derives a master key via Argon2id from the mnemonic seed. Locked vault holds no key material in memory.
- REQ-VAULT-022: `lock()` calls `zeroize` on all in-memory key buffers before clearing references.
- REQ-VAULT-023: Vault emits typed events via `@suaveplan/events`: `unlocked`, `locked`, `signed`, `derived`.
### Chain signer
- REQ-VAULT-030: Exports `ChainSigner` class: `{ addKey(id, keypair), removeKey(id), sign(keyId, message, opts?) }`.
- REQ-VAULT-031: Signs using the key algorithm associated with the stored keypair.
### Error surface
- REQ-VAULT-040: Exports `CryptoVaultError extends SuaveplanError<CryptoVaultCode>`.
- REQ-VAULT-041: Codes: `CRYPTO_VAULT.LOCKED`, `CRYPTO_VAULT.MNEMONIC.INVALID`, `CRYPTO_VAULT.KEY.NOT_FOUND`, `CRYPTO_VAULT.DERIVATION.FAILED`.

View File

@@ -0,0 +1,62 @@
# Capability: hash-primitives
## ADDED Requirements
### Package identity
- REQ-HASH-001: Package name is `@suaveplan/hash`, located at `packages/universal/hash`.
- REQ-HASH-002: Runtime deps: `@noble/hashes`, `@suaveplan/error`, `tslib`, `zod`. Zero browser/React/Node-only deps.
- REQ-HASH-003: Ships `"sideEffects": false`. All modules are individually subpath-exported for tree-shaking.
### Hash digests
- REQ-HASH-010: Exports `sha256(data: Uint8Array): Uint8Array` — 32-byte SHA-256 digest.
- REQ-HASH-011: Exports `sha384(data: Uint8Array): Uint8Array` — 48-byte SHA-384 digest.
- REQ-HASH-012: Exports `sha512(data: Uint8Array): Uint8Array` — 64-byte SHA-512 digest.
- REQ-HASH-013: Exports `HashAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"`.
- REQ-HASH-014: Exports `hash(algo: HashAlgorithm, data: Uint8Array): Uint8Array` — algorithm dispatcher.
### HMAC
- REQ-HASH-020: Exports `hmac(algo: HashAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array`.
- REQ-HASH-021: Empty key is permitted (library-level; policy enforcement is the caller's responsibility).
### HKDF
- REQ-HASH-030: Exports `hkdf(algo: HashAlgorithm, ikm: Uint8Array, options: HkdfOptions): Uint8Array`.
- REQ-HASH-031: `HkdfOptions = { salt: Uint8Array; info: Uint8Array; length: number }`. All fields required.
- REQ-HASH-032: Throws `HashError("HASH.KDF.INVALID_PARAMS")` if `length <= 0` or `length > 255 * hashLen`.
### PBKDF2
- REQ-HASH-040: Exports `pbkdf2(algo: HashAlgorithm, password: Uint8Array, options: Pbkdf2Options): Uint8Array`.
- REQ-HASH-041: `Pbkdf2Options = { salt: Uint8Array; iterations: number; length: number }`.
- REQ-HASH-042: Throws `HashError("HASH.KDF.INVALID_PARAMS")` if `iterations < 600_000` (OWASP 2023 minimum for SHA-256). Same minimum for SHA-384/512 (conservative — these are also interactive-login strength minimums).
- REQ-HASH-043: Exports `PBKDF2_MIN_ITERATIONS = 600_000`.
### Argon2id
- REQ-HASH-050: Exports `argon2id(password: Uint8Array, options: Argon2idOptions): Uint8Array`.
- REQ-HASH-051: `Argon2idOptions = { salt: Uint8Array; timeCost: number; memoryCost: number; parallelism: number; length: number }`.
- REQ-HASH-052: Throws `HashError("HASH.KDF.INVALID_PARAMS")` per-field: `timeCost < 1`, `memoryCost < 8`, `parallelism < 1`, `length <= 0`. Each error's `context` includes `{ field, value, minimum }`.
- REQ-HASH-053: Exports `ARGON2ID_MIN_MEMORY_COST_KIB = 8`, `ARGON2ID_MIN_TIME_COST = 1`, `ARGON2ID_MIN_PARALLELISM = 1`.
### Encoding utilities
- REQ-HASH-060: Exports `toHex(bytes: Uint8Array): string` and `fromHex(hex: string): Uint8Array`.
- REQ-HASH-061: Exports `toBase64(bytes: Uint8Array): string`, `fromBase64(b64: string): Uint8Array`, `toBase64Url(bytes: Uint8Array): string`, `fromBase64Url(b64url: string): Uint8Array`.
- REQ-HASH-062: `fromHex`/`fromBase64`/`fromBase64Url` throw `HashError("HASH.ENCODING.INVALID_INPUT")` on malformed input.
### Error surface
- REQ-HASH-070: Exports `HashError extends SuaveplanError<HashCode>`.
- REQ-HASH-071: Error codes namespace: `HASH.KDF.INVALID_PARAMS`, `HASH.ALGORITHM.UNSUPPORTED`, `HASH.ENCODING.INVALID_INPUT`.
- REQ-HASH-072: No error message or context field contains raw key material or password bytes.
### Quality
- REQ-HASH-080: 100% statement + branch coverage (istanbul).
- REQ-HASH-081: fast-check property tests: determinism (same input → same output), HMAC known-vector (RFC 4231 vectors).
- REQ-HASH-082: mitata benchmarks: sha256/sha384/sha512 at 1 KiB and 1 MiB; argon2id at minimum params.
- REQ-HASH-083: Every `.ts` implementation file has a sibling `.md` (≥200 words).
- REQ-HASH-084: `tests/no-disallowed.test.ts` asserts no `Math.random`, `Date.now`, or `crypto.getRandomValues` in `src/`.