Add birational decoding of Edwards25519 points
I really dislike this interface, I'll probably need to think of something else.
This commit is contained in:
parent
a7d98da04c
commit
374ba5ee4a
|
@ -121,6 +121,25 @@ local function decode(str)
|
||||||
return {fp.decode(str), fp.num(1)}
|
return {fp.decode(str), fp.num(1)}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Decodes an Edwards25519 encoded point into Curve25519, ignoring the sign.
|
||||||
|
--
|
||||||
|
-- There is a single exception: The identity point (0, 1), which gets mapped
|
||||||
|
-- into the 2-torsion point (0, 0), which isn't the identity of Curve25519.
|
||||||
|
--
|
||||||
|
-- @tparam string str A 32-byte encoded Edwards25519 point.
|
||||||
|
-- @treturn point The decoded point, mapped into Curve25519.
|
||||||
|
--
|
||||||
|
local function decodeEd(str)
|
||||||
|
local y = fp.decode(str)
|
||||||
|
local n = fp.carry(fp.add(fp.num(1), y))
|
||||||
|
local d = fp.carry(fp.sub(fp.num(1), y))
|
||||||
|
if fp.eqz(d) then
|
||||||
|
return {fp.num(0), fp.num(1)}
|
||||||
|
else
|
||||||
|
return {n, d}
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
--- Performs a scalar multiplication by the base point G.
|
--- Performs a scalar multiplication by the base point G.
|
||||||
--
|
--
|
||||||
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
|
-- @tparam {number...} bits The scalar multiplier, in little-endian bits.
|
||||||
|
@ -269,6 +288,7 @@ return {
|
||||||
scale = scale,
|
scale = scale,
|
||||||
encode = encode,
|
encode = encode,
|
||||||
decode = decode,
|
decode = decode,
|
||||||
|
decodeEd = decodeEd,
|
||||||
ladder8 = ladder8,
|
ladder8 = ladder8,
|
||||||
mulG = mulG,
|
mulG = mulG,
|
||||||
prac = prac,
|
prac = prac,
|
||||||
|
|
|
@ -34,4 +34,13 @@ function mod.exchange(sk, pk)
|
||||||
return c25.encode(c25.scale(c25.ladder8(c25.decode(pk), util.bits8(sk))))
|
return c25.encode(c25.scale(c25.ladder8(c25.decode(pk), util.bits8(sk))))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Same as @{exchange}, but decodes the public key as an Edwards25519 point.
|
||||||
|
function mod.exchangeEd(sk, pk)
|
||||||
|
expect(1, sk, "string")
|
||||||
|
assert(#sk == 32, "secret key length must be 32")
|
||||||
|
expect(2, pk, "string")
|
||||||
|
assert(#pk == 32, "public key length must be 32")
|
||||||
|
return c25.encode(c25.scale(c25.ladder8(c25.decodeEd(pk), util.bits8(sk))))
|
||||||
|
end
|
||||||
|
|
||||||
return mod
|
return mod
|
||||||
|
|
|
@ -27,23 +27,7 @@ local function remask(msk, oldMask)
|
||||||
return fq.encode(xs), newMask
|
return fq.encode(xs), newMask
|
||||||
end
|
end
|
||||||
|
|
||||||
--- Treats both shares as X25519 keys and performs a double key exchange.
|
local function exchangeOnPoint(msk, mask, P)
|
||||||
--
|
|
||||||
-- Returns 0 if the input public key has small order or if it isn't in the base
|
|
||||||
-- curve. This is different from standard X25519, which performs the exchange
|
|
||||||
-- even on the twist.
|
|
||||||
--
|
|
||||||
-- May incorrectly return 0 with negligible chance if the mask happens to match
|
|
||||||
-- the masked key. I haven't checked if clamping prevents that from happening.
|
|
||||||
--
|
|
||||||
local function exchange(msk, mask, pk)
|
|
||||||
expect(1, msk, "string")
|
|
||||||
assert(#msk == 32, "masked secret key length must be 32")
|
|
||||||
expect(2, mask, "string")
|
|
||||||
assert(#mask == 32, "mask length must be 32")
|
|
||||||
expect(3, pk, "string")
|
|
||||||
assert(#pk == 32, "public key length must be 32")
|
|
||||||
local P = c25.decode(pk)
|
|
||||||
local xr = fq.decode(msk)
|
local xr = fq.decode(msk)
|
||||||
local r = fq.decodeClamped8(mask)
|
local r = fq.decodeClamped8(mask)
|
||||||
local rP, xrP, dP = c25.prac(P, fq.makeRuleset(r, xr))
|
local rP, xrP, dP = c25.prac(P, fq.makeRuleset(r, xr))
|
||||||
|
@ -95,8 +79,39 @@ local function exchange(msk, mask, pk)
|
||||||
return fp.encode(fp.mul(xPx, xPzInv)), fp.encode(fp.mul(rPx, rPzInv))
|
return fp.encode(fp.mul(xPx, xPzInv)), fp.encode(fp.mul(rPx, rPzInv))
|
||||||
end
|
end
|
||||||
|
|
||||||
|
--- Treats both shares as X25519 keys and performs a double key exchange.
|
||||||
|
--
|
||||||
|
-- Returns 0 if the input public key has small order or if it isn't in the base
|
||||||
|
-- curve. This is different from standard X25519, which performs the exchange
|
||||||
|
-- even on the twist.
|
||||||
|
--
|
||||||
|
-- May incorrectly return 0 with negligible chance if the mask happens to match
|
||||||
|
-- the masked key. I haven't checked if clamping prevents that from happening.
|
||||||
|
--
|
||||||
|
local function exchange(msk, mask, pk)
|
||||||
|
expect(1, msk, "string")
|
||||||
|
assert(#msk == 32, "masked secret key length must be 32")
|
||||||
|
expect(2, mask, "string")
|
||||||
|
assert(#mask == 32, "mask length must be 32")
|
||||||
|
expect(3, pk, "string")
|
||||||
|
assert(#pk == 32, "public key length must be 32")
|
||||||
|
return exchangeOnPoint(msk, mask, c25.decode(pk))
|
||||||
|
end
|
||||||
|
|
||||||
|
--- Same as @{exchange}, but decodes the public key as an Edwards25519 point.
|
||||||
|
local function exchangeEd(msk, mask, pk)
|
||||||
|
expect(1, msk, "string")
|
||||||
|
assert(#msk == 32, "masked secret key length must be 32")
|
||||||
|
expect(2, mask, "string")
|
||||||
|
assert(#mask == 32, "mask length must be 32")
|
||||||
|
expect(3, pk, "string")
|
||||||
|
assert(#pk == 32, "public key length must be 32")
|
||||||
|
return exchangeOnPoint(msk, mask, c25.decodeEd(pk))
|
||||||
|
end
|
||||||
|
|
||||||
return {
|
return {
|
||||||
mask = mask,
|
mask = mask,
|
||||||
remask = remask,
|
remask = remask,
|
||||||
exchange = exchange,
|
exchange = exchange,
|
||||||
|
exchangeEd = exchangeEd,
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue