Deno: Miller-Rabin Primality Test Allows Zero Rounds
Date: 2026-06
Affected: rust/deno
Attack vector: other
Category: vulnerability-disclosure
Severity: High
CVE: CVE-2026-49440
CWE: CWE-325
CVSS: 7.4 (CVSS:3.1/AV:N/AC:H/PR:N/UI:N/S:U/C:H/I:H/A:N)

## Summary `node:crypto.checkPrime(candidate[, options][, callback])` and `crypto.checkPrimeSync(candidate[, options])` ran no Miller-Rabin rounds at all when the caller left `options.checks` at its default of `0`. In that mode, the only test applied to the candidate was trial division by the primes up to `17,863`. Any composite whose smallest prime factor exceeds that bound — for example the product of two primes just above it, such as `17,881 × 17,891` — was reported as `true` ("probably prime"). The same divergence affected the lower-level `op_node_check_prime` / `op_node_check_prime_bytes` paths that the polyfill calls into. Node.js itself does not have this problem: it forwards `checks = 0` to OpenSSL's `BN_check_prime`, which substitutes a sensible default number of rounds based on the candidate's bit length (per FIPS 186-4 Appendix C.3 Table C.1). Deno's Rust implementation had no equivalent fallback, so `count = 0` meant "skip the loop entirely." ## Affected APIs - `crypto.checkPrime(candidate)` (callback form, default options) - `crypto.checkPrime(candidate, { checks: 0 }, callback)` - `crypto.checkPrimeSync(candidate)` (default options) - `crypto.checkPrimeSync(candidate, { checks: 0 })` Callers who explicitly passed `checks >= 1` were less affected, the loop ran the number of rounds they asked for, but were still receiving fewer rounds than Node would have applied for the same bit length. With the patched version they get at least the FIPS minimum. ## Not af


Mitigations:

Architectural layers (MAESTRO):