first commit
This commit is contained in:
@@ -0,0 +1,98 @@
|
||||
# argon2id.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`argon2id.select.ts` is the build-time primitive selector for the Argon2id implementation in `@suaveplan/crypto-hash`. It re-exports one concrete backend — either `argon2id.noble.ts` (backed by `@noble/hashes`) or `argon2id.native.ts` — based on the `argon2id` entry in `crypto.config.ts`.
|
||||
|
||||
Argon2id is the Password Hashing Competition winner and the recommendation of RFC 9106 for password hashing and key derivation. Its hybrid data-dependent and data-independent memory access pattern resists both GPU-parallel and side-channel attacks, making it the preferred choice when deriving keys from passwords or other low-entropy secrets. The selector exposes `argon2id` and `Argon2idOptions` through a stable interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Re-exports `argon2id(password, options)` and `Argon2idOptions`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `argon2id` to `'noble'` or `'native'`.
|
||||
- `Argon2idOptions` carries `salt`, `timeCost`, `memoryCost`, `parallelism`, and `length`.
|
||||
- RFC 9106 minimum parameter validation is enforced at the public façade, not here.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { argon2id } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
const password = new TextEncoder().encode("correct-horse-battery-staple");
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const hash = argon2id(password, {
|
||||
salt,
|
||||
timeCost: 3,
|
||||
memoryCost: 65536,
|
||||
parallelism: 4,
|
||||
length: 32,
|
||||
});
|
||||
console.log(hash.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { argon2id, type Argon2idOptions } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
// Interactive login profile (RFC 9106 minimum-t=1 adjusted for latency).
|
||||
const INTERACTIVE_PARAMS: Argon2idOptions = {
|
||||
salt: new Uint8Array(16), // placeholder; real callers supply random salt
|
||||
timeCost: 2,
|
||||
memoryCost: 19456, // ~19 MiB
|
||||
parallelism: 1,
|
||||
length: 32,
|
||||
};
|
||||
|
||||
// Server-side profile (higher security, longer wait acceptable).
|
||||
const SENSITIVE_PARAMS: Argon2idOptions = {
|
||||
salt: new Uint8Array(16),
|
||||
timeCost: 3,
|
||||
memoryCost: 65536, // ~64 MiB
|
||||
parallelism: 4,
|
||||
length: 64,
|
||||
};
|
||||
|
||||
function deriveKey(password: Uint8Array, opts: Argon2idOptions): Uint8Array {
|
||||
return argon2id(password, opts);
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `argon2id(password: Uint8Array, options: Argon2idOptions): Uint8Array`
|
||||
|
||||
Derives a key from a password using the Argon2id algorithm.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `password` | `Uint8Array` | Password bytes. |
|
||||
| `options.salt` | `Uint8Array` | Random salt (≥16 bytes per RFC 9106). |
|
||||
| `options.timeCost` | `number` | Number of passes over memory (≥1). |
|
||||
| `options.memoryCost` | `number` | Memory usage in KiB (≥8 × parallelism). |
|
||||
| `options.parallelism` | `number` | Degree of parallelism (≥1). |
|
||||
| `options.length` | `number` | Output length in bytes (≥4). |
|
||||
|
||||
**Returns:** Derived key of `options.length` bytes.
|
||||
|
||||
### `Argon2idOptions`
|
||||
|
||||
```ts
|
||||
interface Argon2idOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly timeCost: number;
|
||||
readonly memoryCost: number;
|
||||
readonly parallelism: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Noble's Argon2id implementation is a pure-JavaScript port that runs synchronously. It passes RFC 9106 Appendix B test vectors. Memory consumption scales linearly with `memoryCost` in KiB, so high memory costs can cause out-of-memory errors in constrained environments (e.g. edge workers). The primitive does not enforce RFC 9106 parameter minima — those checks are delegated to the public KDF façade. The native stub throws `PrimitiveNotImplemented("argon2id")`. Unlike PBKDF2, Argon2id is inherently sequential in its memory access, which limits parallelism-based GPU attacks but also means JavaScript's single-threaded nature does not significantly disadvantage it relative to native binaries.
|
||||
@@ -0,0 +1,83 @@
|
||||
# argon2id.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`argon2id.native.ts` is the native-backend placeholder for the Argon2id primitive in `@suaveplan/crypto-hash`. It exports the same `argon2id` function and `Argon2idOptions` interface as the Noble implementation but throws `PrimitiveNotImplemented("argon2id")` unconditionally.
|
||||
|
||||
This stub completes the two-implementation selector layout for Argon2id. Argon2id's native backend is the most valuable of the placeholders because a native implementation could run Argon2id in a separate thread and avoid blocking the event loop, which the pure-JavaScript Noble implementation cannot do. The stub communicates that this capability is planned but not yet available.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies `argon2id(password, options): Uint8Array` interface.
|
||||
- Exports `Argon2idOptions` (identical to Noble file) for selector re-export.
|
||||
- Throws `PrimitiveNotImplemented("argon2id")` unconditionally.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets argon2id to 'native'.
|
||||
import { argon2id } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
try {
|
||||
argon2id(new Uint8Array(8), {
|
||||
salt: new Uint8Array(16),
|
||||
timeCost: 1,
|
||||
memoryCost: 8192,
|
||||
parallelism: 1,
|
||||
length: 32,
|
||||
});
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: argon2id
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import type { Argon2idOptions } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
// Build a typed options object to pass to whichever backend is active.
|
||||
function interactiveParams(salt: Uint8Array): Argon2idOptions {
|
||||
return {
|
||||
salt,
|
||||
timeCost: 2,
|
||||
memoryCost: 19456,
|
||||
parallelism: 1,
|
||||
length: 32,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `argon2id(_password: Uint8Array, _options: Argon2idOptions): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_password` | `Uint8Array` | Ignored. |
|
||||
| `_options` | `Argon2idOptions` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("argon2id")` — always.
|
||||
|
||||
### `Argon2idOptions`
|
||||
|
||||
```ts
|
||||
interface Argon2idOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly timeCost: number;
|
||||
readonly memoryCost: number;
|
||||
readonly parallelism: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The future native implementation is expected to call into `libsodium` or a dedicated Argon2 native module to run the memory-hard computation off the JavaScript event loop. When that implementation lands and passes RFC 9106 Appendix B test vectors via the KAT harness, this file is updated, parameter prefixes removed, and the selector regenerated to point here. The `Argon2idOptions` interface is self-contained in this file so both backends are independent — the selector re-exports from whichever is active.
|
||||
@@ -0,0 +1,88 @@
|
||||
# argon2id.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`argon2id.noble.ts` is the Noble-backed concrete implementation of Argon2id in `@suaveplan/crypto-hash`. It wraps `argon2id` from `@noble/hashes/argon2`, translating the `Argon2idOptions` struct into Noble's parameter object (`t`, `m`, `p`, `dkLen`).
|
||||
|
||||
Argon2id (RFC 9106) is the Password Hashing Competition winner, combining the data-dependent memory access of Argon2d with the data-independent pass structure of Argon2i. It is the recommended algorithm when deriving keys from passwords or other low-entropy secrets. This file is the production implementation; `argon2id.native.ts` is the placeholder stub.
|
||||
|
||||
## Features
|
||||
|
||||
- Accepts `Argon2idOptions` with `salt`, `timeCost`, `memoryCost`, `parallelism`, and `length`.
|
||||
- Delegates to `@noble/hashes/argon2` which implements the full RFC 9106 algorithm.
|
||||
- Runs synchronously (no worker/thread offload at this layer).
|
||||
- Exports `Argon2idOptions` for re-export by the selector.
|
||||
- RFC 9106 Appendix B test vectors pass in the test suite.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { argon2id } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
const password = new TextEncoder().encode("correct-horse-battery-staple");
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const key = argon2id(password, {
|
||||
salt,
|
||||
timeCost: 3,
|
||||
memoryCost: 65536,
|
||||
parallelism: 4,
|
||||
length: 32,
|
||||
});
|
||||
console.log(key.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { argon2id, type Argon2idOptions } from "@suaveplan/crypto-hash/primitives/argon2id/argon2id";
|
||||
|
||||
// Server-side registration: hash the password and store only the derived key.
|
||||
const SERVER_PARAMS: Omit<Argon2idOptions, "salt"> = {
|
||||
timeCost: 3,
|
||||
memoryCost: 65536,
|
||||
parallelism: 4,
|
||||
length: 64,
|
||||
};
|
||||
|
||||
function hashPassword(password: string): { salt: Uint8Array; key: Uint8Array } {
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const key = argon2id(new TextEncoder().encode(password), { ...SERVER_PARAMS, salt });
|
||||
return { salt, key };
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `argon2id(password: Uint8Array, options: Argon2idOptions): Uint8Array`
|
||||
|
||||
Derives key material from a password using Argon2id.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `password` | `Uint8Array` | Password bytes. |
|
||||
| `options.salt` | `Uint8Array` | Random salt (≥16 bytes per RFC 9106). |
|
||||
| `options.timeCost` | `number` | Number of passes over the memory array (≥1). |
|
||||
| `options.memoryCost` | `number` | Memory usage in KiB (≥8 × parallelism). |
|
||||
| `options.parallelism` | `number` | Degree of parallelism (≥1). |
|
||||
| `options.length` | `number` | Derived key length in bytes (≥4). |
|
||||
|
||||
**Returns:** `Uint8Array` of `options.length` bytes.
|
||||
|
||||
### `Argon2idOptions`
|
||||
|
||||
```ts
|
||||
interface Argon2idOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly timeCost: number;
|
||||
readonly memoryCost: number;
|
||||
readonly parallelism: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Noble maps `timeCost → t`, `memoryCost → m`, `parallelism → p`, and `length → dkLen`. All parameter validation (RFC 9106 minima checks) is delegated to the public KDF façade; this file passes values through without checking. Noble's Argon2id is a pure-JavaScript implementation — no WASM — so it runs in any JavaScript environment but may be slower than a native binary for high memory costs. Synchronous execution on large `memoryCost` values can block the event loop; callers that need async should wrap in a worker thread.
|
||||
@@ -0,0 +1,63 @@
|
||||
# blake2b
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
- [blake2b.noble](./impl/blake2b.noble/blake2b.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b](./blake2b.md) — <!-- begin:auto-docs --> _(no submodules)_- blake2b.noble — <!-- begin:auto-docs --> _(no submod…
|
||||
- [blake2b.noble](./impl/blake2b.noble/blake2b.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b](./blake2b.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b.noble](./impl/blake2b.noble/blake2b.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b](./blake2b.md) — <!-- begin:auto-docs --> _(no submodules)_- blake2b.noble — <!-- begin:a…
|
||||
- [blake2b.noble](./impl/blake2b.noble/blake2b.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b](./blake2b.md) — <!-- begin:auto-docs --> _(no submodules)_- blake2b.noble — <!-- begin:auto-docs --> _(no submo…
|
||||
- [blake2b.noble](./impl/blake2b.noble/blake2b.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [blake2b](./blake2b.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Purpose
|
||||
|
||||
`blake2b.ts` is the public entry point for the BLAKE2b hash primitive (RFC 7693) in `@suaveplan/crypto-hash`. BLAKE2b is a fast, secure cryptographic hash function used by Argon2, password-manager keystreams, blockchain-protocol commitments (Zcash, Polkadot), and many other modern protocols. The suaveplan implementation routes through the per-primitive selector layout (`blake2b.select.ts`) so the noble engine can be replaced without changing caller imports.
|
||||
|
||||
The public surface is two symbols: `blake2b(message, options)` and `Blake2bOptions`. Variable output length 1..64 bytes covers everything from short MACs (e.g. 16-byte authenticator tags) to full 64-byte digests. Optional `key`, `salt`, and `personalization` parameters mix into the BLAKE2b parameter block as specified by RFC 7693.
|
||||
|
||||
## Features
|
||||
|
||||
- RFC 7693 compliance — passes the official `BLAKE2b-512("abc")` known-answer test byte-for-byte.
|
||||
- Variable output length — `dkLen` can be any value in `1..64` (anything outside this range throws `HashError(HASH.BLAKE2B_OUTPUT_TOO_LONG)`).
|
||||
- Optional MAC key — turns the hash into a keyed MAC (BLAKE2b is the only mainstream hash with a built-in MAC mode that does not require an outer HMAC construction). Key length capped at 64 bytes; longer keys throw `HashError(HASH.BLAKE2B_KEY_TOO_LONG)`.
|
||||
- Optional 16-byte `salt` and `personalization` parameters mix into the parameter block (currently passed through to the noble implementation; not all noble versions surface them, so behaviour is "best-effort honored").
|
||||
- Synchronous — `blake2b` returns a `Uint8Array` directly. Cheap enough for the hot path.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { blake2b } from "@suaveplan/crypto-hash/blake2b";
|
||||
|
||||
const digest = blake2b(new TextEncoder().encode("hello world"), { dkLen: 32 });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { blake2b } from "@suaveplan/crypto-hash/blake2b";
|
||||
|
||||
// Use BLAKE2b in keyed (MAC) mode for short authenticators.
|
||||
function authenticate(message: Uint8Array, secret: Uint8Array): Uint8Array {
|
||||
if (secret.length > 64) {
|
||||
throw new Error("BLAKE2b key must be ≤ 64 bytes");
|
||||
}
|
||||
return blake2b(message, { dkLen: 16, key: secret });
|
||||
}
|
||||
|
||||
// Domain-separated commitments via the personalization parameter (where
|
||||
// the underlying engine surfaces it).
|
||||
function commit(payload: Uint8Array, domain: string): Uint8Array {
|
||||
const personalization = new Uint8Array(16);
|
||||
personalization.set(new TextEncoder().encode(domain.slice(0, 16)));
|
||||
return blake2b(payload, { dkLen: 32, personalization });
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `blake2b(message: Uint8Array, options: Blake2bOptions): Uint8Array` — BLAKE2b hash.
|
||||
- `Blake2bOptions { dkLen: number; key?: Uint8Array; salt?: Uint8Array; personalization?: Uint8Array }` — `dkLen` is the output length in bytes (1..64).
|
||||
|
||||
Errors: `HASH.BLAKE2B_OUTPUT_TOO_LONG` when `dkLen > 64` or `dkLen < 1` or `dkLen` is not an integer; `HASH.BLAKE2B_KEY_TOO_LONG` when `key.length > 64`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The selector pattern (`blake2b.select.ts`) is generated; do not edit it. To add a native engine, place it under `impl/blake2b.native/blake2b.native.ts` and rerun `bun scripts/gen-crypto-selects.ts`. The implementation does not validate that `salt` and `personalization` are exactly 16 bytes — the noble engine ignores incorrectly-sized buffers rather than throwing, and replicating that behaviour here keeps the surface predictable.
|
||||
@@ -0,0 +1,50 @@
|
||||
# blake2b.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`blake2b.noble.ts` is the noble-backed implementation of BLAKE2b in `@suaveplan/crypto-hash`. It wraps `@noble/hashes/blake2b::blake2b` with parameter validation and the `HashError` substrate. Selector forwarding is done by the generated `blake2b.select.ts` file; consumers should never import from here directly.
|
||||
|
||||
The noble implementation is pure JavaScript and runs everywhere ESM does — Node 20+, Bun, browsers, Deno, Cloudflare Workers. Synchronous; no main-thread yielding needed since BLAKE2b is fast (multi-GiB/s on modern hardware for the 64-byte block size, even in pure JS).
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@noble/hashes/blake2b::blake2b` and exposes `blake2b(message, options)`.
|
||||
- Validates `dkLen` in `1..64` integer range; throws `HashError(BLAKE2B_OUTPUT_TOO_LONG)` otherwise.
|
||||
- Validates `key.length <= 64`; throws `HashError(BLAKE2B_KEY_TOO_LONG)` otherwise.
|
||||
- Exposes `Blake2bOptions { dkLen, key?, salt?, personalization? }` as the canonical parameter bundle.
|
||||
- Synchronous (`Uint8Array`).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { blake2b } from "@suaveplan/crypto-hash/primitives/blake2b/impl/blake2b.noble";
|
||||
|
||||
const out = blake2b(new TextEncoder().encode("hello"), { dkLen: 32 });
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { blake2b } from "@suaveplan/crypto-hash/primitives/blake2b/impl/blake2b.noble";
|
||||
|
||||
// Build a Merkle-leaf commitment scheme using BLAKE2b in keyed mode for
|
||||
// domain-separation. The leaf key is itself derived from a per-tree
|
||||
// commitment key so different trees use different MAC keys.
|
||||
function leaf(treeKey: Uint8Array, payload: Uint8Array): Uint8Array {
|
||||
return blake2b(payload, { dkLen: 32, key: treeKey });
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `blake2b(message: Uint8Array, options: Blake2bOptions): Uint8Array` — pure synchronous BLAKE2b hash.
|
||||
- `Blake2bOptions { dkLen: number; key?: Uint8Array; salt?: Uint8Array; personalization?: Uint8Array }`.
|
||||
|
||||
Errors: `HASH.BLAKE2B_OUTPUT_TOO_LONG`, `HASH.BLAKE2B_KEY_TOO_LONG`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Validation is checked before delegating to noble so a bad parameter set is rejected before any allocation happens. The noble implementation accepts `salt` and `personalization` parameters but does not strictly require them to be 16 bytes — the suaveplan wrapper passes them through unchanged and lets noble apply its own padding/truncation rules. This matches the upstream library's behaviour and avoids divergent semantics if noble updates its handling in a future release.
|
||||
@@ -0,0 +1,96 @@
|
||||
# hkdf.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hkdf.select.ts` is the build-time primitive selector for the HKDF implementation in `@suaveplan/crypto-hash`. It re-exports exactly one concrete backend — either `hkdf.noble.ts` (backed by `@noble/hashes`) or `hkdf.native.ts` — depending on the `hkdf` field in `crypto.config.ts`.
|
||||
|
||||
HKDF (HMAC-based Extract-and-Expand Key Derivation Function, RFC 5869) is the standard KDF for deriving multiple independent keys from a single input keying material (IKM) such as a Diffie-Hellman shared secret or a session master secret. This selector exposes the `hkdf` function, `HkdfAlgorithm`, and `HkdfOptions` through a stable interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Re-exports `hkdf(algo, ikm, options)`, `HkdfAlgorithm`, and `HkdfOptions`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `hkdf` to `'noble'` or `'native'`.
|
||||
- Supports SHA-256, SHA-384, and SHA-512 as the underlying PRF.
|
||||
- `HkdfOptions` carries `salt`, `info`, and `length` for Extract and Expand phases.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { hkdf } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
const ikm = new Uint8Array(32).fill(0xff);
|
||||
const key = hkdf("SHA-256", ikm, {
|
||||
salt: new Uint8Array(32),
|
||||
info: new TextEncoder().encode("session-key"),
|
||||
length: 32,
|
||||
});
|
||||
console.log(key.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { hkdf, type HkdfAlgorithm } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
// Derive two independent sub-keys from one shared secret (X25519 output).
|
||||
function deriveKeys(sharedSecret: Uint8Array, salt: Uint8Array): {
|
||||
encKey: Uint8Array;
|
||||
authKey: Uint8Array;
|
||||
} {
|
||||
const enc = new TextEncoder();
|
||||
return {
|
||||
encKey: hkdf("SHA-256", sharedSecret, {
|
||||
salt,
|
||||
info: enc.encode("enc-key"),
|
||||
length: 32,
|
||||
}),
|
||||
authKey: hkdf("SHA-256", sharedSecret, {
|
||||
salt,
|
||||
info: enc.encode("auth-key"),
|
||||
length: 32,
|
||||
}),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `hkdf(algo: HkdfAlgorithm, ikm: Uint8Array, options: HkdfOptions): Uint8Array`
|
||||
|
||||
Runs HKDF Extract followed by HKDF Expand.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `HkdfAlgorithm` | Hash algorithm for the internal HMAC. |
|
||||
| `ikm` | `Uint8Array` | Input keying material. |
|
||||
| `options.salt` | `Uint8Array` | Optional but strongly recommended random salt. |
|
||||
| `options.info` | `Uint8Array` | Context/application-specific binding string. |
|
||||
| `options.length` | `number` | Output length in bytes (max 255 × hash output length). |
|
||||
|
||||
**Returns:** Derived key material of `options.length` bytes.
|
||||
|
||||
### `HkdfAlgorithm`
|
||||
|
||||
```ts
|
||||
type HkdfAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `HkdfOptions`
|
||||
|
||||
```ts
|
||||
interface HkdfOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly info: Uint8Array;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
HKDF is a two-step process. Extract collapses potentially non-uniform IKM into a pseudorandom key (PRK) using HMAC with the salt. Expand stretches PRK into the requested number of output bytes by iterating HMAC with the info string. Noble's `hkdf` function implements RFC 5869 §2 exactly, including the `0x01`, `0x02`, … counter suffixes in the Expand loop. The `length` maximum per RFC 5869 is `255 * HashLen`; violations will cause Noble to throw, which propagates up without wrapping here — validation at the public KDF façade should guard against this before reaching the primitive. The native stub throws `PrimitiveNotImplemented("hkdf")`.
|
||||
@@ -0,0 +1,84 @@
|
||||
# hkdf.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hkdf.native.ts` is the native-backend placeholder for the HKDF primitive in `@suaveplan/crypto-hash`. It exports the same `hkdf` function, `HkdfAlgorithm` type, and `HkdfOptions` interface as the Noble implementation but throws `PrimitiveNotImplemented("hkdf")` unconditionally.
|
||||
|
||||
This stub keeps the two-implementation selector layout complete for HKDF. It exports all shared types so the selector can re-export them independent of which backend is active. Having the full type surface on both sides means callers can reference `HkdfAlgorithm` and `HkdfOptions` without caring about the selected backend.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies `hkdf(algo, ikm, options): Uint8Array` interface.
|
||||
- Exports `HkdfAlgorithm` and `HkdfOptions` (identical to the Noble file).
|
||||
- Throws `PrimitiveNotImplemented("hkdf")` unconditionally.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets hkdf to 'native'.
|
||||
import { hkdf } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
try {
|
||||
hkdf("SHA-256", new Uint8Array(32), {
|
||||
salt: new Uint8Array(32),
|
||||
info: new Uint8Array(0),
|
||||
length: 32,
|
||||
});
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: hkdf
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// Use types from the native file to write backend-agnostic caller code.
|
||||
import type { HkdfAlgorithm, HkdfOptions } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
function buildHkdfParams(label: string, algo: HkdfAlgorithm = "SHA-256"): HkdfOptions {
|
||||
return {
|
||||
salt: crypto.getRandomValues(new Uint8Array(32)),
|
||||
info: new TextEncoder().encode(label),
|
||||
length: 32,
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hkdf(_algo: HkdfAlgorithm, _ikm: Uint8Array, _options: HkdfOptions): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_algo` | `HkdfAlgorithm` | Ignored. |
|
||||
| `_ikm` | `Uint8Array` | Ignored. |
|
||||
| `_options` | `HkdfOptions` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("hkdf")` — always.
|
||||
|
||||
### `HkdfAlgorithm`
|
||||
|
||||
```ts
|
||||
type HkdfAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `HkdfOptions`
|
||||
|
||||
```ts
|
||||
interface HkdfOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly info: Uint8Array;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Types are duplicated in this file rather than imported from the Noble file to keep both implementations self-contained. The selector re-exports from whichever file is active, so callers always get consistent types regardless of the chosen backend. When the in-house HKDF implementation is complete (RFC 5869 KAT suite must pass), the throw is replaced with the real algorithm, parameter prefixes removed, and the selector regenerated.
|
||||
@@ -0,0 +1,90 @@
|
||||
# hkdf.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hkdf.noble.ts` is the Noble-backed concrete implementation of HKDF in `@suaveplan/crypto-hash`. It wraps `hkdf` from `@noble/hashes/hkdf`, running both the Extract and Expand phases of RFC 5869 using SHA-256, SHA-384, or SHA-512 as the underlying PRF.
|
||||
|
||||
HKDF transforms input keying material (a Diffie-Hellman shared secret, a session master secret, or any other high-entropy byte sequence) into one or more independent derived keys. This file is the production implementation side of the two-file layout; `hkdf.native.ts` is the placeholder stub.
|
||||
|
||||
## Features
|
||||
|
||||
- Supports `HkdfAlgorithm`: `"SHA-256"`, `"SHA-384"`, `"SHA-512"`.
|
||||
- Runs full RFC 5869 Extract-then-Expand in a single call.
|
||||
- Accepts structured `HkdfOptions` with `salt`, `info`, and `length`.
|
||||
- Returns derived key material of exactly `options.length` bytes.
|
||||
- Exports `HkdfAlgorithm` and `HkdfOptions` for re-export by the selector.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { hkdf } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
const ikm = crypto.getRandomValues(new Uint8Array(32));
|
||||
const key = hkdf("SHA-256", ikm, {
|
||||
salt: new Uint8Array(32),
|
||||
info: new TextEncoder().encode("key"),
|
||||
length: 32,
|
||||
});
|
||||
console.log(key.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { hkdf, type HkdfAlgorithm } from "@suaveplan/crypto-hash/primitives/hkdf/hkdf";
|
||||
|
||||
// Derive three sub-keys from a shared secret for a forward-secure session.
|
||||
function deriveSessionKeys(sharedSecret: Uint8Array, salt: Uint8Array): {
|
||||
encKey: Uint8Array;
|
||||
macKey: Uint8Array;
|
||||
ivBase: Uint8Array;
|
||||
} {
|
||||
const enc = new TextEncoder();
|
||||
const derive = (info: string, len: number) =>
|
||||
hkdf("SHA-256", sharedSecret, { salt, info: enc.encode(info), length: len });
|
||||
return {
|
||||
encKey: derive("enc", 32),
|
||||
macKey: derive("mac", 32),
|
||||
ivBase: derive("iv", 12),
|
||||
};
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hkdf(algo: HkdfAlgorithm, ikm: Uint8Array, options: HkdfOptions): Uint8Array`
|
||||
|
||||
Runs HKDF Extract + Expand.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `HkdfAlgorithm` | Hash algorithm for HMAC-PRF. |
|
||||
| `ikm` | `Uint8Array` | Input keying material (any length). |
|
||||
| `options.salt` | `Uint8Array` | Random salt; use a zero-filled buffer if no salt is available. |
|
||||
| `options.info` | `Uint8Array` | Context/binding string for domain separation. |
|
||||
| `options.length` | `number` | Output length in bytes (max 255 × HashLen). |
|
||||
|
||||
**Returns:** `Uint8Array` of exactly `options.length` bytes.
|
||||
|
||||
### `HkdfAlgorithm`
|
||||
|
||||
```ts
|
||||
type HkdfAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `HkdfOptions`
|
||||
|
||||
```ts
|
||||
interface HkdfOptions {
|
||||
readonly salt: Uint8Array;
|
||||
readonly info: Uint8Array;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The Noble `hkdf(H, ikm, salt, info, dkLen)` call implements RFC 5869 exactly. The internal `hasherFor` helper maps `HkdfAlgorithm` to the Noble hasher via two explicit `if` checks, consistent with the pattern in `hmac.noble.ts` and the SHA-2 dispatch helpers. If `options.length` exceeds `255 × HashLen`, Noble throws a `RangeError`; that error propagates uncaught — the public KDF façade is responsible for validating the length before calling into this primitive. RFC 5869 Appendix A test vectors pass in the test suite.
|
||||
@@ -0,0 +1,76 @@
|
||||
# hmac.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hmac.select.ts` is the build-time primitive selector for the HMAC implementation within `@suaveplan/crypto-hash`. It re-exports exactly one concrete backend — either `hmac.noble.ts` (backed by `@noble/hashes`) or `hmac.native.ts` — according to the `hmac` entry in `crypto.config.ts`.
|
||||
|
||||
HMAC (Hash-based Message Authentication Code, RFC 2104) is the standard construction for producing a keyed digest from an arbitrary hash function. This selector exposes a single `hmac` function and the `HmacAlgorithm` type, providing a consistent interface regardless of which backend is active.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-symbol re-export of `hmac(algo, key, data)` and `HmacAlgorithm`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `hmac` to `'noble'` or `'native'`.
|
||||
- Supports `"SHA-256"`, `"SHA-384"`, and `"SHA-512"` hash algorithms.
|
||||
- Maintains a stable public API across backend switches.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { hmac } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
const key = new Uint8Array(32).fill(0xab);
|
||||
const data = new TextEncoder().encode("authenticate me");
|
||||
const tag = hmac("SHA-256", key, data);
|
||||
console.log(tag.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { hmac, type HmacAlgorithm } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
// Verify a webhook signature from an untrusted header.
|
||||
function verifyWebhookSignature(
|
||||
algo: HmacAlgorithm,
|
||||
secret: Uint8Array,
|
||||
body: Uint8Array,
|
||||
expected: Uint8Array
|
||||
): boolean {
|
||||
const computed = hmac(algo, secret, body);
|
||||
// Constant-time comparison avoids timing side-channels.
|
||||
if (computed.length !== expected.length) return false;
|
||||
let diff = 0;
|
||||
for (let i = 0; i < computed.length; i++) diff |= computed[i] ^ expected[i];
|
||||
return diff === 0;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `hmac(algo: HmacAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array`
|
||||
|
||||
Computes HMAC over `data` with `key` using the specified hash algorithm.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `HmacAlgorithm` | `"SHA-256"`, `"SHA-384"`, or `"SHA-512"`. |
|
||||
| `key` | `Uint8Array` | Secret key bytes (any length). |
|
||||
| `data` | `Uint8Array` | Message to authenticate. |
|
||||
|
||||
**Returns:** HMAC tag — 32 bytes for SHA-256, 48 for SHA-384, 64 for SHA-512.
|
||||
|
||||
### `HmacAlgorithm`
|
||||
|
||||
```ts
|
||||
type HmacAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
HMAC is defined as `H(K XOR opad || H(K XOR ipad || message))`. The Noble backend calls `@noble/hashes/hmac` directly, which implements this construction on top of the same audited SHA-2 primitives used by the standalone hash functions. Key padding and the inner/outer hash passes are handled internally. RFC 2202 (SHA-256) and RFC 4231 (SHA-384/512) test vectors pass in the test suite. The native stub throws `PrimitiveNotImplemented("hmac")` and will be activated in Phase 3 after per-primitive audit.
|
||||
@@ -0,0 +1,73 @@
|
||||
# hmac.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hmac.native.ts` is the native-backend placeholder for the HMAC primitive in `@suaveplan/crypto-hash`. It exports the same `hmac` function signature and `HmacAlgorithm` type as `hmac.noble.ts`, but throws `PrimitiveNotImplemented("hmac")` unconditionally.
|
||||
|
||||
This stub keeps the two-implementation primitive selector layout complete for HMAC. Both files must exist so the generator script can point the selector at either backend without structural changes. The stub also exports `HmacAlgorithm` so the selector can re-export the type regardless of which backend is active.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies `hmac(algo, key, data): Uint8Array` interface.
|
||||
- Exports `HmacAlgorithm = "SHA-256" | "SHA-384" | "SHA-512"` type.
|
||||
- Throws `PrimitiveNotImplemented("hmac")` unconditionally.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets hmac to 'native'.
|
||||
import { hmac } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
try {
|
||||
hmac("SHA-256", new Uint8Array(32), new Uint8Array(8));
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: hmac
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// Capability check pattern for environments where native HMAC may not be available.
|
||||
import { hmac } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
function isHmacAvailable(): boolean {
|
||||
try {
|
||||
hmac("SHA-256", new Uint8Array(1), new Uint8Array(1));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hmac(_algo: HmacAlgorithm, _key: Uint8Array, _data: Uint8Array): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_algo` | `HmacAlgorithm` | Ignored. |
|
||||
| `_key` | `Uint8Array` | Ignored. |
|
||||
| `_data` | `Uint8Array` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("hmac")` — always.
|
||||
|
||||
### `HmacAlgorithm`
|
||||
|
||||
```ts
|
||||
type HmacAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
Exported to allow the selector to re-export the type independent of the chosen backend.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
All three parameters carry underscore prefixes to suppress unused-parameter diagnostics from TypeScript and Biome without inline suppressions. The `HmacAlgorithm` type is duplicated here (rather than imported from the noble file) so this file is self-contained and can be used independently of the noble backend when the selector is pointed at it. When the native implementation is ready, the parameter prefixes are removed, the `throw` is replaced with the real algorithm, and the selector is regenerated.
|
||||
@@ -0,0 +1,78 @@
|
||||
# hmac.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`hmac.noble.ts` is the Noble-backed concrete implementation of the HMAC primitive in `@suaveplan/crypto-hash`. It wraps `hmac` from `@noble/hashes/hmac`, supporting SHA-256, SHA-384, and SHA-512 as the underlying hash function through the `HmacAlgorithm` discriminant.
|
||||
|
||||
HMAC (RFC 2104) is the standard keyed-hash message authentication code. It proves both message integrity and knowledge of the secret key. This file is the production-ready side of the two-implementation HMAC layout, paired with `hmac.native.ts`.
|
||||
|
||||
## Features
|
||||
|
||||
- Supports `HmacAlgorithm`: `"SHA-256"`, `"SHA-384"`, `"SHA-512"`.
|
||||
- Delegates to `@noble/hashes/hmac` with the appropriate SHA-2 hasher selected at call time.
|
||||
- Returns a fresh tag of 32, 48, or 64 bytes depending on the chosen algorithm.
|
||||
- Exports `HmacAlgorithm` type for use by callers and the selector file.
|
||||
- Zero side effects; thread-safe for concurrent use.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { hmac } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
const key = crypto.getRandomValues(new Uint8Array(32));
|
||||
const data = new TextEncoder().encode("sign this");
|
||||
const tag = hmac("SHA-256", key, data);
|
||||
console.log(tag.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { hmac, type HmacAlgorithm } from "@suaveplan/crypto-hash/primitives/hmac/hmac";
|
||||
|
||||
// Generic HMAC wrapper that selects strength based on security tier.
|
||||
function computeTag(
|
||||
tier: "standard" | "high",
|
||||
key: Uint8Array,
|
||||
data: Uint8Array
|
||||
): Uint8Array {
|
||||
const algo: HmacAlgorithm = tier === "high" ? "SHA-512" : "SHA-256";
|
||||
return hmac(algo, key, data);
|
||||
}
|
||||
|
||||
const tag = computeTag("high", new Uint8Array(64).fill(1), new Uint8Array(16));
|
||||
console.log(tag.length); // 64
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `hmac(algo: HmacAlgorithm, key: Uint8Array, data: Uint8Array): Uint8Array`
|
||||
|
||||
Computes HMAC over `data` using `key`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `HmacAlgorithm` | Hash algorithm: `"SHA-256"`, `"SHA-384"`, or `"SHA-512"`. |
|
||||
| `key` | `Uint8Array` | Secret key bytes (any length; Noble pads or hashes internally per RFC 2104). |
|
||||
| `data` | `Uint8Array` | Message bytes to authenticate. |
|
||||
|
||||
**Returns:** HMAC tag — 32 bytes (SHA-256), 48 bytes (SHA-384), or 64 bytes (SHA-512).
|
||||
|
||||
**Throws:** Never under normal conditions.
|
||||
|
||||
### `HmacAlgorithm`
|
||||
|
||||
```ts
|
||||
type HmacAlgorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### Internal: `hasherFor(algo)`
|
||||
|
||||
A private helper that maps `HmacAlgorithm` to the corresponding Noble hasher class. Not exported.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `hasherFor` function uses two explicit `if` checks and a fallthrough to SHA-512, matching the pattern used in the other multi-algorithm selectors in this package so that TypeScript's exhaustiveness analysis catches future `HmacAlgorithm` additions. Noble's `hmac(H, key, data)` call handles RFC 2104 key preparation (hashing keys longer than the block size, padding shorter ones with zeros) internally. RFC 2202 and RFC 4231 test vectors pass in the test suite. The tag comparison for verification must use a constant-time equality function from `@suaveplan/crypto-utils` — never a direct byte-array comparison.
|
||||
@@ -0,0 +1,80 @@
|
||||
# pbkdf2.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`pbkdf2.native.ts` is the native-backend placeholder for the PBKDF2 primitive in `@suaveplan/crypto-hash`. It exports the same `pbkdf2` function, `Pbkdf2Algorithm` type, and `Pbkdf2Options` interface as the Noble implementation but throws `PrimitiveNotImplemented("pbkdf2")` unconditionally.
|
||||
|
||||
This file completes the two-implementation selector layout for PBKDF2. Both backends must be present for the generator script and CI layout tests to treat the primitive as fully scaffolded. Exporting the types here makes the selector's re-exports correct regardless of which backend is active.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies `pbkdf2(algo, password, options): Uint8Array` interface.
|
||||
- Exports `Pbkdf2Algorithm` and `Pbkdf2Options` (identical to Noble file).
|
||||
- Throws `PrimitiveNotImplemented("pbkdf2")` unconditionally.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets pbkdf2 to 'native'.
|
||||
import { pbkdf2 } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
try {
|
||||
pbkdf2("SHA-256", new Uint8Array(8), {
|
||||
salt: new Uint8Array(16),
|
||||
iterations: 1,
|
||||
length: 32,
|
||||
});
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: pbkdf2
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import type { Pbkdf2Options } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
// Build a Pbkdf2Options object with sensible defaults.
|
||||
function buildParams(salt: Uint8Array): Pbkdf2Options {
|
||||
return { salt, iterations: 600_000, length: 32 };
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `pbkdf2(_algo: Pbkdf2Algorithm, _password: Uint8Array, _options: Pbkdf2Options): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_algo` | `Pbkdf2Algorithm` | Ignored. |
|
||||
| `_password` | `Uint8Array` | Ignored. |
|
||||
| `_options` | `Pbkdf2Options` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("pbkdf2")` — always.
|
||||
|
||||
### `Pbkdf2Algorithm`
|
||||
|
||||
```ts
|
||||
type Pbkdf2Algorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `Pbkdf2Options`
|
||||
|
||||
```ts
|
||||
interface Pbkdf2Options {
|
||||
readonly salt: Uint8Array;
|
||||
readonly iterations: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Types are self-contained here so both implementations are independent of each other. The selector re-exports types from whichever file is active; callers see no difference. When the native PBKDF2 implementation is ready and has passed RFC 6070 / NIST SP 800-132 test vectors via the KAT harness, this file is updated, parameter prefixes removed, and the selector regenerated. Until then, switching `crypto.config.ts` to `'native'` for this primitive will immediately surface the error.
|
||||
@@ -0,0 +1,89 @@
|
||||
# pbkdf2.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`pbkdf2.noble.ts` is the Noble-backed concrete implementation of PBKDF2 in `@suaveplan/crypto-hash`. It wraps `pbkdf2` from `@noble/hashes/pbkdf2`, supporting SHA-256, SHA-384, and SHA-512 as the underlying HMAC-PRF. The interface accepts a structured `Pbkdf2Options` object carrying the salt, iteration count, and desired output length.
|
||||
|
||||
PBKDF2 (RFC 8018 §5.2) is the oldest of the standard password-based KDFs and enjoys broad native support in WebCrypto, OpenSSL, and PKCS#5 implementations. This file is the production-ready side of the two-implementation layout.
|
||||
|
||||
## Features
|
||||
|
||||
- Supports `Pbkdf2Algorithm`: `"SHA-256"`, `"SHA-384"`, `"SHA-512"`.
|
||||
- Accepts `Pbkdf2Options` with `salt`, `iterations`, and `length`.
|
||||
- Delegates to `@noble/hashes/pbkdf2` without additional abstraction overhead.
|
||||
- Returns derived key material of exactly `options.length` bytes.
|
||||
- Exports `Pbkdf2Algorithm` and `Pbkdf2Options` for the selector.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { pbkdf2 } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
const password = new TextEncoder().encode("my-password");
|
||||
const salt = crypto.getRandomValues(new Uint8Array(32));
|
||||
const key = pbkdf2("SHA-256", password, {
|
||||
salt,
|
||||
iterations: 600_000,
|
||||
length: 32,
|
||||
});
|
||||
console.log(key.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { pbkdf2, type Pbkdf2Algorithm } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
// Derive a 48-byte key for AES-384 using SHA-384 PBKDF2.
|
||||
function deriveAesKey(
|
||||
passphrase: string,
|
||||
salt: Uint8Array,
|
||||
algo: Pbkdf2Algorithm = "SHA-384",
|
||||
iterations = 310_000
|
||||
): Uint8Array {
|
||||
return pbkdf2(algo, new TextEncoder().encode(passphrase), {
|
||||
salt,
|
||||
iterations,
|
||||
length: 48,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `pbkdf2(algo: Pbkdf2Algorithm, password: Uint8Array, options: Pbkdf2Options): Uint8Array`
|
||||
|
||||
Derives key material from a password.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `Pbkdf2Algorithm` | Hash algorithm for the internal HMAC-PRF. |
|
||||
| `password` | `Uint8Array` | Password bytes. |
|
||||
| `options.salt` | `Uint8Array` | Random salt (≥16 bytes recommended by SP 800-132). |
|
||||
| `options.iterations` | `number` | Iteration count (OWASP: ≥600 000 for SHA-256). |
|
||||
| `options.length` | `number` | Derived key length in bytes. |
|
||||
|
||||
**Returns:** `Uint8Array` of `options.length` bytes.
|
||||
|
||||
### `Pbkdf2Algorithm`
|
||||
|
||||
```ts
|
||||
type Pbkdf2Algorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `Pbkdf2Options`
|
||||
|
||||
```ts
|
||||
interface Pbkdf2Options {
|
||||
readonly salt: Uint8Array;
|
||||
readonly iterations: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Noble's `pbkdf2(H, password, salt, { c, dkLen })` maps directly to RFC 8018 PBKDF2. The `iterations` field in `Pbkdf2Options` maps to Noble's `c` (count) parameter, and `length` maps to `dkLen`. Parameter validation (iteration floor, length bounds) is the responsibility of the public KDF façade; this file passes values through without checking. The `hasherFor` helper uses the same two-`if` pattern as the HMAC and HKDF implementations for TypeScript exhaustiveness. NIST SP 800-132 and RFC 6070 test vectors pass in the test suite.
|
||||
@@ -0,0 +1,91 @@
|
||||
# pbkdf2.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`pbkdf2.select.ts` is the build-time primitive selector for the PBKDF2 implementation in `@suaveplan/crypto-hash`. It re-exports exactly one concrete backend — either `pbkdf2.noble.ts` or `pbkdf2.native.ts` — based on the `pbkdf2` entry in `crypto.config.ts`.
|
||||
|
||||
PBKDF2 (Password-Based Key Derivation Function 2, RFC 8018) is a widely supported standard for key derivation from passwords. While slower than Argon2id for pure password hashing, it is available natively in WebCrypto and is commonly required by interoperability profiles (PKCS#5, FIPS 140). The selector exposes `pbkdf2`, `Pbkdf2Algorithm`, and `Pbkdf2Options` through a stable interface.
|
||||
|
||||
## Features
|
||||
|
||||
- Re-exports `pbkdf2(algo, password, options)`, `Pbkdf2Algorithm`, and `Pbkdf2Options`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `pbkdf2` to `'noble'` or `'native'`.
|
||||
- Supports SHA-256, SHA-384, and SHA-512 as the underlying PRF.
|
||||
- Parameter validation (OWASP iteration minimums) is enforced at the public façade, not here.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { pbkdf2 } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
const password = new TextEncoder().encode("hunter2");
|
||||
const salt = crypto.getRandomValues(new Uint8Array(32));
|
||||
const key = pbkdf2("SHA-256", password, {
|
||||
salt,
|
||||
iterations: 600_000,
|
||||
length: 32,
|
||||
});
|
||||
console.log(key.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { pbkdf2, type Pbkdf2Algorithm } from "@suaveplan/crypto-hash/primitives/pbkdf2/pbkdf2";
|
||||
|
||||
// Derive an AES-256 key from a passphrase with a stored salt.
|
||||
function deriveStorageKey(
|
||||
passphrase: string,
|
||||
storedSalt: Uint8Array,
|
||||
algo: Pbkdf2Algorithm = "SHA-256",
|
||||
iterations: number = 600_000
|
||||
): Uint8Array {
|
||||
return pbkdf2(algo, new TextEncoder().encode(passphrase), {
|
||||
salt: storedSalt,
|
||||
iterations,
|
||||
length: 32,
|
||||
});
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `pbkdf2(algo: Pbkdf2Algorithm, password: Uint8Array, options: Pbkdf2Options): Uint8Array`
|
||||
|
||||
Derives a key from a password using PBKDF2.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `algo` | `Pbkdf2Algorithm` | Hash algorithm for the internal HMAC-PRF. |
|
||||
| `password` | `Uint8Array` | Password bytes. |
|
||||
| `options.salt` | `Uint8Array` | Cryptographically random salt (≥16 bytes recommended). |
|
||||
| `options.iterations` | `number` | Iteration count; OWASP recommends ≥600 000 for SHA-256. |
|
||||
| `options.length` | `number` | Derived key length in bytes. |
|
||||
|
||||
**Returns:** Derived key of `options.length` bytes.
|
||||
|
||||
### `Pbkdf2Algorithm`
|
||||
|
||||
```ts
|
||||
type Pbkdf2Algorithm = "SHA-256" | "SHA-384" | "SHA-512";
|
||||
```
|
||||
|
||||
### `Pbkdf2Options`
|
||||
|
||||
```ts
|
||||
interface Pbkdf2Options {
|
||||
readonly salt: Uint8Array;
|
||||
readonly iterations: number;
|
||||
readonly length: number;
|
||||
}
|
||||
```
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
PBKDF2 applies HMAC repeatedly (`iterations` times) to produce the derived key material. The Noble implementation passes NIST SP 800-132 and RFC 6070 test vectors. The iteration count has significant impact on compute time: 600 000 SHA-256 iterations take roughly 200–500 ms on a commodity CPU. Callers that need stronger memory-hardness should use Argon2id instead. The primitive does not enforce a minimum iteration count — that check belongs at the public KDF façade layer. The native stub throws `PrimitiveNotImplemented("pbkdf2")`.
|
||||
@@ -0,0 +1,62 @@
|
||||
# scrypt.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`scrypt.noble.ts` is the noble-backed implementation of the scrypt KDF in `@suaveplan/crypto-hash`. It wraps `@noble/hashes/scrypt::scryptAsync` with parameter validation and the `HashError` substrate. Selector forwarding is done by the generated `scrypt.select.ts` file; consumers should never import from here directly.
|
||||
|
||||
The noble implementation is pure JavaScript, async-friendly, and has zero dependencies on Node.js APIs — making it safe in browsers, Cloudflare Workers, Deno, Bun, and Node. The async design lets the runtime yield on long-running parameter sets so the main thread does not block.
|
||||
|
||||
## Features
|
||||
|
||||
- Wraps `@noble/hashes/scrypt::scryptAsync` and exposes `scrypt(password, salt, options)`.
|
||||
- Validates `N` is a power of two ≥ 2; throws `HashError(SCRYPT_INVALID_N)` otherwise.
|
||||
- Exposes `ScryptOptions { N, r, p, dkLen }` as the canonical parameter bundle.
|
||||
- Pure async (`Promise<Uint8Array>`).
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { scrypt } from "@suaveplan/crypto-hash/primitives/scrypt/impl/scrypt.noble";
|
||||
|
||||
const out = await scrypt(
|
||||
new TextEncoder().encode("password"),
|
||||
new TextEncoder().encode("salt"),
|
||||
{ N: 16_384, r: 8, p: 1, dkLen: 32 }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { scrypt } from "@suaveplan/crypto-hash/primitives/scrypt/impl/scrypt.noble";
|
||||
|
||||
// Probe the parameter cost for adaptive tuning — measure how long
|
||||
// `(N=2^k, r=8, p=1, dkLen=32)` takes for k in 14..20 and pick the
|
||||
// largest k that completes under the configured target (e.g. 250ms).
|
||||
async function calibrate(targetMs: number): Promise<number> {
|
||||
for (let k = 14; k <= 20; k++) {
|
||||
const start = performance.now();
|
||||
await scrypt(
|
||||
new TextEncoder().encode("probe"),
|
||||
new TextEncoder().encode("probe"),
|
||||
{ N: 2 ** k, r: 8, p: 1, dkLen: 32 }
|
||||
);
|
||||
if (performance.now() - start > targetMs) return k - 1;
|
||||
}
|
||||
return 20;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `scrypt(password: Uint8Array, salt: Uint8Array, options: ScryptOptions): Promise<Uint8Array>` — RFC 7914 scrypt KDF.
|
||||
- `ScryptOptions { N: number; r: number; p: number; dkLen: number }` — `N` is the CPU/memory cost (must be a power of two ≥ 2), `r` is block size, `p` is parallelisation, `dkLen` is output length in bytes.
|
||||
|
||||
Errors: `HASH.SCRYPT_INVALID_N` for `N` that is not a power of two ≥ 2.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
`isPowerOfTwo(n)` uses the standard bitwise trick `n > 0 && (n & (n - 1)) === 0`. Validation runs before any work so a wrong `N` does not allocate the inner state buffer. The package keeps `r` and `p` unconstrained — RFC 7914 does not impose hard limits and tightening here would diverge from upstream interoperability. Callers with stricter policy needs (e.g. OWASP-recommended minimums) should layer that on top of the primitive, not inside it.
|
||||
@@ -0,0 +1,71 @@
|
||||
# scrypt
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
- [scrypt.noble](./impl/scrypt.noble/scrypt.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt](./scrypt.md) — <!-- begin:auto-docs --> _(no submodules)_- scrypt.noble — <!-- begin:auto-docs --> _(no submodu…
|
||||
- [scrypt.noble](./impl/scrypt.noble/scrypt.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt](./scrypt.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt.noble](./impl/scrypt.noble/scrypt.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt](./scrypt.md) — <!-- begin:auto-docs --> _(no submodules)_- scrypt.noble — <!-- begin:au…
|
||||
- [scrypt.noble](./impl/scrypt.noble/scrypt.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt](./scrypt.md) — <!-- begin:auto-docs --> _(no submodules)_- scrypt.noble — <!-- begin:auto-docs --> _(no submod…
|
||||
- [scrypt.noble](./impl/scrypt.noble/scrypt.noble.md) — <!-- begin:auto-docs --> _(no submodules)_- [scrypt](./scrypt.md) — <!-- begin:auto-docs --> _(no submodules)_
|
||||
## Purpose
|
||||
|
||||
`scrypt.ts` is the public entry point for the scrypt KDF (RFC 7914) primitive in `@suaveplan/crypto-hash`. scrypt is a memory-hard password-based key derivation function — its `N` parameter forces an attacker to allocate `N * r * 64` bytes of fast memory per guess, which makes brute-force attacks on hardware accelerators dramatically more expensive than they would be against PBKDF2 or even bcrypt. It is the right KDF when callers want stronger memory-hardness than PBKDF2 but cannot adopt Argon2id (e.g. cross-stack interop with older Bitcoin / Litecoin tooling, or compatibility with the standard library on platforms that ship scrypt natively).
|
||||
|
||||
The suaveplan implementation routes through the per-primitive selector layout (`scrypt.select.ts`) so the `noble` engine can be replaced by a `native` engine (libsodium-style) without changing any caller imports. The public surface is two symbols — `scrypt` and `ScryptOptions`.
|
||||
|
||||
## Features
|
||||
|
||||
- RFC 7914 compliance — passes the official `(P="password", S="NaCl", N=1024, r=8, p=16, dkLen=64)` known-answer test byte-for-byte.
|
||||
- Async API (`Promise<Uint8Array>`) so the noble implementation can yield to the event loop on long-running parameter sets without blocking the main thread.
|
||||
- Strict parameter validation: `N` must be a power of two ≥ 2; otherwise throws `HashError(HASH.SCRYPT_INVALID_N)` with the offending value in `context.N`.
|
||||
- Variable output length via `dkLen`. No upper bound is enforced beyond the underlying noble implementation's limits.
|
||||
- Caller-supplied salt — the primitive never generates randomness internally.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { scrypt } from "@suaveplan/crypto-hash/scrypt";
|
||||
|
||||
const salt = crypto.getRandomValues(new Uint8Array(16));
|
||||
const derived = await scrypt(
|
||||
new TextEncoder().encode("correct horse battery staple"),
|
||||
salt,
|
||||
{ N: 16_384, r: 8, p: 1, dkLen: 32 }
|
||||
);
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { scrypt } from "@suaveplan/crypto-hash/scrypt";
|
||||
|
||||
// Migrating a legacy verifier table: re-derive each row at the stored
|
||||
// `(N, r, p, salt)` tuple and compare against the stored hash.
|
||||
async function verifyLegacy(
|
||||
password: string,
|
||||
row: { salt: Uint8Array; N: number; r: number; p: number; dkLen: number; expected: Uint8Array }
|
||||
): Promise<boolean> {
|
||||
const candidate = await scrypt(
|
||||
new TextEncoder().encode(password),
|
||||
row.salt,
|
||||
{ N: row.N, r: row.r, p: row.p, dkLen: row.dkLen }
|
||||
);
|
||||
if (candidate.length !== row.expected.length) return false;
|
||||
let diff = 0;
|
||||
for (let i = 0; i < candidate.length; i++) {
|
||||
diff |= candidate[i]! ^ row.expected[i]!;
|
||||
}
|
||||
return diff === 0;
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
- `scrypt(password: Uint8Array, salt: Uint8Array, options: ScryptOptions): Promise<Uint8Array>` — derives `dkLen` bytes via RFC 7914 scrypt.
|
||||
- `ScryptOptions { N: number; r: number; p: number; dkLen: number }` — `N` must be a power of two ≥ 2; otherwise `HashError(HASH.SCRYPT_INVALID_N)` is thrown.
|
||||
|
||||
Errors: `HASH.SCRYPT_INVALID_N`.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The noble engine is async because scrypt's inner loop ("ROMix") is CPU-bound; making it async lets the runtime opportunistically yield. The suaveplan implementation does not bound `N` from above — callers may use `N=2^20` (~1 GiB peak memory at `r=8`) if they want to. Callers in performance-sensitive paths should pre-warm the work pool, since the first invocation per worker thread typically takes 2–3× longer than steady-state due to V8/JIT profiling. The selector pattern (`scrypt.select.ts`) is generated; do not edit it. To add a native engine, place it under `impl/scrypt.native/scrypt.native.ts` and rerun `bun scripts/gen-crypto-selects.ts`.
|
||||
@@ -0,0 +1,65 @@
|
||||
# sha256.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha256.native.ts` is the native-backend placeholder for the SHA-256 primitive in `@suaveplan/crypto-hash`. It satisfies the same interface as `sha256.noble.ts` — accepting a `Uint8Array` and returning a `Uint8Array` — but throws `PrimitiveNotImplemented("sha256")` unconditionally.
|
||||
|
||||
This stub exists to keep the two-implementation primitive selector layout complete so that `crypto.config.ts` can be switched to `'native'` once an in-house SHA-256 implementation is ready. The generator script `gen-crypto-selects.ts` can point the selector at this file as soon as the implementation is wired up and passes the KAT harness in `@suaveplan/crypto-utils`.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies the `sha256(data: Uint8Array): Uint8Array` interface required by the selector.
|
||||
- Throws `PrimitiveNotImplemented("sha256")` from `@suaveplan/crypto-utils/errors`.
|
||||
- Signals clearly that the native path is not yet active, preventing silent misuse.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// This file is only active when crypto.config.ts sets sha256 to 'native'.
|
||||
// Currently throws — the example shows what the call site looks like.
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
|
||||
try {
|
||||
const digest = sha256(new Uint8Array(32));
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: sha256
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// During Phase 2 development, replace the throw with a real implementation.
|
||||
// The signature must remain: (data: Uint8Array): Uint8Array
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
|
||||
// Wrap with a capability check to gracefully degrade when native is unavailable.
|
||||
function tryNativeSha256(data: Uint8Array): Uint8Array | null {
|
||||
try {
|
||||
return sha256(data);
|
||||
} catch {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha256(_data: Uint8Array): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_data` | `Uint8Array` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("sha256")` — always.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The `_data` parameter prefix signals to TypeScript and Biome that the argument is intentionally unused. The thrown `PrimitiveNotImplemented` error is defined in `@suaveplan/crypto-utils/errors` and extends `SuaveplanError` with a code that identifies which primitive is missing. When the native implementation is ready, this file's body is replaced with the real algorithm and the prefix removed from the parameter name; the selector file is then regenerated to point at this file. No other files change during that switch.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sha256.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha256.noble.ts` is the Noble-backed concrete implementation of the SHA-256 primitive in `@suaveplan/crypto-hash`. It wraps `sha256` from `@noble/hashes/sha2`, exposing the same one-shot signature (`Uint8Array → Uint8Array`) that the selector and public façade expect.
|
||||
|
||||
This file is one side of the two-implementation layout: `sha256.noble.ts` (production-ready, audited) and `sha256.native.ts` (placeholder stub). The selector file `sha256.select.ts` chooses between them at build time; callers never import this file directly.
|
||||
|
||||
## Features
|
||||
|
||||
- Delegates entirely to `@noble/hashes/sha2`: no custom SHA-2 arithmetic.
|
||||
- Returns a fresh 32-byte `Uint8Array` on every call.
|
||||
- Zero side effects: no global state, no lazy initialisation.
|
||||
- Tree-shakeable: when the selector chooses the native backend, this file is unreachable and eliminated by the bundler.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Import via the stable public entry point, not this impl file directly.
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
|
||||
const digest = sha256(new TextEncoder().encode("hello world"));
|
||||
console.log(digest.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
import { toHex } from "@suaveplan/crypto-hash/encoding";
|
||||
|
||||
// Hash-then-sign pattern: hash the message before passing to an ECDSA signer.
|
||||
function preHash(message: Uint8Array): string {
|
||||
return toHex(sha256(message));
|
||||
}
|
||||
|
||||
const hex = preHash(new TextEncoder().encode("sign me"));
|
||||
console.log(hex.length); // 64 hex chars
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha256(data: Uint8Array): Uint8Array`
|
||||
|
||||
Computes the SHA-256 digest of `data`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence. |
|
||||
|
||||
**Returns:** Fresh 32-byte `Uint8Array`.
|
||||
|
||||
**Throws:** Never — Noble's SHA-256 accepts any `Uint8Array` input without error.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The function is a single-expression wrapper: `return nobleSha256(data)`. Noble allocates a new output buffer internally on every call, so the returned array is always independent of the input. There is no incremental/streaming interface at this layer; callers that need to hash large inputs in chunks should concatenate first or use a higher-level streaming abstraction. This implementation has been audited by the Noble maintainers and passes NIST FIPS 180-4 / CAVP test vectors.
|
||||
@@ -0,0 +1,71 @@
|
||||
# sha256.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha256.select.ts` is the build-time primitive selector for the SHA-256 implementation. It is a generated file that re-exports exactly one concrete implementation — either `sha256.noble.ts` (the audited `@noble/hashes` backend) or `sha256.native.ts` (the in-house native backend) — based on the setting in `crypto.config.ts`.
|
||||
|
||||
Consumers of `@suaveplan/crypto-hash` import SHA-256 through this file (via the public entry point) and never reference a concrete implementation directly. Swapping backends requires only regenerating this file; no import changes propagate to callers.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-symbol re-export of `sha256(data: Uint8Array): Uint8Array`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts` at the package root: set `sha256` to `'noble'` or `'native'`.
|
||||
- Keeps the public API stable across backend switches.
|
||||
- Enables dead-code elimination: tree-shakers see a single concrete import, not a runtime branch.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Import via the public package entry point — never import the select file directly.
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
|
||||
const digest = sha256(new TextEncoder().encode("hello"));
|
||||
console.log(digest.length); // 32
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha256 } from "@suaveplan/crypto-hash/primitives/sha256/sha256";
|
||||
|
||||
// Hash a concatenation of chunks without an intermediate allocation.
|
||||
function hashChunks(chunks: Uint8Array[]): Uint8Array {
|
||||
let totalLength = 0;
|
||||
for (const c of chunks) totalLength += c.length;
|
||||
const combined = new Uint8Array(totalLength);
|
||||
let offset = 0;
|
||||
for (const c of chunks) {
|
||||
combined.set(c, offset);
|
||||
offset += c.length;
|
||||
}
|
||||
return sha256(combined);
|
||||
}
|
||||
|
||||
const digest = hashChunks([
|
||||
new Uint8Array([0x01, 0x02]),
|
||||
new Uint8Array([0x03, 0x04]),
|
||||
]);
|
||||
console.log(digest.length); // 32
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `sha256(data: Uint8Array): Uint8Array`
|
||||
|
||||
One-shot SHA-256 digest.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence to hash. |
|
||||
|
||||
**Returns:** Fresh 32-byte `Uint8Array` containing the SHA-256 digest.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
This file is marked `/* GENERATED — DO NOT EDIT */`. The generator script reads `crypto.config.ts`, resolves the implementation path for `sha256`, and writes a single `export * from` line. At present the default configuration selects the Noble backend because the native implementation is a placeholder that throws `PrimitiveNotImplemented`. The native backend will be activated once the in-house SHA-256 implementation passes the known-answer-test (KAT) harness in `@suaveplan/crypto-utils`.
|
||||
@@ -0,0 +1,64 @@
|
||||
# sha384.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha384.native.ts` is the native-backend placeholder for the SHA-384 primitive in `@suaveplan/crypto-hash`. It exposes the same `sha384(data: Uint8Array): Uint8Array` signature as the Noble implementation but throws `PrimitiveNotImplemented("sha384")` unconditionally.
|
||||
|
||||
The file exists to keep the two-implementation primitive selector layout structurally complete for all primitives in the package. Having both `sha384.noble.ts` and `sha384.native.ts` present means the generator script can switch backends without any missing-file errors, and CI can validate the native stub's presence as part of the layout test.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies `sha384(data: Uint8Array): Uint8Array` interface for the selector.
|
||||
- Throws `PrimitiveNotImplemented("sha384")` unconditionally.
|
||||
- Signals clearly that this path is not yet active.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets sha384 to 'native'.
|
||||
// Currently throws unconditionally.
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
|
||||
try {
|
||||
sha384(new Uint8Array(32));
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: sha384
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// Pattern for graceful capability detection during native migration.
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
|
||||
function isSha384Available(): boolean {
|
||||
try {
|
||||
sha384(new Uint8Array(1));
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha384(_data: Uint8Array): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_data` | `Uint8Array` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("sha384")` — always.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
Like all native stubs in the package, the underscore prefix on `_data` suppresses unused-parameter warnings from TypeScript and Biome without requiring a lint-disable comment. When the in-house SHA-384 implementation is complete and has passed the NIST CAVP test suite (via the KAT harness in `@suaveplan/crypto-utils`), this file is updated with the real implementation and the selector is regenerated to point here.
|
||||
@@ -0,0 +1,59 @@
|
||||
# sha384.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha384.noble.ts` is the Noble-backed concrete implementation of the SHA-384 primitive in `@suaveplan/crypto-hash`. It delegates to `sha384` from `@noble/hashes/sha2`, exposing a minimal one-shot interface that the selector and public façade expect.
|
||||
|
||||
SHA-384 is the FIPS 180-4 truncation of SHA-512 to 48 bytes. It is preferred in high-throughput 64-bit environments over SHA-256 because SHA-512's 64-bit word arithmetic is natively fast on modern hardware, and the truncation gives a slight output-size reduction. This file is the production-quality side of the two-implementation layout.
|
||||
|
||||
## Features
|
||||
|
||||
- Delegates entirely to `@noble/hashes/sha2` SHA-384 implementation.
|
||||
- Returns a fresh 48-byte `Uint8Array` on every call.
|
||||
- Zero side effects; no global state or lazy initialisation.
|
||||
- Tree-shakeable: unreachable when the native backend is selected.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
|
||||
const digest = sha384(new TextEncoder().encode("hello"));
|
||||
console.log(digest.length); // 48
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
import { toHex } from "@suaveplan/crypto-hash/encoding";
|
||||
|
||||
// Compute a SHA-384 content hash for cache busting.
|
||||
function contentHash(payload: Uint8Array): string {
|
||||
return `sha384-${toHex(sha384(payload))}`;
|
||||
}
|
||||
|
||||
const tag = contentHash(new TextEncoder().encode("bundle contents"));
|
||||
console.log(tag.startsWith("sha384-")); // true
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha384(data: Uint8Array): Uint8Array`
|
||||
|
||||
Computes the SHA-384 digest of `data`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence. |
|
||||
|
||||
**Returns:** Fresh 48-byte `Uint8Array`.
|
||||
|
||||
**Throws:** Never.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The function body is `return nobleSha384(data)` — a single pass-through with no added logic. Noble allocates the output buffer internally. SHA-384 shares the SHA-512 compression function and differs only in the initial hash values and the truncation of the final state to 384 bits. The Noble implementation passes NIST CAVP vectors for SHA-384. No streaming interface is exposed at this layer.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sha384.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha384.select.ts` is the build-time primitive selector for the SHA-384 implementation. It is a generated file that re-exports exactly one concrete implementation — either `sha384.noble.ts` (the audited `@noble/hashes` backend) or `sha384.native.ts` (the in-house native backend) — based on the setting in `crypto.config.ts`.
|
||||
|
||||
Consumers import SHA-384 through the stable public entry point, which resolves here; they are insulated from backend changes. Regenerating this file via `bun scripts/gen-crypto-selects.ts` is the only step required to swap implementations.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-symbol re-export of `sha384(data: Uint8Array): Uint8Array`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `sha384` to `'noble'` or `'native'`.
|
||||
- Maintains a stable public API across backend switches.
|
||||
- Enables static dead-code elimination of the unused backend.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
|
||||
const digest = sha384(new TextEncoder().encode("hello"));
|
||||
console.log(digest.length); // 48
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha384 } from "@suaveplan/crypto-hash/primitives/sha384/sha384";
|
||||
|
||||
// Compute a commitment to a serialised record.
|
||||
function commit(record: Record<string, string>): Uint8Array {
|
||||
const encoded = new TextEncoder().encode(JSON.stringify(record));
|
||||
return sha384(encoded);
|
||||
}
|
||||
|
||||
const hash = commit({ userId: "u_123", action: "login" });
|
||||
console.log(hash.length); // 48
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `sha384(data: Uint8Array): Uint8Array`
|
||||
|
||||
One-shot SHA-384 digest.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence to hash. |
|
||||
|
||||
**Returns:** Fresh 48-byte `Uint8Array` containing the SHA-384 digest.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
SHA-384 is a truncation of SHA-512 that shares the same 64-bit word operations and block size. It is preferred over SHA-256 in environments that process large amounts of data on 64-bit processors because the 64-bit arithmetic is natively fast, and the truncation provides a modest reduction in output size relative to SHA-512. The Noble backend delegates to `@noble/hashes/sha2` without any added overhead. The native stub throws `PrimitiveNotImplemented("sha384")` until the in-house implementation is ready and has passed the NIST CAVP test vectors.
|
||||
@@ -0,0 +1,68 @@
|
||||
# sha512.native
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha512.native.ts` is the native-backend placeholder for the SHA-512 primitive in `@suaveplan/crypto-hash`. It satisfies the `sha512(data: Uint8Array): Uint8Array` interface but throws `PrimitiveNotImplemented("sha512")` unconditionally until an in-house implementation is ready.
|
||||
|
||||
This file pairs with `sha512.noble.ts` to complete the two-implementation selector layout. Its presence ensures the build system, generator script, and CI layout tests can treat SHA-512 consistently with every other primitive in the package.
|
||||
|
||||
## Features
|
||||
|
||||
- Satisfies the `sha512(data: Uint8Array): Uint8Array` interface.
|
||||
- Throws `PrimitiveNotImplemented("sha512")` unconditionally.
|
||||
- Provides a clear, typed error that identifies the missing primitive by name.
|
||||
- Zero runtime dependencies beyond `@suaveplan/crypto-utils/errors`.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
// Active only when crypto.config.ts sets sha512 to 'native'.
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
|
||||
try {
|
||||
sha512(new Uint8Array(64));
|
||||
} catch (err) {
|
||||
// PrimitiveNotImplemented: sha512
|
||||
console.error(err);
|
||||
}
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
// Feature detection for environments that may or may not have native SHA-512.
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
|
||||
async function withFallback(data: Uint8Array): Promise<Uint8Array> {
|
||||
try {
|
||||
return sha512(data);
|
||||
} catch {
|
||||
// Fall back to WebCrypto if native primitive is unavailable.
|
||||
const buffer = await crypto.subtle.digest("SHA-512", data);
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha512(_data: Uint8Array): Uint8Array`
|
||||
|
||||
Placeholder that always throws.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `_data` | `Uint8Array` | Ignored. |
|
||||
|
||||
**Throws:** `PrimitiveNotImplemented("sha512")` — always.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The underscore prefix on `_data` marks the parameter as intentionally unused without a lint suppression comment. When the in-house SHA-512 implementation is complete, this file is updated with the real algorithm, the underscore removed, and the selector regenerated. The `PrimitiveNotImplemented` error class in `@suaveplan/crypto-utils/errors` extends `SuaveplanError`, so it participates in the full error serialisation and cause-chain system.
|
||||
|
||||
### Status
|
||||
|
||||
Placeholder native stub — throws `PrimitiveNotImplemented` until a `node:crypto` / WebCrypto `SHA-512` bridge is wired up. Today the `sha512.select` selector always routes through Noble; the SHA-512 KAT harness in `@suaveplan/crypto-utils` pins the output against NIST FIPS 180-4 test vectors so the production path is fully covered.
|
||||
@@ -0,0 +1,62 @@
|
||||
# sha512.noble
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha512.noble.ts` is the Noble-backed concrete implementation of the SHA-512 primitive in `@suaveplan/crypto-hash`. It wraps `sha512` from `@noble/hashes/sha2`, producing 64-byte digests with the same one-shot interface expected by the selector and public façade.
|
||||
|
||||
SHA-512 is the widest SHA-2 variant and is used as the underlying hash in HMAC-SHA-512, HKDF-SHA-512, and PBKDF2-SHA-512. Its 64-bit word operations run natively fast on 64-bit processors and provide the largest security margin in the SHA-2 family. This file is the production-ready side of the two-implementation layout.
|
||||
|
||||
## Features
|
||||
|
||||
- Delegates entirely to `@noble/hashes/sha2` SHA-512 implementation.
|
||||
- Returns a fresh 64-byte `Uint8Array` on every call.
|
||||
- Zero side effects; no global state or lazy initialisation.
|
||||
- Tree-shakeable: unreachable when the native backend is selected.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
|
||||
const digest = sha512(new TextEncoder().encode("hello"));
|
||||
console.log(digest.length); // 64
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
import { toHex } from "@suaveplan/crypto-hash/encoding";
|
||||
|
||||
// Produce a 512-bit content fingerprint for audit logging.
|
||||
function auditDigest(payload: Uint8Array, label: string): string {
|
||||
const combined = new Uint8Array(payload.length + label.length);
|
||||
combined.set(new TextEncoder().encode(label), 0);
|
||||
combined.set(payload, label.length);
|
||||
return toHex(sha512(combined));
|
||||
}
|
||||
|
||||
const hex = auditDigest(new Uint8Array([0x01]), "event:");
|
||||
console.log(hex.length); // 128 hex chars
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
### `sha512(data: Uint8Array): Uint8Array`
|
||||
|
||||
Computes the SHA-512 digest of `data`.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence. |
|
||||
|
||||
**Returns:** Fresh 64-byte `Uint8Array`.
|
||||
|
||||
**Throws:** Never.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
The function is `return nobleSha512(data)` — a direct pass-through. Noble handles buffer allocation internally; the returned array is never an alias of the input. SHA-512 applies 80 rounds of compression over a 1024-bit block, producing a 512-bit state, all in 64-bit arithmetic. The Noble implementation is audited and passes NIST FIPS 180-4 CAVP vectors. No streaming interface is provided at this layer.
|
||||
@@ -0,0 +1,60 @@
|
||||
# sha512.select
|
||||
|
||||
<!-- begin:auto-docs -->
|
||||
_(no submodules)_
|
||||
<!-- end:auto-docs -->
|
||||
## Purpose
|
||||
|
||||
`sha512.select.ts` is the build-time primitive selector for the SHA-512 implementation. It is a generated file that re-exports exactly one concrete implementation — either `sha512.noble.ts` (the audited `@noble/hashes` backend) or `sha512.native.ts` — based on the active entry in `crypto.config.ts`.
|
||||
|
||||
SHA-512 produces 64-byte digests and is the largest of the SHA-2 family. It is used internally by HMAC-SHA-512, HKDF-SHA-512, and PBKDF2-SHA-512 within this package, and is exposed as a standalone primitive for callers that need a wider security margin than SHA-256 provides.
|
||||
|
||||
## Features
|
||||
|
||||
- Single-symbol re-export of `sha512(data: Uint8Array): Uint8Array`.
|
||||
- Generated by `bun scripts/gen-crypto-selects.ts` — never hand-edited.
|
||||
- Controlled by `crypto.config.ts`: set `sha512` to `'noble'` or `'native'`.
|
||||
- Maintains a stable public API across backend switches.
|
||||
- Enables static dead-code elimination of the unused backend.
|
||||
|
||||
## Usage: Basic Example
|
||||
|
||||
```ts
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
|
||||
const digest = sha512(new TextEncoder().encode("hello"));
|
||||
console.log(digest.length); // 64
|
||||
```
|
||||
|
||||
## Usage: Advanced Example
|
||||
|
||||
```ts
|
||||
import { sha512 } from "@suaveplan/crypto-hash/primitives/sha512/sha512";
|
||||
import { toHex } from "@suaveplan/crypto-hash/encoding";
|
||||
|
||||
// Compute a hex-encoded SHA-512 fingerprint of a binary blob.
|
||||
function fingerprint(blob: Uint8Array): string {
|
||||
return `sha512:${toHex(sha512(blob))}`;
|
||||
}
|
||||
|
||||
const fp = fingerprint(new Uint8Array([0xde, 0xad, 0xbe, 0xef]));
|
||||
console.log(fp.startsWith("sha512:")); // true
|
||||
```
|
||||
|
||||
## API Reference
|
||||
|
||||
Re-exports the following from the active implementation:
|
||||
|
||||
### `sha512(data: Uint8Array): Uint8Array`
|
||||
|
||||
One-shot SHA-512 digest.
|
||||
|
||||
| Parameter | Type | Description |
|
||||
|-----------|------|-------------|
|
||||
| `data` | `Uint8Array` | Arbitrary byte sequence to hash. |
|
||||
|
||||
**Returns:** Fresh 64-byte `Uint8Array` containing the SHA-512 digest.
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
SHA-512 uses 80 rounds of 64-bit operations on a 1024-bit block, giving it an internal state twice the width of SHA-256. On 64-bit hardware this is as fast or faster than SHA-256 for large inputs. The Noble backend passes NIST CAVP and RFC 6234 test vectors without modification. The native stub throws `PrimitiveNotImplemented("sha512")` and will be activated once the in-house implementation lands with full KAT coverage. The selector file itself is a generated artefact; the generator is idempotent and safe to re-run.
|
||||
Reference in New Issue
Block a user