Initial Commit

This commit is contained in:
2026-05-27 23:47:53 +01:00
parent fbc70f96e6
commit 9b28db724f
18103 changed files with 1600554 additions and 0 deletions

View File

@@ -0,0 +1,89 @@
# 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.