Skip to content

Conversation

@XoifaiI
Copy link

@XoifaiI XoifaiI commented Nov 23, 2025

Test case 101 in x25519_test.json uses a public key that lies on the twist of Curve25519, not on the main curve. This PR changes the result from "valid" to "acceptable" and adds the "Twist" flag so it matches with the other twist point tests.

The public key in 101 is:

e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413

For a point to be on Curve25519, the value x^3 + 486662x^2 + x must be a quadratic residue (have a square root in the field). Computing the Legendre symbol:
x^3 + 486662x^2 + x = 2b5a0cb55d322bba66520d668373c2f655f4103ff12bfedf527d57b26d5fb505
Legendre symbol = -1 (quadratic non-residue)

This confirms the point is on the twist, not the main curve.

Implementations that reject twist points are making a valid security decision. Masked scalar multiplication implementations (used for side channel protection) may not be able to process twist points. While RFC 7748 recommends accepting all inputs (Section 7 notes rejecting twist points is 'not recommended'), Wycheproof's purpose is really for testing cryptographic correctness and security. Rejecting twist points is stricter than the RFC requires but arguably more secure against invalid curve attacks.

If it helps

-- Using https://github.com/daily3014/rbx-cryptography/tree/main/src/Verification/EdDSA
local PublicKeyHex = "e5210f12786811d3f4b7959d0538ae2c31dbe7106fc03c3efc4cd549c715a413"
local PublicKeyX = FieldPrime.Decode(FromHex(PublicKeyHex))

local X2 = FieldPrime.Square(PublicKeyX)
local X3 = FieldPrime.Mul(X2, PublicKeyX)
local AX2 = FieldPrime.KMul(X2, 486662)
local RHS = FieldPrime.Canonicalize(FieldPrime.Add(X3, FieldPrime.Add(AX2, PublicKeyX)))

print("Public key:", PublicKeyHex)
print("x^3 + 486662x^2 + x =", ToHex(FieldPrime.Encode(RHS)))

-- Compute Legendre symbol: RHS^((p-1)/2)
-- If result is 1, point is on curve. If result is -1 (p-1), point is on twist
local function NSquare(A, n)
   for i = 1, n do
       A = FieldPrime.Square(A)
   end
   return A
end

local R_2_254 = NSquare(RHS, 254)
local R8 = NSquare(RHS, 3)
local R2 = FieldPrime.Square(RHS)
local R10 = FieldPrime.Mul(R8, R2)
local R10Inv = FieldPrime.Invert(R10)
local Legendre = FieldPrime.Mul(R_2_254, R10Inv)

print("Legendre symbol:", ToHex(FieldPrime.Encode(FieldPrime.Canonicalize(Legendre))))
print("If 01000...00 = on curve (QR)")
print("If ecffff...7f = on twist (QNR)")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant