first commit
This commit is contained in:
@@ -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.
|
||||
Reference in New Issue
Block a user