# RSASSA-PSS algorithms (`PS256` / `PS384` / `PS512`) ## Purpose RFC 7518 §3.5 RSASSA-PSS signing and verification. The signing input is hashed, EMSA-PSS-encoded with a random salt, and signed via `@suaveplan/crypto-rsa.rsaSignPss`. The salt length is fixed at the hash output length per RFC 7518 §3.5 ("Salt Length shall be equal to the size of the hash output") — externally-produced signatures with non-matching salt lengths simply fail verify rather than being silently accepted, which catches producers that defaulted to a different PSS profile. ## Features - `PS256` → RSASSA-PSS + SHA-256 + 32-byte salt. - `PS384` → RSASSA-PSS + SHA-384 + 48-byte salt. - `PS512` → RSASSA-PSS + SHA-512 + 64-byte salt. - MGF1 hash matches the message hash (RFC 7518 §3.5 default). - Delegates to `@suaveplan/crypto-rsa.rsaSignPss` / `rsaVerifyPss`. ## Usage: Basic Example ```ts import { generateRsaKeyPair } from "@suaveplan/crypto-rsa"; import { compactSign, compactVerify } from "@suaveplan/jws"; const { publicKey, privateKey } = await generateRsaKeyPair({ modulusLength: 2048 }); const token = await compactSign("hello", privateKey, "PS256"); await compactVerify(token, publicKey, { algorithms: ["PS256"] }); ``` ## Usage: Advanced Example ```ts import { ps256 } from "@suaveplan/jws"; // Signature produced with a 16-byte salt instead of 32: const externalSig = produceFromBadProducer(message, priv, /*saltLen*/ 16); const ok = await ps256.verify(new TextEncoder().encode("hdr.payload"), externalSig, pub); // ok === false — salt length mismatch caught by EMSA-PSS-Verify. // Algorithm-confusion defence: passing an HMAC byte-key throws. import { JwsError } from "@suaveplan/jws"; try { await ps256.verify(input, sig, new Uint8Array(32)); } catch (err) { if (err instanceof JwsError && err.code === "JWS.KEY_ALG_MISMATCH") { // PSS verify expects an RsaPublicKey, not a raw byte buffer. } } ``` ## API Reference - `ps256: AlgorithmImpl` - `ps384: AlgorithmImpl` - `ps512: AlgorithmImpl` `verify` returns `Promise`; `sign` returns `Promise` whose length matches the modulus (256 bytes for 2048-bit keys). ## Implementation Notes PSS salt is drawn by `@suaveplan/crypto-rsa.rsaSignPss` via either the caller's optional `Rng` injection or the default RNG (`crypto.getRandomValues`). This means PSS signatures are probabilistic — the same payload signed twice produces two different tokens; tests that need determinism inject a deterministic `Rng`. Verifying with a fixed salt length and rejecting mismatched-salt signatures is a deliberate departure from libraries that auto-detect salt length from the signature length: that auto-detection is the source of many JWT-PSS interop bugs because the auto-detect heuristic disagrees on edge-case modulus sizes.