The Root That Brought Its Own Generator — Java Bug Hunt
Modelled on "Curveball" (CVE-2020-0601, patched by Microsoft in January 2020 after a report from the NSA).
- Language: Java
- Layer: Backend
- Difficulty: Hard
- Concepts: Security, Validation
- Modelled on: Windows CryptoAPI · CVE-2020-0601
- Visible tests: the genuine root is trusted; a root's key with a substituted generator is not trusted
- Reward: 50 XP for a complete fix
Briefing
Modelled on "Curveball" (CVE-2020-0601, patched by Microsoft in January 2020 after a report from the NSA). Windows' CryptoAPI accepted elliptic-curve certificates that spell out their curve parameters explicitly. When checking whether a certificate was a trusted root, it matched on the public key but did not check that the curve parameters — in particular the generator point — were the standard ones. An attacker could keep a trusted root's public key, pick a different generator for which they knew the matching private key, and have their forged "root" trusted for TLS and code signing.
ChainValidator.java decides whether a certificate is one of the trusted roots. Curves can be named (resolved through Curves.named) or given explicitly.
Fix isTrustedRoot so a match requires the same public key on the same curve.
Bug report
BUG-CURVEBALL · Priority: Critical (signature spoofing) · Reported by: security
ChainValidator.isTrustedRoot(cert, store) is true only when some root in store.roots has:
- the same public key point (qx, qy), AND
- identical domain parameters: p, a, b, gx, gy and n all equal
A key's parameters are key.explicit when present, otherwise Curves.named(key.curveName). A curve that resolves to nothing (unknown name, no name and no parameters) never matches. Explicitly spelled-out parameters equal to a named curve's values count as that curve.
Observed: a certificate carrying a trusted root's public key with its own explicit generator is accepted as that root.
Logs
[chain] leaf "update.example" -> root "Trusted Root CA" (explicit params, gx=555 gy=777)
[chain] root matched by public key -> TRUSTEDThe code as shipped
src/crypto/ChainValidator.java (editable)
class ChainValidator {
// A certificate is anchored when it is one of the trusted roots.
static boolean isTrustedRoot(Certificate cert, RootStore store) {
for (Certificate root : store.roots) {
if (root.key.qx == cert.key.qx && root.key.qy == cert.key.qy) {
return true;
}
}
return false;
}
}Read-only context: src/crypto/Certificate.java, src/crypto/EcParams.java.
Open the hunt to edit the files, run the visible tests and submit against the hidden ones. More Java bug hunts.