← Blog · 2025-10-08 · 8 min read
CVE-2025-8556 — Cryptographic Issues in Cloudflare’s CIRCL FourQ Implementation
In early 2025, while working on a project which required us to perform a broad audit of OSS elliptic-curve implementations, we discovered several cryptographic issues in Cloudflare’s CIRCL library — specifically with the implementation of the FourQ elliptic curve.
We reported the issues through Cloudflare’s HackerOne bug-bounty program in March 2025, and subsequently contacted Cloudflare directly, after receiving a lukewarm and laconic response from the HackerOne triage team.
Once the team at Cloudflare stepped in, the issues were appropriately acknowledged and fixed.
FourQ, Cloudflare’s CIRCL
CIRCL,
Cloudflare’s cryptography library, offers a basic implementation of the FourQ
curve, as well as a Diffie-Hellman implementation named Curve4Q that
provides shared-secret functionality.
FourQ is an elliptic curve with 128-bit security, developed by Microsoft Research and defined by a twisted Edwards curve equation.
The curve is defined over a two-dimensional extension of the prime field defined by the Mersenne prime , the curve twist parameter and set to a quadratic non-residue in .
Simply put — much as the complex numbers are an extension field of the real numbers, the FourQ curve is defined over an extension of a prime field, its elements being of the form where and are integers .
In addition, the FourQ curve defines two endomorphisms — structure-preserving functions — which serve as “shortcuts” to perform computations on the curve more efficiently.
These endomorphisms, along with the curve’s other characteristics, make it fast and suitable for use-cases where computational resources are scarce, such as embedded systems.
Invalid point attacks
A certain class of attacks on elliptic-curve implementations lets an attacker force the server to perform a calculation that discloses information about the secret key used.
This kind of attack is often called an invalid-curve or invalid-point attack, and stems from insufficient validation of the points used in the calculation.
Elliptic-Curve Diffie-Hellman (ECDH) involves each side taking a secret scalar and multiplying it by a fixed generator point to compute the point . Each side transmits its point and receives the other side’s, multiplying it by its own scalar. Since scalar multiplication on elliptic curves is commutative, both sides end up with the same point.
// Shared calculates a shared key k from Alice's secret and Bob's public key.
// Returns true on success.
func Shared(shared, secret, public *Key) bool {
var P, Q fourq.Point
ok := P.Unmarshal((*[Size]byte)(public))
Q.ScalarMult((*[Size]byte)(secret), &P)
Q.Marshal((*[Size]byte)(shared))
ok = ok && Q.IsOnCurve()
return ok
}
An example from CIRCL’s Curve4Q implementation
(pre-remediation) shows a shared secret being calculated by taking a public point
(P) and a secret scalar as input and multiplying them.
The issue arises when the computation is done without first verifying that the other side’s point is a valid point on the curve. To be secure, all points on an elliptic curve must be members of an -torsion subgroup, where is the order of the curve — the total number of points on it. Put more clearly: if we consider a point being added to itself a “step”, then every point on the curve should take the same number of steps to lead back to the identity point.
So if one multiplies a certain point by the secret scalar , and the point is valid and on the expected curve, the result should land on any of the points of the curve. For the curve to be considered secure, its order must be either prime or the product of a large prime and a small cofactor.
This is what makes the discrete-logarithm problem hard with respect to scalar multiplication, creating a “trap-door” function: easy to perform the multiplication, hard to reverse it.
If an attacker can force the server to perform the scalar multiplication of its secret with an invalid point that is not on the curve, they may choose so that it belongs to a curve with a smooth (many small factors) subgroup order .
As a result, instead of computing any possible point on the original curve, it lands on any of a much smaller set of points. If the subgroup order of is only 400 points, the attacker can trivially brute-force 400 values of to find the server’s secret modulo 400.
Repeated for multiple invalid points with different subgroup orders, and combined with the Chinese Remainder Theorem, the attacker eventually recovers the server’s full secret .
Degenerate-curve attacks on Edwards curves
The attack above applies to a form of elliptic curve called Weierstrass curves. While Edwards curves are birationally equivalent to Weierstrass curves — a curve such as FourQ can be represented with Weierstrass formulas — the invalid-curve attack as presented does not generalize to Edwards curves.
Weierstrass addition ():
Edwards addition:
The reason is that while addition with the Weierstrass formulas is independent of the curve parameters, the Edwards addition formulas depend on both parameters and , which makes it very difficult to pass arbitrary points — points not on the curve — and have the server add them correctly.
Looking closer at the Edwards addition formula, the curve parameters ( and ) are coefficients of the variable. So if we fix to 0, the curve parameters cancel out and we are left with a less generalized invalid-point attack that does work on all Edwards curves.
Concretely: if we pass in a point of the form , multiplying it by the secret value computes . So if we select a such that the point has a small multiplicative subgroup order and receive , solving the discrete-logarithm problem to recover becomes trivial.
CVE-2025-8556
Given the invalid-curve attacks above, the main adversarial threat to an ECC implementation lies in performing computations on invalid points — points not on the graph. Points should always be validated before being relied upon for any computation.
At minimum, unmarshalling a point — loading a byte-array of the right length and converting it to a point on the curve — should ensure the loaded point is indeed valid, simply by checking that the curve equation holds.
For added security, the point should also be validated before use in any of the basic computations: addition, doubling, scalar multiplication.
Auditing CIRCL’s FourQ implementation, we pinpointed 7 issues related to these security primitives and to the testing code, which incorrectly demonstrated some security proofs. Below are the 4 major points we raised, which were addressed to some extent by the fixes to CIRCL.
Incorrect point validation in Point.Unmarshal
A missing step. The IETF spec for FourQ accounts for ambiguity in the unmarshalling process by conjugating the point’s value — if neither the unmarshalled point nor its conjugate is a valid point on the curve, the point is invalid. The IETF spec gives the following pseudocode:
if -x^2+y^2 != 1+d*x^2*y^2: # Check curve equation with x
x = conj(x)
if -x^2+y^2 != 1+d*x^2*y^2: # ... or its conjugate
return FAILED
return P = (x, y)
The CIRCL implementation fails to re-validate that the point is on the curve after conjugating its value:
if !P.IsOnCurve() {
fpNeg(&P.X[1], &P.X[1])
}
return true
Faulty point comparison in pointR1.isEqual
Per the IETF spec, the CIRCL code uses several representations of projected coordinates: in addition to the and values, each point also carries , and values, where .
The issue: if is set to 0 — invalid in the
projected representation — the isEqual check always returns true. Several
checks in the code were affected, including faulty tests.
Lack of point validation in pointR1.ClearCofactor
Since the FourQ curve has a cofactor of 392 — its order is not prime but a prime multiplied by 392 — to ensure the point used for computation is an -torsion point, the cofactor must be cleared by multiplying the point by 392 before any further scalar multiplications. If clearing the cofactor yields the neutral point, the input point is invalid.
The CIRCL implementation deviates from the spec by failing to perform this verification after clearing the cofactor.
Lack of point validation in pointR1.ScalarMult
The scalar-multiplication implementation on pointR1
assumes the projected values are valid and that the point is indeed on the curve. As a
result of the previous unmarshalling issue, it’s possible to load a point that
isn’t on the curve and then compute on it, exposing the implementation to the
degenerate-curve attacks described above.
Fixing the unmarshalling issue prevents this, as does the change to the
Curve4Q code that performs the DH computation. Still, to conform with more
stringent security measures, it would be advisable to validate that the input point is
on the curve before performing the scalar multiplication.