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,68 @@
# `math/crt`
## Purpose
RSA private-key operation via the Chinese Remainder Theorem (RFC 8017 §5.1.2,
second-form algorithm). Computes `m = c^d mod n` using the CRT parameters
`p, q, dp, dq, qInv` already stored on the canonical `RsaPrivateKey` shape, so
the working modulus during exponentiation is half the size of `n`.
Empirically the CRT path is 3-4× faster than the naive `c^d mod n` route on
2048-bit keys; the speed-up grows quadratically with modulus size. Stage 1
ships the algorithm; Stages 2-3 wire it into `rsaSign*` and `rsaDecrypt*`
once MGF1 + EMSA encoding modules land.
## Features
- Pure-function implementation — no allocation beyond `bigint` intermediates.
- Reuses `modPow` so the banned-pattern grep gate stays clean.
- Negative-h fix-up (`h += p` when negative) is constant-form in TypeScript so
the JIT does not branch-predict away the canonicalisation step.
## Usage: Basic Example
```ts
import { rsaPrivateOperationCrt } from "@suaveplan/crypto-rsa/math";
const m = rsaPrivateOperationCrt(c, privateKey);
```
## Usage: Advanced Example
```ts
import {
i2osp,
os2ip,
rsaPrivateOperationCrt,
} from "@suaveplan/crypto-rsa/math";
function decryptCiphertextBytes(
ciphertext: Uint8Array,
key: RsaPrivateKey
): Uint8Array {
const c = os2ip(ciphertext);
const m = rsaPrivateOperationCrt(c, key);
return i2osp(m, ciphertext.length);
}
```
## API Reference
### `rsaPrivateOperationCrt(c: bigint, key: RsaPrivateKey): bigint`
Returns the message representative `m ∈ [0, n)` such that `m^e mod n === c`.
Requires `c < n` (caller's responsibility — the CRT formulation does not
verify the bound).
## Implementation Notes
The second-form algorithm (RFC 8017 §5.1.2) is preferred over the first-form
because it never materialises any value larger than `p` or `q` during the hot
loop, keeping memory pressure halved. The final reconstruction `m = m2 + h*q`
returns a value in `[0, n)` provided `c < n`; with `c >= n` the result is
mathematically valid but semantically meaningless because RSA encrypted/signed
representatives are always reduced mod `n`.
Negative `h` arises when `m1 < m2`. The `if (h < 0n) h += p` step canonicalises
`h` into `[0, p)` before the multiplication so the final `m2 + h*q` is
unambiguously non-negative.

View File

@@ -0,0 +1,60 @@
# `math/i2osp`
## Purpose
Integer-to-Octet-String Primitive (RFC 8017 §4.1). Encodes a non-negative integer
as a fixed-length big-endian byte string. Required by every RSA signature and
encryption scheme to convert the modular-exponentiation output into a byte
string of the modulus's byte length.
This module is a thin re-export of the canonical `i2osp` in `@suaveplan/crypto-utils`
so the entire project shares a single, audited definition. The re-export gives
RSA consumers a path-local import that mirrors the spec section number.
## Features
- Fixed-output-length big-endian byte encoding.
- Throws on negative input or insufficient output length.
- Accepts both `number` and `bigint` for source-side ergonomics.
## Usage: Basic Example
```ts
import { i2osp } from "@suaveplan/crypto-rsa/math";
const bytes = i2osp(255n, 4); // Uint8Array([0, 0, 0, 255])
```
## Usage: Advanced Example
```ts
import { i2osp, modPow } from "@suaveplan/crypto-rsa/math";
function publicOpAsBytes(
c: bigint,
e: bigint,
n: bigint,
modulusByteLength: number
): Uint8Array {
const result = modPow(c, e, n);
return i2osp(result, modulusByteLength);
}
```
## API Reference
### `i2osp(n: number | bigint, xLen: number): Uint8Array`
Returns a `Uint8Array` of length `xLen`. Throws
`CryptoUtilsError(CRYPTO.UTILS.INTEGER.OUT_OF_RANGE)` when `n` is negative or
does not fit in `xLen` bytes; throws
`CryptoUtilsError(CRYPTO.UTILS.BYTES.INVALID_LENGTH)` when `xLen` is negative
or not an integer.
## Implementation Notes
The canonical implementation lives in `@suaveplan/crypto-utils/bytes/i2osp/i2osp.ts`.
This re-export is preserved instead of importing the utility directly so that
RSA consumers reading the source see a path that maps 1:1 to RFC 8017 §4.1, and
so the RSA package can swap the implementation later (for instance to a
constant-time native variant) without churn at every call site.

