first commit

This commit is contained in:
2026-05-29 08:20:56 +01:00
commit 949299f58d
6368 changed files with 527176 additions and 0 deletions

View File

@@ -0,0 +1,112 @@
# `keygen/generate`
## Purpose
RSA key-pair generation per NIST FIPS 186-5 Appendix A.1 and
SP 800-56B Rev. 2 §6.3. Two-prime by default; multi-prime
(`primes ∈ {3, 4, 5}`) when callers explicitly opt in.
The algorithm samples each prime as a random odd `bits`-wide
integer with the top two bits forced to `1` (so the product
always reaches the target modulus length), checks coprimality
with `e`, runs the Miller-Rabin probable-prime test from the
sibling module, and assembles a canonical `RsaPrivateKey` with
all CRT fields populated.
## Features
- `generateRsaKeyPair(options)` returns a fully-formed
`RsaPrivateKey` ready for any encrypt / decrypt / sign / verify
surface in the package.
- `AbortSignal` plumbed through the prime-search loop — yields
via `await Promise.resolve()` every 64 candidates so an
aborted signal scheduled via `queueMicrotask` lands within
≤ 100 ms.
- Deterministic mode — identical seeded `Rng` produces
byte-identical keys. Used by tests and by reproducible-build
pipelines.
- Miller-Rabin exhaustion guard — caps each prime search at
100 000 candidates and surfaces `CRYPTO_RSA.KEYGEN_FAILED` on
hitting the cap (vanishingly rare in practice).
- Multi-prime delegation — `primes !== 2` routes to the sibling
`generateMultiPrimeKey` for the RFC 8017 §3 multi-prime path.
- `crypto_rsa.keygen_duration_ms{modulus_bits,primes}` histogram.
## Usage: Basic Example
```ts
import { generateRsaKeyPair } from "@suaveplan/crypto-rsa/keygen/generate";
const key = await generateRsaKeyPair({ modulusBits: 2048 });
// key has n, e, d, p, q, dp, dq, qInv all populated
```
## Usage: Advanced Example
Cancel a slow generation on a timeout via `AbortSignal`:
```ts
import { generateRsaKeyPair } from "@suaveplan/crypto-rsa/keygen/generate";
const ctrl = new AbortController();
setTimeout(() => ctrl.abort(), 5000);
try {
const key = await generateRsaKeyPair({
modulusBits: 4096,
signal: ctrl.signal,
});
} catch (err) {
if (err.code === "CRYPTO_RSA.ABORTED") {
// surfaced from the next yield point in the prime search
}
}
```
Deterministic seeded generation for tests:
```ts
const seedRng = makeSeededRng(0xdeadbeef);
const k1 = await generateRsaKeyPair({
modulusBits: 1024,
rng: seedRng(),
});
const k2 = await generateRsaKeyPair({
modulusBits: 1024,
rng: seedRng(), // fresh from same seed
});
// k1 and k2 byte-identical
```
## API Reference
### `generateRsaKeyPair(options?): Promise<RsaPrivateKey>`
- `options.modulusBits?: number` — defaults to 3072.
- `options.publicExponent?: bigint` — defaults to `65537n`.
- `options.primes?: number` — defaults to 2; accepts 3–5.
- `options.rng?: Rng` — defaults to `defaultRng()`.
- `options.signal?: AbortSignal` — optional cancellation.
### `samplePrime(bits, e, rng, signal): Promise<bigint>`
Building block exposed for tests and for the multi-prime path.
Returns a probable prime with the top two bits set and coprime
to `e`. Throws `CRYPTO_RSA.KEYGEN_FAILED` after 100 000
candidates without success.
## Implementation Notes
- The top-two-bits-set rule guarantees that any two `bits`-wide
primes `p, q` produce a `p * q` of exactly `2 * bits` bits.
The post-multiplication length check is a paranoia guard that
fails loudly on a pathological RNG draw.
- The `bits` split on odd `modulusBits` is `(modulusBits + 1)/2`
for `p` and `modulusBits/2` for `q` so the top prime carries
the extra bit.
- `λ(n) = lcm(p-1, q-1)` per RFC 8017's modern derivation; this
produces a strictly smaller `d` than `φ(n) = (p-1)(q-1)`
while remaining cryptographically equivalent.
- `await Promise.resolve()` between candidate batches is the
cheapest yield point in V8 — it lets the microtask queue drain
(so a `queueMicrotask`-scheduled `signal.abort()` lands)
without paying for `setTimeout(0)`.