# emsa-pss-verify ## Purpose Implements the EMSA-PSS verification operation defined in RFC 8017 §9.1.2. Given the message digest, the candidate encoded message `EM`, the encoded-message bit length, the expected salt length, and the hash + MGF1 hash pair, this function returns whether `EM` is a valid PSS encoding of the digest. ## Features - RFC 8017 §9.1.2 conformant — full structure, padding, and trailer checks - Returns `false` on every malformed input rather than throwing — the signature scheme decides how to escalate - Constant-time final compare via `constantTimeEqual` from `@suaveplan/crypto-utils` - Validates leftmost-bit clearance (RFC 8017 §9.1.2 step 6) - Validates the `0xbc` trailer - Validates the all-zero PS region and the `0x01` separator ## Usage: Basic Example ```ts import { emsaPssVerify } from "@suaveplan/crypto-rsa/encoding/emsa-pss-verify"; import { emsaPssEncode } from "@suaveplan/crypto-rsa/encoding/emsa-pss-encode"; import { sha256 } from "@suaveplan/crypto-hash"; const mHash = sha256(new TextEncoder().encode("hello")); const salt = new Uint8Array(32); const em = emsaPssEncode(mHash, 2047, salt, "SHA-256", "SHA-256"); console.log(emsaPssVerify(mHash, em, 2047, 32, "SHA-256", "SHA-256")); // true ``` ## Usage: Advanced Example A bit-flipped EM is rejected at one of several gates: the trailer check, the leftmost-bit check, the PS region check, the separator check, or the final salt+hash compare. All of these gates fail closed (return `false`) without revealing which gate caught the divergence. ```ts import { emsaPssVerify } from "@suaveplan/crypto-rsa/encoding/emsa-pss-verify"; import { emsaPssEncode } from "@suaveplan/crypto-rsa/encoding/emsa-pss-encode"; import { sha256 } from "@suaveplan/crypto-hash"; const mHash = sha256(new TextEncoder().encode("hello")); const em = emsaPssEncode( mHash, 2047, new Uint8Array(32), "SHA-256", "SHA-256" ); const tampered = new Uint8Array(em); tampered[0] = tampered[0] ^ 0x01; console.log(emsaPssVerify(mHash, tampered, 2047, 32, "SHA-256", "SHA-256")); // false ``` ## API Reference ### `emsaPssVerify(mHash, em, emBits, saltLength, hash, mgfHash): boolean` - `mHash: Uint8Array` — `Hash(message)`; length must equal `hashOutputLength(hash)` (otherwise returns `false`) - `em: Uint8Array` — candidate encoded message; length must equal `ceil(emBits / 8)` (otherwise returns `false`) - `emBits: number` — encoded-message bit length, typically `modulusBits - 1` - `saltLength: number` — expected salt length, must match the value used by `emsaPssEncode` - `hash: RsaHashAlgorithm` — message hash; also used to digest `M'` - `mgfHash: RsaHashAlgorithm` — MGF1 hash for `dbMask` - **returns** `true` when `EM` is a valid PSS encoding of `mHash` under the triple, `false` otherwise ## Implementation Notes - The function never throws on encoding-level failures — every malformed input returns `false`. This matches the RFC 8017 §9.1.2 contract: the caller is responsible for distinguishing between expected verify failures and protocol errors. - The final `H == H'` comparison is constant-time. The earlier structural checks (trailer byte, PS region, separator byte) short- circuit on first failure — that is acceptable because the public inputs determining those bytes are known to the verifier; only the hash output carries the secret-derived signal that timing attacks would target. - The `leftmostBitsToClear` check (RFC 8017 §9.1.2 step 6) prevents a trivial signature-malleability attack where an attacker flips bits in the cleared region without being detected. - Returning `false` on a length mismatch (`em.length !== emLen` or `mHash.length !== hLen`) preserves the same constant-effort failure shape as a structurally-valid mismatch.