View File

@@ -0,0 +1,60 @@
# `math/modpow`
## Purpose
Single-source-of-truth modular exponentiation for the entire `@suaveplan/crypto-rsa`
package. Wraps `@noble/curves/abstract/modular.pow` so every signing, verifying,
encrypting, decrypting, and CRT path goes through one function — and so the banned
native pattern `BigInt ** BigInt % N` never appears in package source.
The native `**` + `%` pattern materialises the full exponentiation result before
modular reduction. For RSA-grade exponents (2048+ bits) this allocates digits in
the gigabytes and is also non-constant-time. `@noble/curves/abstract/modular.pow`
implements the standard square-and-multiply ladder over the modular ring without
materialising the full power.
## Features
- One-line wrapper — no extra allocation, no extra branching.
- Pinned to `@noble/curves`'s audited `pow` implementation (the same primitive
that backs every elliptic curve in `@suaveplan/crypto-curve`).
- Repo-grep gate ensures no other file in the package emits the banned pattern.
## Usage: Basic Example
```ts
import { modPow } from "@suaveplan/crypto-rsa/math";
const result = modPow(2n, 10n, 1000n); // 24n
```
## Usage: Advanced Example
```ts
import { modPow } from "@suaveplan/crypto-rsa/math";
import type { RsaPublicKey } from "@suaveplan/crypto-rsa";
function rsaPublicOp(c: bigint, key: RsaPublicKey): bigint {
return modPow(c, key.e, key.n);
}
```
## API Reference
### `modPow(base: bigint, exponent: bigint, modulus: bigint): bigint`
Returns `base^exponent mod modulus` in `[0, modulus)`. Delegates entirely to
`@noble/curves/abstract/modular.pow`. The noble implementation throws
`RangeError` on `modulus <= 0n` or `exponent < 0n`.
## Implementation Notes
The function exists purely to centralise the import. Inlining would not save any
runtime cost (the wrapper is a single tail call) but would bypass the
single-import grep gate that the package relies on for the `BigInt ** %` audit.
The dependency on `@noble/curves` is justified because the package already
depends on it transitively for prime-related utilities and because the
`@suaveplan/crypto-curve` precedent established the engine selection. No
alternative pure-JS implementation in the project currently provides
audited modular exponentiation over `bigint`.

View File

@@ -0,0 +1,52 @@
# `math/os2ip`
## Purpose
Octet-String-to-Integer Primitive (RFC 8017 §4.2). Decodes a big-endian byte string
into a non-negative `bigint`. Used by every RSA scheme to convert a modulus-sized
byte payload back into the integer the modular operations expect.
Thin re-export of `@suaveplan/crypto-utils`'s canonical implementation so the
entire project agrees on encoding semantics.
## Features
- Always returns `bigint`, never `number`, so precision is preserved for any
byte length.
- Pure decoder; never throws.
## Usage: Basic Example
```ts
import { os2ip } from "@suaveplan/crypto-rsa/math";
const value = os2ip(new Uint8Array([0, 0, 0, 255])); // 255n
```
## Usage: Advanced Example
```ts
import { i2osp, modPow, os2ip } from "@suaveplan/crypto-rsa/math";
function privateOpRoundTrip(
sigBytes: Uint8Array,
e: bigint,
n: bigint,
modulusByteLength: number
): Uint8Array {
const sigInt = os2ip(sigBytes);
const recovered = modPow(sigInt, e, n);
return i2osp(recovered, modulusByteLength);
}
```
## API Reference
### `os2ip(bytes: Uint8Array): bigint`
Returns the big-endian-decoded `bigint`. Empty input returns `0n`.
## Implementation Notes
The canonical implementation lives in `@suaveplan/crypto-utils/bytes/os2ip/os2ip.ts`.
This re-export gives RSA consumers a path-local import mirroring RFC 8017 §4.2.