Files
skills/docs/security-stack/crypto/crypto-rsa/src/math/crt.md
2026-05-29 08:20:56 +01:00

2.3 KiB
Raw Blame History

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

import { rsaPrivateOperationCrt } from "@suaveplan/crypto-rsa/math";

const m = rsaPrivateOperationCrt(c, privateKey);

Usage: Advanced Example

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.