94 lines
2.5 KiB
Lua
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
|