3.2 KiB
3.2 KiB
encrypt/oaep
Purpose
RSAES-OAEP encryption per RFC 8017 §7.1.1. The Optimal Asymmetric Encryption Padding (OAEP) scheme is the modern default for RSA public-key encryption — provably secure under the random-oracle model when the underlying hash is collision-resistant, and immune to the chosen-ciphertext attacks (Bleichenbacher, Manger) that plague PKCS1-v1_5.
This module provides the encryption side; the matching decrypt
lives in decrypt/oaep and applies the Manger-safe combined
error path.
Features
- RFC 8017 §7.1.1 reference algorithm with caller-overridable
hash (
SHA-256default), MGF hash (defaults to the OAEP hash), and optional label (defaults to empty). - Routes hashing through
@suaveplan/crypto-hashfor SHA-256/384/512 and@noble/hashes/sha1for SHA-1; same dispatcher Stage 2'smgf1uses, so OAEP and MGF1 stay in lock-step. - Modulus-length policy gate runs first; sub-1024-bit keys rejected outright.
- Plaintext oversize →
CRYPTO_RSA.INVALID_KEYwith{ mLen, max }context for diagnosability. crypto_rsa.encrypt_duration_ms{scheme="oaep",hash}histogram.
Usage: Basic Example
import { rsaEncryptOaep } from "@suaveplan/crypto-rsa/encrypt/oaep";
const ciphertext = rsaEncryptOaep(
publicKey,
new TextEncoder().encode("hello"),
{ hash: "SHA-256" }
);
Usage: Advanced Example
OAEP with SHA-384 hashing and a non-empty label (mirrors a
common JWE RSA-OAEP-384 deployment with a context-bound
label):
import { rsaEncryptOaep } from "@suaveplan/crypto-rsa/encrypt/oaep";
const label = new TextEncoder().encode("session-2026-05-11");
const ct = rsaEncryptOaep(publicKey, plaintext, {
hash: "SHA-384",
mgfHash: "SHA-384",
label,
allowLegacy: false,
});
API Reference
rsaEncryptOaep(key, plaintext, options?): Uint8Array
key: RsaPublicKeyplaintext: Uint8Array— at mostk - 2*hLen - 2bytes.options.hash?: RsaHashAlgorithm— defaults to"SHA-256".options.mgfHash?: RsaHashAlgorithm— defaults tohash.options.label?: Uint8Array— defaults to empty.options.rng?: Rng— defaults todefaultRng().options.allowLegacy?: boolean— accept 1024-bit moduli.- Throws
CRYPTO_RSA.INVALID_KEYon plaintext oversize,CRYPTO_RSA.UNSUPPORTED_HASHon unknown algorithm,CRYPTO_RSA.MGF_FAILUREon absurd mask-length input, modulus-length policy violations on bad key.
Implementation Notes
- The seed is drawn from the injected
Rngvia the sharednextBytesViaRnghelper (same path as blinding) so seeded test RNGs work transparently. - We share the
mgf1anddigestWithimplementations from Stage 2'ssrc/encoding/mgf1.tsrather than duplicating — RFC 8017 §B.2.1 is short but having two copies invites drift. dbis allocated once and built in place; XOR with the mask produces a new buffer rather than mutating in place becausexorfrom@suaveplan/crypto-utilsreturns a fresh allocation (length-mismatch guard).- The OAEP encoding allocates
O(k)— for 4096-bit keys this is 512 bytes. Hot-loop callers (e.g. JWE bulk re-encryption) should prefer keeping a singleRsaPublicKeyand amortising the modpow cost over many ciphertexts.