3.3 KiB
3.3 KiB
verify/pkcs1v15
Purpose
RSASSA-PKCS1-v1_5 signature verification per RFC 8017 §8.2.2. Recovers
the encoded message from signature via s^e mod n, reconstructs the
expected EMSA-PKCS1-v1_5 encoding of message with the same hash, and
compares the two byte strings constant-time.
Features
- RFC 8017 §8.2.2 conformant
- Constant-time encoded-message compare via
constantTimeEqualfrom@suaveplan/crypto-utils - Returns
falseon rejection (no exception); throws only on structural malformation (signature length mismatch) - Accepts either an
RsaPublicKeyor anRsaPrivateKey(both carrynande) - Modulus-length policy gates run on every call
- Records
crypto_rsa.verify_duration_msandcrypto_rsa.outcome_total{operation,outcome}on every call
Usage: Basic Example
import { rsaVerifyPkcs1v15 } from "@suaveplan/crypto-rsa/verify/pkcs1v15";
import { rsaSignPkcs1v15 } from "@suaveplan/crypto-rsa/sign/pkcs1v15";
const message = new TextEncoder().encode("hello, signed world");
const signature = rsaSignPkcs1v15(privateKey, message, { hash: "SHA-256" });
const ok = rsaVerifyPkcs1v15(publicKey, message, signature, {
hash: "SHA-256",
});
console.log(ok); // true
Usage: Advanced Example
A bit-flipped signature, a tampered message, or a hash mismatch all
return false without revealing which one tripped the gate.
import { rsaVerifyPkcs1v15 } from "@suaveplan/crypto-rsa/verify/pkcs1v15";
const tampered = new Uint8Array(signature);
tampered[0] = tampered[0] ^ 0x01;
console.log(
rsaVerifyPkcs1v15(publicKey, message, tampered, { hash: "SHA-256" })
); // false
console.log(
rsaVerifyPkcs1v15(publicKey, message, signature, { hash: "SHA-512" })
); // false (hash mismatch)
API Reference
rsaVerifyPkcs1v15(key, message, signature, options): boolean
key: RsaPublicKey | RsaPrivateKeymessage: Uint8Array— the bytes that were signedsignature: Uint8Array— must be exactlybyteLength(n)longoptions.hash: RsaHashAlgorithm— must match the hash used at sign timeoptions.allowLegacy?: booleanoptions.allowOversize?: boolean- returns
truewhen the signature is valid,falseon any verify-level rejection - throws
CryptoRsaError(CRYPTO_RSA.INVALID_SIGNATURE)on signature length mismatch - throws
CryptoRsaError(CRYPTO_RSA.MODULUS_TOO_SHORT)/MODULUS_TOO_LARGE/UNSUPPORTED_HASH
Implementation Notes
- The signature integer
s = os2ip(signature)is checked againsts < nbefore the modular exponentiation; signatures withs >= nare rejected withfalserather than allowed to wrap around. - Comparison uses
constantTimeEqual(recovered, expected)— never===and never byte-by-byte short-circuit. This is the verify-side defence against the timing-oracle attacks described in Bleichenbacher 2006 (the SHA-1 padding-oracle exploit) and follow-ups. - The encoded message is reconstructed every call rather than carried
alongside the signature. This is the RFC 8017 §8.2.2 design — the
verifier MUST regenerate
EMfrom the public inputs to detect signature substitution. - The
verify_duration_mshistogram is recorded on every code path (success, rejection, structural error) so service-side dashboards see the full latency distribution including the failure modes.