Fix exchange validation
Now that it accepts any (projective) base as input, we need to check the projective equation, rather than the affine one, which assumes Z = 1.
This commit is contained in:
parent
73e0df0b5e
commit
dcd7f93a3c
|
@ -62,37 +62,43 @@ local function exchangeOnPoint(sk, P)
|
||||||
local xP = c25.dadd(dP, rP, xrP)
|
local xP = c25.dadd(dP, rP, xrP)
|
||||||
|
|
||||||
-- Extract coordinates for scaling.
|
-- Extract coordinates for scaling.
|
||||||
|
local Px, Pz = P[1], P[2]
|
||||||
local xPx, xPz = xP[1], xP[2]
|
local xPx, xPz = xP[1], xP[2]
|
||||||
local rPx, rPz = rP[1], rP[2]
|
local rPx, rPz = rP[1], rP[2]
|
||||||
|
|
||||||
|
-- Ensure all Z coordinates are squares.
|
||||||
|
Px, Pz = fp.mul(Px, Pz), fp.square(Pz)
|
||||||
|
xPx, xPz = fp.mul(xPx, xPz), fp.square(xPz)
|
||||||
|
rPx, rPz = fp.mul(rPx, rPz), fp.square(rPz)
|
||||||
|
|
||||||
-- We're splitting the secret x into (x - r (mod q), r). The multiplication
|
-- We're splitting the secret x into (x - r (mod q), r). The multiplication
|
||||||
-- adds them back together, but this only works if P's order is q, which is
|
-- adds them back together, but this only works if P's order is q, which is
|
||||||
-- not the case on the twist.
|
-- not the case on the twist.
|
||||||
-- As a result, we need to check if P is on the twist and return 0 so as to
|
-- As a result, we need to check if P is on the twist and return 0 so as to
|
||||||
-- not leak part of x. We do this by checking the curve equation against P.
|
-- not leak part of x. We do this by checking the curve equation against P.
|
||||||
-- The equation for Curve25519 is y² = x³ + 486662x² + x. Checking it
|
-- The projective equation for curve25519 is Y²Z = X(X² + AXZ + Z²). Since Z
|
||||||
-- amounts to verifying that x³ + 486662x² + x is a quadratic residue.
|
-- is a square, checking validity means checking the right-hand side to be a
|
||||||
local Px = P[1]
|
-- square.
|
||||||
local Px2 = fp.square(Px)
|
local Px2 = fp.square(Px)
|
||||||
local Px3 = fp.mul(Px2, Px)
|
local Pz2 = fp.square(Pz)
|
||||||
local APx2 = fp.kmul(Px2, 486662)
|
local Pxz = fp.mul(Px, Pz)
|
||||||
local Py2 = fp.carry(fp.add(fp.carry(fp.add(Px3, APx2)), Px))
|
local APxz = fp.kmul(Pxz, 486662)
|
||||||
|
local rhs = fp.mul(Px, fp.add(Px2, fp.carry(fp.add(APxz, Pz2))))
|
||||||
|
|
||||||
-- Square the Z coordinate on both products.
|
-- Find the square root of 1 / (rhs * xPz * rPz).
|
||||||
xPx, xPz = fp.mul(xPx, xPz), fp.square(xPz)
|
-- Neither rPz, xPz, nor rhs are 0:
|
||||||
rPx, rPz = fp.mul(rPx, rPz), fp.square(rPz)
|
-- - If rhs was 0, then P would be low order, which would return at (1).
|
||||||
|
|
||||||
-- Find the square root of 1 / (Py2 * xPz * rPz).
|
|
||||||
-- Neither rPz, xPz, nor Py2 are 0:
|
|
||||||
-- - If Py2 was 0, then P would be low order, which would return at (1).
|
|
||||||
-- - Since P isn't low order, clamping prevents the ladder from returning O.
|
-- - Since P isn't low order, clamping prevents the ladder from returning O.
|
||||||
-- Since we've just squared both xPz and rPz, the root will exist iff Py2 is
|
-- Since we've just squared both xPz and rPz, the root will exist iff rhs is
|
||||||
-- a quadratic residue. This checks the curve equation, so we're done.
|
-- a square. This checks the curve equation, so we're done.
|
||||||
local root = fp.sqrtDiv(fp.num(1), fp.mul(fp.mul(xPz, rPz), Py2))
|
local root = fp.sqrtDiv(fp.num(1), fp.mul(fp.mul(xPz, rPz), rhs))
|
||||||
if not root then return fp.encode(fp.num(0)) end
|
if not root then
|
||||||
|
local out = fp.encode(fp.num(0))
|
||||||
|
return out, out
|
||||||
|
end
|
||||||
|
|
||||||
-- Get the inverses of both Z values.
|
-- Get the inverses of both Z values.
|
||||||
local xPzrPzInv = fp.mul(fp.square(root), Py2)
|
local xPzrPzInv = fp.mul(fp.square(root), rhs)
|
||||||
local xPzInv = fp.mul(xPzrPzInv, rPz)
|
local xPzInv = fp.mul(xPzrPzInv, rPz)
|
||||||
local rPzInv = fp.mul(xPzrPzInv, xPz)
|
local rPzInv = fp.mul(xPzrPzInv, xPz)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue