Add masked X25519
This commit is contained in:
parent
eae7c91453
commit
501e81a36a
|
@ -3,7 +3,7 @@
|
|||
-- @module ccryptolib.internal.fq
|
||||
--
|
||||
|
||||
local util = require "ccryptolib.util"
|
||||
local util = require "ccryptolib.internal.util"
|
||||
|
||||
local unpack = unpack or table.unpack
|
||||
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
local fp = require "ccryptolibinternal.fp"
|
||||
local fp = require "ccryptolib.internal.fp"
|
||||
|
||||
local G = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
local function step(dx, x1, z1, x2, z2)
|
||||
local a = fp.add(x1, z1)
|
||||
|
@ -57,6 +59,7 @@ local function bits(str)
|
|||
end
|
||||
|
||||
return {
|
||||
G = G,
|
||||
step = step,
|
||||
ladder = ladder,
|
||||
bits = bits,
|
||||
|
|
66
random.lua
Normal file
66
random.lua
Normal file
|
@ -0,0 +1,66 @@
|
|||
local blake3 = require "ccryptolib.blake3"
|
||||
local chacha20 = require "ccryptolib.chacha20"
|
||||
|
||||
-- Initialize from local context.
|
||||
local ctx = {
|
||||
os.epoch("utc"),
|
||||
math.random(0, 2 ^ 24 - 1),
|
||||
math.random(0, 2 ^ 24 - 1),
|
||||
tostring({}),
|
||||
tostring({}),
|
||||
}
|
||||
|
||||
local state = blake3.digest(table.concat(ctx, "|"), 32)
|
||||
|
||||
local function seed(data)
|
||||
state = blake3.digestKeyed(state, data, 32)
|
||||
end
|
||||
|
||||
local function stir(n)
|
||||
-- Collect samples from jitter.
|
||||
local epoch = os.epoch
|
||||
local acc = {}
|
||||
local byte = 0
|
||||
for i = 1, n do
|
||||
local t0 = epoch("utc")
|
||||
repeat byte = byte + 1 until epoch("utc") ~= t0
|
||||
acc[i] = byte % 256
|
||||
end
|
||||
|
||||
-- Extract into the new state.
|
||||
seed(string.char(table.unpack(acc)))
|
||||
end
|
||||
|
||||
local function random(len)
|
||||
local msg = ("\0"):rep(len + 32)
|
||||
local nonce = ("\0"):rep(12)
|
||||
local out = chacha20.crypt(state, nonce, msg, 8, 0)
|
||||
state = out:sub(1, 32)
|
||||
return out:sub(33)
|
||||
end
|
||||
|
||||
local function save()
|
||||
local file = fs.open("/.random", "wb")
|
||||
file.write(random(32))
|
||||
file.close()
|
||||
end
|
||||
|
||||
-- Load.
|
||||
if fs.exists("./random") then
|
||||
local file = fs.open("./random", "rb")
|
||||
seed(file.read(32) or "")
|
||||
end
|
||||
|
||||
-- Add extra entropy.
|
||||
stir(512)
|
||||
|
||||
-- Save.
|
||||
math.randomseed(("I4"):unpack(random(4)))
|
||||
save()
|
||||
|
||||
return {
|
||||
seed = seed,
|
||||
stir = stir,
|
||||
random = random,
|
||||
save = save,
|
||||
}
|
|
@ -2,15 +2,13 @@ local expect = require "cc.expect".expect
|
|||
local fp = require "ccryptolib.internal.fp"
|
||||
local x25519 = require "ccryptolib.internal.x25519"
|
||||
|
||||
local G = {9, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
|
||||
|
||||
local mod = {}
|
||||
|
||||
function mod.publicKey(sk)
|
||||
expect(1, sk, "string")
|
||||
assert(#sk == 32, "secret key length must be 32")
|
||||
|
||||
return fp.encode(x25519.ladder(G, x25519.bits(sk)))
|
||||
return fp.encode(x25519.ladder(x25519.G, x25519.bits(sk)))
|
||||
end
|
||||
|
||||
function mod.exchange(sk, pk)
|
||||
|
@ -19,7 +17,7 @@ function mod.exchange(sk, pk)
|
|||
expect(2, pk, "string")
|
||||
assert(#pk == 32, "public key length must be 32")
|
||||
|
||||
return fp.encode(x25519(fp.decode(pk), x25519.bits(sk)))
|
||||
return fp.encode(x25519.ladder(fp.decode(pk), x25519.bits(sk)))
|
||||
end
|
||||
|
||||
return mod
|
||||
|
|
111
x25519c.lua
Normal file
111
x25519c.lua
Normal file
|
@ -0,0 +1,111 @@
|
|||
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 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 fqRandom()
|
||||
return fq.decodeWide(random.random(64))
|
||||
end
|
||||
|
||||
local function fqDecodeStd(str)
|
||||
-- Decode.
|
||||
local words = {("<I3I3I3I3I3I3I3I3I3I3I2"):unpack(str)} words[12] = nil
|
||||
|
||||
-- Clamp.
|
||||
words[1] = bit32.band(words[1], 0xfffff8)
|
||||
words[11] = bit32.band(words[11], 0x7fff)
|
||||
words[11] = bit32.bor(words[11], 0x4000)
|
||||
|
||||
return fq.montgomery(words)
|
||||
end
|
||||
|
||||
local mod = {}
|
||||
|
||||
function mod.secretKeyInit(sk)
|
||||
sk = fqDecodeStd(sk)
|
||||
|
||||
-- Set up the mask.
|
||||
local sks = {}
|
||||
local sum = fq.num(0)
|
||||
for i = 1, ORDER - 1 do
|
||||
sks[i] = fqRandom()
|
||||
sum = fq.add(sum, sks[i])
|
||||
end
|
||||
sks[ORDER] = fq.add(sk, fq.neg(sum))
|
||||
|
||||
return sks
|
||||
end
|
||||
|
||||
function mod.secretKeyEncode(sks)
|
||||
local out = {}
|
||||
for i = 1, ORDER do out[i] = fq.encode(sks[i]) end
|
||||
return table.concat(out)
|
||||
end
|
||||
|
||||
function mod.secretKeyDecode(str)
|
||||
expect(1, str, "string")
|
||||
assert(#str == ORDER * 32, ("secret key length must be %d"):format(ORDER * 32))
|
||||
|
||||
local out = {}
|
||||
for i = 1, ORDER do out[i] = fq.decode(str:sub(i * 32 - 31, i * 32)) end
|
||||
return out
|
||||
end
|
||||
|
||||
function mod.secretKeyRemask(sk)
|
||||
local sum = fq.num(0)
|
||||
local out = {}
|
||||
|
||||
for i = 1, ORDER - 1 do
|
||||
local element = fqRandom()
|
||||
out[i] = fq.add(sk[i], element)
|
||||
sum = fq.add(sum, element)
|
||||
end
|
||||
|
||||
out[ORDER] = fq.add(sk[ORDER], fq.neg(sum))
|
||||
|
||||
return out
|
||||
end
|
||||
|
||||
function mod.exchange(sk, 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")
|
||||
|
||||
-- Get the multiplier in Fq.
|
||||
mc = fqDecodeStd(mc)
|
||||
|
||||
-- Multiply secret key members and add them together.
|
||||
-- This unwraps into the "true" secret key times the multiplier (mod q).
|
||||
local skmt = fq.num(0)
|
||||
for i = 1, #sk do skmt = fq.add(skmt, fq.mul(sk[i], mc)) end
|
||||
|
||||
-- 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(skmt, INV8Q))
|
||||
local bits8 = {0, 0, 0}
|
||||
for i = 1, 253 do bits8[i + 3] = bits[i] end
|
||||
|
||||
return fp.encode(x25519.ladder(fp.decode(pk), bits8))
|
||||
end
|
||||
|
||||
return mod
|
Loading…
Reference in a new issue