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,57 @@
# 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<RsaPublicKey | RsaPrivateKey>`
- `ps384: AlgorithmImpl<RsaPublicKey | RsaPrivateKey>`
- `ps512: AlgorithmImpl<RsaPublicKey | RsaPrivateKey>`
`verify` returns `Promise<boolean>`; `sign` returns `Promise<Uint8Array>` 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.