# `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` - `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` 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)`.