diff --git a/ed25519.lua b/ed25519.lua index 1942aaa..717aa5c 100644 --- a/ed25519.lua +++ b/ed25519.lua @@ -1,15 +1,231 @@ --- 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 expect = require "cc.expect".expect +local fp = require "ccryptolib.internal.fp" +local fq = require "ccryptolib.internal.fq" +local sha512 = require "ccryptolib.internal.sha512" +local random = require "ccryptolib.random" + +local unpack = unpack or table.unpack + +local D = fp.mul(fp.num(-121665), fp.invert(fp.num(121666))) +local K = fp.kmul(D, 2) + +local O = {fp.num(0), fp.num(1), fp.num(1), fp.num(0)} +local G = nil + +local function double(P1) + local P1x, P1y, P1z = unpack(P1) + local a = fp.square(P1x) + local b = fp.square(P1y) + local c = fp.square(P1z) + local d = fp.kmul(c, 2) + local e = fp.add(a, b) + local f = fp.add(P1x, P1y) + local g = fp.square(f) + local h = fp.sub(g, e) + local i = fp.sub(b, a) + local j = fp.sub(d, i) + local P3x = fp.mul(h, j) + local P3y = fp.mul(i, e) + local P3z = fp.mul(j, i) + local P3t = fp.mul(h, e) + return {P3x, P3y, P3z, P3t} +end + +local function add(P1, N1) + local P1x, P1y, P1z, P1t = unpack(P1) + local N1p, N1m, N1z, N1t = unpack(N1) + local a = fp.sub(P1y, P1x) + local b = fp.mul(a, N1m) + local c = fp.add(P1y, P1x) + local d = fp.mul(c, N1p) + local e = fp.mul(P1t, N1t) + local f = fp.mul(P1z, N1z) + local g = fp.sub(d, b) + local h = fp.sub(f, e) + local i = fp.add(f, e) + local j = fp.add(d, b) + local P3x = fp.mul(g, h) + local P3y = fp.mul(i, j) + local P3z = fp.mul(h, i) + local P3t = fp.mul(g, j) + return {P3x, P3y, P3z, P3t} +end + +local function sub(P1, N1) + local P1x, P1y, P1z, P1t = unpack(P1) + local N1p, N1m, N1z, N1t = unpack(N1) + local a = fp.sub(P1y, P1x) + local b = fp.mul(a, N1p) + local c = fp.add(P1y, P1x) + local d = fp.mul(c, N1m) + local e = fp.mul(P1t, N1t) + local f = fp.mul(P1z, N1z) + local g = fp.sub(d, b) + local h = fp.add(f, e) + local i = fp.sub(f, e) + local j = fp.add(d, b) + local P3x = fp.mul(g, h) + local P3y = fp.mul(i, j) + local P3z = fp.mul(h, i) + local P3t = fp.mul(g, j) + return {P3x, P3y, P3z, P3t} +end + +local function niels(P1) + local P1x, P1y, P1z, P1t = unpack(P1) + local N3p = fp.add(P1y, P1x) + local N3m = fp.sub(P1y, P1x) + local N3z = fp.add(P1z, P1z) + local N3t = fp.mul(P1t, K) + return {N3p, N3m, N3z, N3t} +end + +local function scale(P1) + local P1x, P1y, P1z = unpack(P1) + local zInv = fp.invert(P1z) + local P3x = fp.mul(P1x, zInv) + local P3y = fp.mul(P1y, zInv) + local P3z = fp.num(1) + local P3t = fp.mul(P3x, P3y) + return {P3x, P3y, P3z, P3t} +end + +local function encode(P1) + local P1x, P1y = unpack(P1) + local y = fp.encode(P1y) + local xBit = fp.canonicalize(P1x)[1] % 2 + return y:sub(1, -2) .. string.char(y:byte(-1) + xBit * 128) +end + +local function decode(str) + local P3y = fp.decode(str) + local a = fp.square(P3y) + local b = fp.sub(a, fp.num(1)) + local c = fp.mul(a, D) + local d = fp.add(c, fp.num(1)) + local P3x = fp.sqrtDiv(b, d) + if not P3x then return nil end + local xBit = fp.canonicalize(P3x)[1] % 2 + if xBit ~= bit32.extract(str:byte(-1), 7) then + P3x = fp.neg(P3x) + P3x = fp.carry(P3x) + end + local P3z = fp.num(1) + local P3t = fp.mul(P3x, P3y) + return {P3x, P3y, P3z, P3t} +end + +G = decode("Xfffffffffffffffffffffffffffffff") + +local function signedRadixW(bits, w) + -- TODO Find a more elegant way of doing this. + local wPow = 2 ^ w + local wPowh = wPow / 2 + local out = {} + local acc = 0 + local mul = 1 + for i = 1, #bits do + acc = acc + bits[i] * mul + mul = mul * 2 + while i == #bits and acc > 0 or mul > wPow do + local rem = acc % wPow + if rem >= wPowh then rem = rem - wPow end + acc = (acc - rem) / wPow + mul = mul / wPow + out[#out + 1] = rem + end + end + return out +end + +local function radixWTable(P, w) + local out = {} + for i = 1, 255 / w do + local row = {niels(P)} + for j = 2, 2 ^ w / 2 do + P = add(P, row[1]) + row[j] = niels(P) + end + out[i] = row + P = double(P) + end + return out +end + +local G_W = 5 +local G_TABLE = radixWTable(G, G_W) + +local function WNAF(bits, w) + -- TODO Find a more elegant way of doing this. + local wPow = 2 ^ w + local wPowh = wPow / 2 + local out = {} + local acc = 0 + local mul = 1 + for i = 1, #bits do + acc = acc + bits[i] * mul + mul = mul * 2 + while i == #bits and acc > 0 or mul > wPow do + if acc % 2 == 0 then + acc = acc / 2 + mul = mul / 2 + out[#out + 1] = 0 + else + local rem = acc % wPow + if rem >= wPowh then rem = rem - wPow end + acc = acc - rem + out[#out + 1] = rem + end + end + end + while out[#out] == 0 do out[#out] = nil end + return out +end + +local function WNAFTable(P, w) + local dP = double(P) + local out = {niels(P)} + for i = 3, 2 ^ w, 2 do + out[i] = niels(add(dP, out[i - 2])) + end + return out +end + +local function mulG(bits) + local sw = signedRadixW(bits, G_W) + local R = O + for i = 1, #sw do + local b = sw[i] + if b > 0 then + R = add(R, G_TABLE[i][b]) + elseif b < 0 then + R = sub(R, G_TABLE[i][-b]) + end + end + return R +end + +local function mul(P, bits) + local naf = WNAF(bits, 5) + local tbl = WNAFTable(P, 5) + local R = O + for i = #naf, 1, -1 do + local b = naf[i] + if b == 0 then + R = double(R) + elseif b > 0 then + R = add(R, tbl[b]) + else + R = sub(R, tbl[-b]) + end + end + return R +end local mod = {} @@ -25,7 +241,7 @@ function mod.publicKey(sk) local h = sha512.digest(sk) local x = fq.decodeClamped(h:sub(1, 32)) - return ed25519.encode(ed25519.scale(ed25519.mulG(fq.bits(x)))) + return encode(scale(mulG(fq.bits(x)))) end --- Signs a message. @@ -47,15 +263,16 @@ function mod.sign(sk, pk, msg) 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)) + local k = fq.decodeWide(random.random(64)) + local r = mulG(fq.bits(k)) + local rStr = encode(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 m = fq.decodeWide(random.random(64)) + local s = fq.add(fq.add(k, fq.neg(fq.mul(fq.add(x, m), e))), fq.mul(m, e)) local sStr = fq.encode(s) return rStr .. sStr @@ -75,7 +292,7 @@ function mod.verify(pk, msg, sig) expect(3, sig, "string") assert(#sig == 64, "signature length must be 64") - local y = ed25519.decode(pk) + local y = decode(pk) if not y then return nil end local rStr = sig:sub(1, 32) @@ -83,11 +300,11 @@ function mod.verify(pk, msg, sig) 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)) + local gs = mulG(fq.bits(fq.decode(sStr))) + local ye = mul(y, fq.bits(e)) + local rv = add(gs, niels(ye)) - return ed25519.encode(ed25519.scale(rv)) == rStr + return encode(scale(rv)) == rStr end return mod diff --git a/ed25519c.lua b/ed25519c.lua deleted file mode 100644 index 85e3435..0000000 --- a/ed25519c.lua +++ /dev/null @@ -1,53 +0,0 @@ -local expect = require "cc.expect".expect -local fq = require "ccryptolib.internal.fq" -local sha512 = require "ccryptolib.internal.sha512" -local ed25519 = require "ccryptolib.internal.ed25519" -local maddq = require "ccryptolib.internal.maddq" -local random = require "ccryptolib.random" - -local ORDER = 4 - -local mod = {} - -function mod.new(sk) - expect(1, sk, "string") - assert(#sk == 32, "secret key length must be 32") - - return maddq.new(fq.decodeClamped(sha512.digest(sk):sub(1, 32)), ORDER) -end - -function mod.encode(sks) - return maddq.encode(sks) -end - -function mod.decode(str) - expect(1, str, "string") - assert(#str == 128, "encoded sks length must be 128") - - return maddq.decode(str) -end - -function mod.remask(sks) - return maddq.remask(sks) -end - -function mod.sign(sks, pk, msg) - -- Commitment. - local k = fq.decodeWide(random.random(64)) - 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. - -- Reduce secret key using the challenge and an extra mask. - local m = fq.decodeWide(random.random(64)) - local xme = maddq.unwrap(maddq.mul(maddq.add(sks, m), e)) - local s = fq.add(fq.add(k, fq.neg(xme)), fq.mul(m, e)) - local sStr = fq.encode(s) - - return rStr .. sStr -end - -return mod diff --git a/internal/ed25519.lua b/internal/ed25519.lua deleted file mode 100644 index 6ac4e5d..0000000 --- a/internal/ed25519.lua +++ /dev/null @@ -1,229 +0,0 @@ -local fp = require "ccryptolib.internal.fp" - -local unpack = unpack or table.unpack - -local D = fp.mul(fp.num(-121665), fp.invert(fp.num(121666))) -local K = fp.kmul(D, 2) - -local O = {fp.num(0), fp.num(1), fp.num(1), fp.num(0)} -local G = nil - -local function double(P1) - local P1x, P1y, P1z = unpack(P1) - local a = fp.square(P1x) - local b = fp.square(P1y) - local c = fp.square(P1z) - local d = fp.kmul(c, 2) - local e = fp.add(a, b) - local f = fp.add(P1x, P1y) - local g = fp.square(f) - local h = fp.sub(g, e) - local i = fp.sub(b, a) - local j = fp.sub(d, i) - local P3x = fp.mul(h, j) - local P3y = fp.mul(i, e) - local P3z = fp.mul(j, i) - local P3t = fp.mul(h, e) - return {P3x, P3y, P3z, P3t} -end - -local function add(P1, N1) - local P1x, P1y, P1z, P1t = unpack(P1) - local N1p, N1m, N1z, N1t = unpack(N1) - local a = fp.sub(P1y, P1x) - local b = fp.mul(a, N1m) - local c = fp.add(P1y, P1x) - local d = fp.mul(c, N1p) - local e = fp.mul(P1t, N1t) - local f = fp.mul(P1z, N1z) - local g = fp.sub(d, b) - local h = fp.sub(f, e) - local i = fp.add(f, e) - local j = fp.add(d, b) - local P3x = fp.mul(g, h) - local P3y = fp.mul(i, j) - local P3z = fp.mul(h, i) - local P3t = fp.mul(g, j) - return {P3x, P3y, P3z, P3t} -end - -local function sub(P1, N1) - local P1x, P1y, P1z, P1t = unpack(P1) - local N1p, N1m, N1z, N1t = unpack(N1) - local a = fp.sub(P1y, P1x) - local b = fp.mul(a, N1p) - local c = fp.add(P1y, P1x) - local d = fp.mul(c, N1m) - local e = fp.mul(P1t, N1t) - local f = fp.mul(P1z, N1z) - local g = fp.sub(d, b) - local h = fp.add(f, e) - local i = fp.sub(f, e) - local j = fp.add(d, b) - local P3x = fp.mul(g, h) - local P3y = fp.mul(i, j) - local P3z = fp.mul(h, i) - local P3t = fp.mul(g, j) - return {P3x, P3y, P3z, P3t} -end - -local function niels(P1) - local P1x, P1y, P1z, P1t = unpack(P1) - local N3p = fp.add(P1y, P1x) - local N3m = fp.sub(P1y, P1x) - local N3z = fp.add(P1z, P1z) - local N3t = fp.mul(P1t, K) - return {N3p, N3m, N3z, N3t} -end - -local function scale(P1) - local P1x, P1y, P1z = unpack(P1) - local zInv = fp.invert(P1z) - local P3x = fp.mul(P1x, zInv) - local P3y = fp.mul(P1y, zInv) - local P3z = fp.num(1) - local P3t = fp.mul(P3x, P3y) - return {P3x, P3y, P3z, P3t} -end - -local function encode(P1) - local P1x, P1y = unpack(P1) - local y = fp.encode(P1y) - local xBit = fp.canonicalize(P1x)[1] % 2 - return y:sub(1, -2) .. string.char(y:byte(-1) + xBit * 128) -end - -local function decode(str) - local P3y = fp.decode(str) - local a = fp.square(P3y) - local b = fp.sub(a, fp.num(1)) - local c = fp.mul(a, D) - local d = fp.add(c, fp.num(1)) - local P3x = fp.sqrtDiv(b, d) - if not P3x then return nil end - local xBit = fp.canonicalize(P3x)[1] % 2 - if xBit ~= bit32.extract(str:byte(-1), 7) then - P3x = fp.neg(P3x) - P3x = fp.carry(P3x) - end - local P3z = fp.num(1) - local P3t = fp.mul(P3x, P3y) - return {P3x, P3y, P3z, P3t} -end - -G = decode("Xfffffffffffffffffffffffffffffff") - -local function signedRadixW(bits, w) - -- TODO Find a more elegant way of doing this. - local wPow = 2 ^ w - local wPowh = wPow / 2 - local out = {} - local acc = 0 - local mul = 1 - for i = 1, #bits do - acc = acc + bits[i] * mul - mul = mul * 2 - while i == #bits and acc > 0 or mul > wPow do - local rem = acc % wPow - if rem >= wPowh then rem = rem - wPow end - acc = (acc - rem) / wPow - mul = mul / wPow - out[#out + 1] = rem - end - end - return out -end - -local function radixWTable(P, w) - local out = {} - for i = 1, 255 / w do - local row = {niels(P)} - for j = 2, 2 ^ w / 2 do - P = add(P, row[1]) - row[j] = niels(P) - end - out[i] = row - P = double(P) - end - return out -end - -local G_W = 5 -local G_TABLE = radixWTable(G, G_W) - -local function WNAF(bits, w) - -- TODO Find a more elegant way of doing this. - local wPow = 2 ^ w - local wPowh = wPow / 2 - local out = {} - local acc = 0 - local mul = 1 - for i = 1, #bits do - acc = acc + bits[i] * mul - mul = mul * 2 - while i == #bits and acc > 0 or mul > wPow do - if acc % 2 == 0 then - acc = acc / 2 - mul = mul / 2 - out[#out + 1] = 0 - else - local rem = acc % wPow - if rem >= wPowh then rem = rem - wPow end - acc = acc - rem - out[#out + 1] = rem - end - end - end - while out[#out] == 0 do out[#out] = nil end - return out -end - -local function WNAFTable(P, w) - local dP = double(P) - local out = {niels(P)} - for i = 3, 2 ^ w, 2 do - out[i] = niels(add(dP, out[i - 2])) - end - return out -end - -local function mulG(bits) - local sw = signedRadixW(bits, G_W) - local R = O - for i = 1, #sw do - local b = sw[i] - if b > 0 then - R = add(R, G_TABLE[i][b]) - elseif b < 0 then - R = sub(R, G_TABLE[i][-b]) - end - end - return R -end - -local function mul(P, bits) - local naf = WNAF(bits, 5) - local tbl = WNAFTable(P, 5) - local R = O - for i = #naf, 1, -1 do - local b = naf[i] - if b == 0 then - R = double(R) - elseif b > 0 then - R = add(R, tbl[b]) - else - R = sub(R, tbl[-b]) - end - end - return R -end - -return { - add = add, - niels = niels, - scale = scale, - encode = encode, - decode = decode, - mulG = mulG, - mul = mul, -} diff --git a/internal/maddq.lua b/internal/maddq.lua deleted file mode 100644 index f016fc6..0000000 --- a/internal/maddq.lua +++ /dev/null @@ -1,114 +0,0 @@ ---- Additive arithmetic masking modulo q (unstable, for internal use only). --- --- Representing secret scalars in Cobalt is potentially dangerous since the VM --- leaks information through timing. To remedy this, we use _masking_, which is --- a randomized representation of a scalar s as several values {s₁, s₂, s₃, ...} --- such that s₁ + s₂ + s₃ + ... ≡ s (mod q). --- --- Using masking, we can perform arithmetic on the masked value without needing --- to render the raw secrets in memory. After these operations, we can unwrap --- the result which, depending on the operations, is harder to measure. --- --- @module internal.maddq --- - -local fq = require "ccryptolib.internal.fq" -local random = require "ccryptolib.random" - ---- Builds a masked scalar from a regular one. --- --- @tparam {number...} val The input scalar. --- @tparam number order How many values to use, must be at least 2. --- @treturn {{number...}...} The masked scalar. --- -local function new(val, order) - local out = {} - local sum = fq.num(0) - for i = 1, order - 1 do - out[i] = fq.decodeWide(random.random(64)) - sum = fq.add(sum, out[i]) - end - - out[order] = fq.add(val, fq.neg(sum)) - - return out -end - ---- Unwraps a masked scalar, getting the raw unmasked value. --- --- @tparam {{number...}...} arr The input masked scalar. --- @treturn {number...} The unmasked scalar. --- -local function unwrap(arr) - local sum = fq.num(0) - for i = 1, #arr do sum = fq.add(sum, arr[i]) end - return sum -end - ---- Encodes a masked scalar into a string. --- --- @tparam {{number...}...} arr The input masked scalar. --- @treturn string The encoded value. --- -local function encode(arr) - local out = {} - for i = 1, #arr do out[i] = fq.encode(arr[i]) end - return table.concat(out) -end - ---- Decodes a masked scalar from a string. --- --- @tparam string str The encoded scalar. Length must be a multiple of 32. --- @treturn {{number...}...} The decoded value. --- -local function decode(str) - local out = {} - for i = 1, #str / 32 do out[i] = fq.decode(str:sub(i * 32 - 31, i * 32)) end - return out -end - ---- Rerandomizes a masked scalar's representation. --- --- @tparam {{number...}...} arr The input masked scalar. --- @treturn {{number...}...} The rerandomized representation of the input. --- -local function remask(arr) - local out = new(fq.num(0), #arr) - for i = 1, #arr do out[i] = fq.add(out[i], arr[i]) end - return out -end - ---- Multiplies a masked scalar by a regular scalar. --- --- @tparam {{number...}...} arr The input masked scalar. --- @tparam {number...} k The scalar to multiply by. --- @treturn {{number...}...} The masked product of both values. --- -local function mul(arr, k) - local out = {} - for i = 1, #arr do out[i] = fq.mul(arr[i], k) end - return out -end - ---- Adds a regular scalar to a masked scalar. --- --- @tparam {{number...}...} The input masked scalar. --- @tparam {number...} v The scalar to add to. --- @treturn {{number...}...} The masked sum of both values. --- -local function add(arr, v) - local out = {} - for i = 1, #arr do out[i] = fq.clone(arr[i]) end - out[#arr] = fq.add(out[#arr], v) - return out -end - -return { - new = new, - unwrap = unwrap, - encode = encode, - decode = decode, - remask = remask, - mul = mul, - add = add, -} diff --git a/internal/x25519.lua b/internal/x25519.lua deleted file mode 100644 index e6b9886..0000000 --- a/internal/x25519.lua +++ /dev/null @@ -1,37 +0,0 @@ -local fp = require "ccryptolib.internal.fp" - -local G = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - -local function double(x1, z1) - local a = fp.add(x1, z1) - local aa = fp.square(a) - local b = fp.sub(x1, z1) - local bb = fp.square(b) - local c = fp.sub(aa, bb) - local x3 = fp.mul(aa, bb) - local z3 = fp.mul(c, fp.add(bb, fp.kmul(c, 121666))) - return x3, z3 -end - -local function step(dx, x1, z1, x2, z2) - local a = fp.add(x1, z1) - local aa = fp.square(a) - local b = fp.sub(x1, z1) - local bb = fp.square(b) - local e = fp.sub(aa, bb) - local c = fp.add(x2, z2) - local d = fp.sub(x2, z2) - local da = fp.mul(d, a) - local cb = fp.mul(c, b) - local x4 = fp.square(fp.add(da, cb)) - local z4 = fp.mul(dx, fp.square(fp.sub(da, cb))) - local x3 = fp.mul(aa, bb) - local z3 = fp.mul(e, fp.add(bb, fp.kmul(e, 121666))) - return x3, z3, x4, z4 -end - -return { - G = G, - double = double, - step = step, -} diff --git a/x25519.lua b/x25519.lua index 4686b29..a29231e 100644 --- a/x25519.lua +++ b/x25519.lua @@ -1,9 +1,37 @@ local expect = require "cc.expect".expect local fp = require "ccryptolib.internal.fp" -local x25519 = require "ccryptolib.internal.x25519" +local random = require "ccryptolib.random" local unpack = unpack or table.unpack +local function double(x1, z1) + local a = fp.add(x1, z1) + local aa = fp.square(a) + local b = fp.sub(x1, z1) + local bb = fp.square(b) + local c = fp.sub(aa, bb) + local x3 = fp.mul(aa, bb) + local z3 = fp.mul(c, fp.add(bb, fp.kmul(c, 121666))) + return x3, z3 +end + +local function step(dx, x1, z1, x2, z2) + local a = fp.add(x1, z1) + local aa = fp.square(a) + local b = fp.sub(x1, z1) + local bb = fp.square(b) + local e = fp.sub(aa, bb) + local c = fp.add(x2, z2) + local d = fp.sub(x2, z2) + local da = fp.mul(d, a) + local cb = fp.mul(c, b) + local x4 = fp.square(fp.add(da, cb)) + local z4 = fp.mul(dx, fp.square(fp.sub(da, cb))) + local x3 = fp.mul(aa, bb) + local z3 = fp.mul(e, fp.add(bb, fp.kmul(e, 121666))) + return x3, z3, x4, z4 +end + local function bits(str) -- Decode. local bytes = {str:byte(1, 32)} @@ -26,22 +54,24 @@ local function bits(str) end local function ladder8(dx, bits) - local x1 = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - local z1 = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0} - local x2, z2 = dx, x1 + local x1 = fp.num(1) + local z1 = fp.num(0) + + local z2 = fp.decode(random.random(32)) + local x2 = fp.mul(dx, z2) -- Standard ladder. for i = #bits, 1, -1 do if bits[i] == 0 then - x1, z1, x2, z2 = x25519.step(dx, x1, z1, x2, z2) + x1, z1, x2, z2 = step(dx, x1, z1, x2, z2) else - x2, z2, x1, z1 = x25519.step(dx, x2, z2, x1, z1) + x2, z2, x1, z1 = step(dx, x2, z2, x1, z1) end end -- Multiply by 8 (double 3 times). for _ = 1, 3 do - x1, z1 = x25519.double(x1, z1) + x1, z1 = double(x1, z1) end return fp.mul(x1, fp.invert(z1)) @@ -53,7 +83,7 @@ function mod.publicKey(sk) expect(1, sk, "string") assert(#sk == 32, "secret key length must be 32") - return fp.encode(ladder8(x25519.G, bits(sk))) + return fp.encode(ladder8(fp.num(9), bits(sk))) end function mod.exchange(sk, pk) diff --git a/x25519c.lua b/x25519c.lua deleted file mode 100644 index 97d598e..0000000 --- a/x25519c.lua +++ /dev/null @@ -1,94 +0,0 @@ -local expect = require "cc.expect".expect -local fp = require "ccryptolib.internal.fp" -local fq = require "ccryptolib.internal.fq" -local x25519 = require "ccryptolib.internal.x25519" -local maddq = require "ccryptolib.internal.maddq" -local random = require "ccryptolib.random" - -local ORDER = 4 - ---- The inverse of 8 modulo q (in montgomery form). -local INV8Q = { - 5110253, - 3039345, - 2503500, - 11779568, - 15416472, - 16766550, - 16777215, - 16777215, - 16777215, - 16777215, - 4095, -} - -local function ladder8(dx, bits) - local x1 = fp.num(1) - local z1 = fp.num(0) - - -- Compute a randomization factor for randomized projective coordinates. - -- Biased but good enough. - local rf = fp.decode(random.random(32)) - - local x2 = fp.mul(rf, dx) - local z2 = rf - - -- Standard ladder. - for i = #bits, 1, -1 do - if bits[i] == 0 then - x1, z1, x2, z2 = x25519.step(dx, x1, z1, x2, z2) - else - x2, z2, x1, z1 = x25519.step(dx, x2, z2, x1, z1) - end - end - - -- Multiply by 8 (double 3 times). - for _ = 1, 3 do - x1, z1 = x25519.double(x1, z1) - end - - return fp.mul(x1, fp.invert(z1)) -end - -local mod = {} - -function mod.new(sk) - expect(1, sk, "string") - assert(#sk == 32, "secret key length must be 32") - - return maddq.new(fq.decodeClamped(sk), ORDER) -end - -function mod.encode(sks) - return maddq.encode(sks) -end - -function mod.decode(str) - expect(1, str, "string") - assert(#str == 128, "encoded sks length must be 128") - - return maddq.decode(str) -end - -function mod.remask(sks) - return maddq.remask(sks) -end - -function mod.exchange(sks, pk, mc) - expect(2, pk, "string") - assert(#pk == 32, "public key length must be 32") - expect(3, mc, "string") - assert(#mc == 32, "multiplier length must be 32") - - -- Reduce secret key using the multiplier. - local skmc = maddq.unwrap(maddq.mul(sks, fq.decodeClamped(mc))) - - -- Get bits. - -- We have our exponent modulo q. We also know that its value is 0 modulo 8. - -- Use the Chinese Remainder Theorem to find its value modulo 8q. - local bits = fq.bits(fq.mul(skmc, INV8Q)) - - return fp.encode(ladder8(fp.decode(pk), bits)) -end - -return mod