ccryptolib/ed25519.lua
2022-03-04 11:46:26 -03:00

94 lines
2.5 KiB
Lua

--- The Ed25519 digital signature scheme.
--
-- **Note:** This library is provided for compatibility and provides no side
-- channel resistance by itself.
--
-- @module ed25519
--
local expect = require "cc.expect".expect
local fq = require "ccryptolib.internal.fq"
local ed25519 = require "ccryptolib.internal.ed25519"
local sha512 = require "ccryptolib.internal.sha512"
local mod = {}
--- Computes a public key from a secret key.
--
-- @tparam string sk A random 32-byte secret key.
-- @treturn string The matching 32-byte public key.
--
function mod.publicKey(sk)
expect(1, sk, "string")
assert(#sk == 32, "secret key length must be 32")
local h = sha512.digest(sk)
local x = fq.decodeClamped(h:sub(1, 32))
return ed25519.encode(ed25519.scale(ed25519.mulG(fq.bits(x))))
end
--- Signs a message.
--
-- @tparam string sk The signer's secret key.
-- @tparam string pk The signer's public key.
-- @tparam string msg The message to be signed.
-- @treturn string The 64-byte signature on the message.
--
function mod.sign(sk, pk, msg)
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")
expect(3, msg, "string")
-- Secret key.
local h = sha512.digest(sk)
local x = fq.decodeClamped(h:sub(1, 32))
-- Commitment.
local k = fq.decodeWide(sha512.digest(h:sub(33) .. msg))
local r = ed25519.mulG(fq.bits(k))
local rStr = ed25519.encode(ed25519.scale(r))
-- Challenge.
local e = fq.decodeWide(sha512.digest(rStr .. pk .. msg))
-- Response.
local s = fq.add(k, fq.neg(fq.mul(x, e)))
local sStr = fq.encode(s)
return rStr .. sStr
end
--- Verifies a signature on a message.
--
-- @tparam string pk The signer's public key.
-- @tparam string msg The signed message.
-- @tparam string sig The signature.
-- @treturn boolean Whether the signature is valid or not.
--
function mod.verify(pk, msg, sig)
expect(1, pk, "string")
assert(#pk == 32, "public key length must be 32")
expect(2, msg, "string")
expect(3, sig, "string")
assert(#sig == 64, "signature length must be 64")
local y = ed25519.decode(pk)
if not y then return nil end
local rStr = sig:sub(1, 32)
local sStr = sig:sub(33)
local e = fq.decodeWide(sha512.digest(rStr .. pk .. msg))
local gs = ed25519.mulG(fq.bits(fq.decode(sStr)))
local ye = ed25519.mul(y, fq.bits(e))
local rv = ed25519.add(gs, ed25519.niels(ye))
return ed25519.encode(ed25519.scale(rv)) == rStr
end
return mod