Hi @alexmgr,
I'm reporting two critical security vulnerabilities in tinyec v0.4.0 that enable private key recovery.
Finding 1: Insecure Random Number Generator (CRITICAL)
make_keypair() uses random.randint() which is backed by Mersenne Twister — a PRNG that is not cryptographically secure. After observing 624 consecutive 32-bit outputs, an attacker can reconstruct the internal state and predict all generated private keys.
# Current (INSECURE)
import random
priv = random.randint(1, curve.field.n)
# Fix
import secrets
priv = secrets.randbelow(curve.field.n - 1) + 1
Finding 2: Missing Point Validation in ECDH (CRITICAL)
ECDH.get_secret() does not validate that the peer's public key lies on the expected curve. This enables the classic invalid curve attack — an attacker sends points on weak curves with small-order subgroups, then recovers the victim's full private key via CRT after ~20 queries.
# PoC — off-curve point accepted without error
fake_pub = Point(curve, 4, 2)
print(fake_pub.on_curve) # False
ecdh = ECDH(victim_keypair)
secret = ecdh.get_secret(Keypair(curve, pub=fake_pub)) # Works!
Fix: Add if not keypair.pub.on_curve: raise ValueError(...) at the start of get_secret().
Additional Findings
- Timing side-channel (HIGH): Double-and-add scalar multiplication leaks key bits via timing
- Incorrect
Inf.__sub__ (MEDIUM): O - P returns P instead of -P
Disclosure Timeline
- 2026-03-07: Initial report (this issue)
- 2026-06-05: Public disclosure (90 days)
I'm happy to submit a PR with fixes if you'd like. CVE requests have been filed with MITRE for findings 1 and 2.
Best regards,
Connor (conner.webber000@gmail.com)
Hi @alexmgr,
I'm reporting two critical security vulnerabilities in tinyec v0.4.0 that enable private key recovery.
Finding 1: Insecure Random Number Generator (CRITICAL)
make_keypair()usesrandom.randint()which is backed by Mersenne Twister — a PRNG that is not cryptographically secure. After observing 624 consecutive 32-bit outputs, an attacker can reconstruct the internal state and predict all generated private keys.Finding 2: Missing Point Validation in ECDH (CRITICAL)
ECDH.get_secret()does not validate that the peer's public key lies on the expected curve. This enables the classic invalid curve attack — an attacker sends points on weak curves with small-order subgroups, then recovers the victim's full private key via CRT after ~20 queries.Fix: Add
if not keypair.pub.on_curve: raise ValueError(...)at the start ofget_secret().Additional Findings
Inf.__sub__(MEDIUM):O - PreturnsPinstead of-PDisclosure Timeline
I'm happy to submit a PR with fixes if you'd like. CVE requests have been filed with MITRE for findings 1 and 2.
Best regards,
Connor (conner.webber000@gmail.com)