3.7 KiB
3.7 KiB
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
falseon every malformed input rather than throwing — the signature scheme decides how to escalate - Constant-time final compare via
constantTimeEqualfrom@suaveplan/crypto-utils - Validates leftmost-bit clearance (RFC 8017 §9.1.2 step 6)
- Validates the
0xbctrailer - Validates the all-zero PS region and the
0x01separator
Usage: Basic Example
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.
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 equalhashOutputLength(hash)(otherwise returnsfalse)em: Uint8Array— candidate encoded message; length must equalceil(emBits / 8)(otherwise returnsfalse)emBits: number— encoded-message bit length, typicallymodulusBits - 1saltLength: number— expected salt length, must match the value used byemsaPssEncodehash: RsaHashAlgorithm— message hash; also used to digestM'mgfHash: RsaHashAlgorithm— MGF1 hash fordbMask- returns
truewhenEMis a valid PSS encoding ofmHashunder the triple,falseotherwise
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
leftmostBitsToClearcheck (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
falseon a length mismatch (em.length !== emLenormHash.length !== hLen) preserves the same constant-effort failure shape as a structurally-valid mismatch.