From 501e81a36a4ecc3a8fe7f58cecb80a212e0fc7a2 Mon Sep 17 00:00:00 2001 From: Miguel Oliveira Date: Wed, 2 Mar 2022 13:51:51 -0300 Subject: [PATCH] Add masked X25519 --- internal/fq.lua | 2 +- internal/x25519.lua | 5 +- random.lua | 66 ++++++++++++++++++++++++++ x25519.lua | 6 +-- x25519c.lua | 111 ++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 184 insertions(+), 6 deletions(-) create mode 100644 random.lua create mode 100644 x25519c.lua diff --git a/internal/fq.lua b/internal/fq.lua index bd65d3a..294044b 100644 --- a/internal/fq.lua +++ b/internal/fq.lua @@ -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 diff --git a/internal/x25519.lua b/internal/x25519.lua index b4f7a20..23ba994 100644 --- a/internal/x25519.lua +++ b/internal/x25519.lua @@ -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, diff --git a/random.lua b/random.lua new file mode 100644 index 0000000..34e7c5a --- /dev/null +++ b/random.lua @@ -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, +} diff --git a/x25519.lua b/x25519.lua index 89d959e..65ea4ab 100644 --- a/x25519.lua +++ b/x25519.lua @@ -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 diff --git a/x25519c.lua b/x25519c.lua new file mode 100644 index 0000000..1854c70 --- /dev/null +++ b/x25519c.lua @@ -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 = {("