first commit
This commit is contained in:
112
docs/security-stack/crypto/crypto-rsa/src/keygen/generate.md
Normal file
112
docs/security-stack/crypto/crypto-rsa/src/keygen/generate.md
Normal 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)`.
|
||||
Reference in New Issue
Block a user