Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- -- Elliptic Curve Cryptography in Computercraft
- ---- Update (Jun 7 2023)
- -- Fix string inputs not working on signatures
- -- Switch internal byte arrays to strings on most places
- -- Improve entropy gathering by using counting
- -- Other general improvements to syntax
- ---- Update (Jun 4 2021)
- -- Fix compatibility with CraftOS-PC
- ---- Update (Jul 30 2020)
- -- Make randomModQ and use it instead of hashing from random.random()
- ---- Update (Feb 10 2020)
- -- Make a more robust encoding/decoding implementation
- ---- Update (Dec 30 2019)
- -- Fix rng not accumulating entropy from loop
- -- (older versions should be fine from other sources + stored in disk)
- ---- Update (Dec 28 2019)
- -- Slightly better integer multiplication and squaring
- -- Fix global variable declarations in modQ division and verify() (no security concerns)
- -- Small tweaks from SquidDev's illuaminate (https://github.com/SquidDev/illuaminate/)
- local function mapToStr(t)
- if type(t) == "table" then
- return string.char(unpack(t))
- else
- return tostring(t)
- end
- end
- local byteTableMT = {
- __tostring = mapToStr,
- __index = {
- toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end,
- isEqual = function(self, t)
- if type(t) ~= "table" then return false end
- if #self ~= #t then return false end
- local ret = 0
- for i = 1, #self do
- ret = bit32.bor(ret, bit32.bxor(self[i], t[i]))
- end
- return ret == 0
- end
- }
- }
- local function strToByteArr(s)
- return setmetatable({s:byte(1, -1)}, byteTableMT)
- end
- -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft
- -- By Anavrins
- -- For help and details, you can PM me on the CC forums
- -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
- -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
- -- http://pastebin.com/6UV4qfNF
- -- Last update: October 10, 2017
- local sha256 = (function()
- local mod32 = 2^32
- local band = bit32 and bit32.band or bit.band
- local bnot = bit32 and bit32.bnot or bit.bnot
- local bxor = bit32 and bit32.bxor or bit.bxor
- local blshift = bit32 and bit32.lshift or bit.blshift
- local upack = unpack
- local function rrotate(n, b)
- local s = n/(2^b)
- local f = s%1
- return (s-f) + f*mod32
- end
- local function brshift(int, by) -- Thanks bit32 for bad rshift
- local s = int / (2^by)
- return s - s%1
- end
- local H = {
- 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
- 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19,
- }
- local K = {
- 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
- 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
- 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
- 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
- 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
- 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
- 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
- 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2,
- }
- local function counter(incr)
- local t1, t2 = 0, 0
- if 0xFFFFFFFF - t1 < incr then
- t2 = t2 + 1
- t1 = incr - (0xFFFFFFFF - t1) - 1
- else t1 = t1 + incr
- end
- return t2, t1
- end
- local function BE_toInt(bs, i)
- return blshift((bs[i] or 0), 24) + blshift((bs[i+1] or 0), 16) + blshift((bs[i+2] or 0), 8) + (bs[i+3] or 0)
- end
- local function preprocess(data)
- local len = #data
- local proc = {}
- data[#data+1] = 0x80
- while #data%64~=56 do data[#data+1] = 0 end
- local blocks = math.ceil(#data/64)
- for i = 1, blocks do
- proc[i] = {}
- for j = 1, 16 do
- proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4))
- end
- end
- proc[blocks][15], proc[blocks][16] = counter(len*8)
- return proc
- end
- local function digestblock(w, C)
- for j = 17, 64 do
- local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3))
- local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10))
- w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32
- end
- local a, b, c, d, e, f, g, h = upack(C)
- for j = 1, 64 do
- local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25))
- local ch = bxor(band(e, f), band(bnot(e), g))
- local temp1 = (h + S1 + ch + K[j] + w[j])%mod32
- local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22))
- local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c))
- local temp2 = (S0 + maj)%mod32
- h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32
- end
- C[1] = (C[1] + a)%mod32
- C[2] = (C[2] + b)%mod32
- C[3] = (C[3] + c)%mod32
- C[4] = (C[4] + d)%mod32
- C[5] = (C[5] + e)%mod32
- C[6] = (C[6] + f)%mod32
- C[7] = (C[7] + g)%mod32
- C[8] = (C[8] + h)%mod32
- return C
- end
- local function toBytes(t, n)
- local b = {}
- for i = 1, n do
- b[(i-1)*4+1] = band(brshift(t[i], 24), 0xFF)
- b[(i-1)*4+2] = band(brshift(t[i], 16), 0xFF)
- b[(i-1)*4+3] = band(brshift(t[i], 8), 0xFF)
- b[(i-1)*4+4] = band(t[i], 0xFF)
- end
- return setmetatable(b, byteTableMT)
- end
- local function digest(data)
- data = preprocess(strToByteArr(mapToStr(data or "")))
- local C = {upack(H)}
- for i = 1, #data do C = digestblock(data[i], C) end
- return toBytes(C, 8)
- end
- local function hmac(data, key)
- data = strToByteArr(mapToStr(data))
- key = mapToStr(key)
- local blocksize = 64
- key = #key > blocksize and digest(key) or strToByteArr(key)
- local ipad = {}
- local opad = {}
- local padded_key = {}
- for i = 1, blocksize do
- ipad[i] = bxor(0x36, key[i] or 0)
- opad[i] = bxor(0x5C, key[i] or 0)
- end
- for i = 1, #data do
- ipad[blocksize+i] = data[i]
- end
- ipad = digest(ipad)
- for i = 1, blocksize do
- padded_key[i] = opad[i]
- padded_key[blocksize+i] = ipad[i]
- end
- return digest(padded_key)
- end
- local function pbkdf2(pass, salt, iter, dklen)
- salt = strToByteArr(mapToStr(salt))
- local hashlen = 32
- iter = tonumber(iter) or 1000
- dklen = tonumber(dklen) or 32
- local block = 1
- local out = {}
- while dklen > 0 do
- local ikey = {}
- local isalt = {upack(salt)}
- local clen = dklen > hashlen and hashlen or dklen
- isalt[#isalt+1] = band(brshift(block, 24), 0xFF)
- isalt[#isalt+1] = band(brshift(block, 16), 0xFF)
- isalt[#isalt+1] = band(brshift(block, 8), 0xFF)
- isalt[#isalt+1] = band(block, 0xFF)
- for j = 1, iter do
- isalt = hmac(isalt, mapToStr(pass))
- for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end
- if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
- end
- dklen = dklen - clen
- block = block+1
- for k = 1, clen do out[#out+1] = ikey[k] end
- end
- return setmetatable(out, byteTableMT)
- end
- return {
- digest = digest,
- hmac = hmac,
- pbkdf2 = pbkdf2
- }
- end)()
- -- Chacha20 cipher in ComputerCraft
- -- By Anavrins
- -- For help and details, you can PM me on the CC forums
- -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact
- -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins
- -- http://pastebin.com/GPzf9JSa
- -- Last update: April 17, 2017
- local chacha20 = (function()
- local bxor = bit32.bxor
- local band = bit32.band
- local blshift = bit32.lshift
- local brshift = bit32.arshift
- local mod = 2^32
- local tau = {("expand 16-byte k"):byte(1,-1)}
- local sigma = {("expand 32-byte k"):byte(1,-1)}
- local function rotl(n, b)
- local s = n/(2^(32-b))
- local f = s%1
- return (s-f) + f*mod
- end
- local function quarterRound(s, a, b, c, d)
- s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 16)
- s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 12)
- s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 8)
- s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 7)
- return s
- end
- local function hashBlock(state, rnd)
- local s = {unpack(state)}
- for i = 1, rnd do
- local r = i%2==1
- s = r and quarterRound(s, 1, 5, 9, 13) or quarterRound(s, 1, 6, 11, 16)
- s = r and quarterRound(s, 2, 6, 10, 14) or quarterRound(s, 2, 7, 12, 13)
- s = r and quarterRound(s, 3, 7, 11, 15) or quarterRound(s, 3, 8, 9, 14)
- s = r and quarterRound(s, 4, 8, 12, 16) or quarterRound(s, 4, 5, 10, 15)
- end
- for i = 1, 16 do s[i] = (s[i]+state[i])%mod end
- return s
- end
- local function LE_toInt(bs, i)
- return (bs[i+1] or 0)+
- blshift((bs[i+2] or 0), 8)+
- blshift((bs[i+3] or 0), 16)+
- blshift((bs[i+4] or 0), 24)
- end
- local function initState(key, nonce, counter)
- local isKey256 = #key == 32
- local const = isKey256 and sigma or tau
- local state = {}
- state[ 1] = LE_toInt(const, 0)
- state[ 2] = LE_toInt(const, 4)
- state[ 3] = LE_toInt(const, 8)
- state[ 4] = LE_toInt(const, 12)
- state[ 5] = LE_toInt(key, 0)
- state[ 6] = LE_toInt(key, 4)
- state[ 7] = LE_toInt(key, 8)
- state[ 8] = LE_toInt(key, 12)
- state[ 9] = LE_toInt(key, isKey256 and 16 or 0)
- state[10] = LE_toInt(key, isKey256 and 20 or 4)
- state[11] = LE_toInt(key, isKey256 and 24 or 8)
- state[12] = LE_toInt(key, isKey256 and 28 or 12)
- state[13] = counter
- state[14] = LE_toInt(nonce, 0)
- state[15] = LE_toInt(nonce, 4)
- state[16] = LE_toInt(nonce, 8)
- return state
- end
- local function serialize(state)
- local r = {}
- for i = 1, 16 do
- r[#r+1] = band(state[i], 0xFF)
- r[#r+1] = band(brshift(state[i], 8), 0xFF)
- r[#r+1] = band(brshift(state[i], 16), 0xFF)
- r[#r+1] = band(brshift(state[i], 24), 0xFF)
- end
- return r
- end
- local function crypt(data, key, nonce, cntr, round)
- data = strToByteArr(mapToStr(data))
- key = strToByteArr(mapToStr(key))
- nonce = strToByteArr(mapToStr(nonce))
- assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32")
- assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12")
- cntr = tonumber(cntr) or 1
- round = tonumber(round) or 20
- local out = {}
- local state = initState(key, nonce, cntr)
- local blockAmt = math.floor(#data/64)
- for i = 0, blockAmt do
- local ks = serialize(hashBlock(state, round))
- state[13] = (state[13]+1) % mod
- local block = {}
- for j = 1, 64 do
- block[j] = data[((i)*64)+j]
- end
- for j = 1, #block do
- out[#out+1] = bxor(block[j], ks[j])
- end
- if i % 1000 == 0 then
- os.queueEvent("")
- os.pullEvent("")
- end
- end
- return setmetatable(out, byteTableMT)
- end
- return {
- crypt = crypt
- }
- end)()
- -- random.lua - Random Byte Generator
- local random = (function()
- local entropy = ""
- local accumulator = ""
- local entropyPath = "/.random"
- local function feed(data)
- accumulator = accumulator .. (data or "")
- end
- local function digest()
- entropy = tostring(sha256.digest(entropy .. accumulator))
- accumulator = ""
- end
- if fs.exists(entropyPath) then
- local entropyFile = fs.open(entropyPath, "rb")
- feed(entropyFile.readAll())
- entropyFile.close()
- end
- -- Add context.
- feed("init")
- feed(tostring(math.random(1, 2^31 - 1)))
- feed("|")
- feed(tostring(math.random(1, 2^31 - 1)))
- feed("|")
- feed(tostring(math.random(1, 2^4)))
- feed("|")
- feed(tostring(os.epoch("utc")))
- feed("|")
- feed(tostring({}))
- feed(tostring({}))
- digest()
- feed(tostring(os.epoch("utc")))
- digest()
- -- Add entropy by counting.
- local countTable = {}
- local inner = "function()return{" .. ("e'utc',"):rep(256) .. "}end"
- local countf = assert(load("local e=os.epoch return " .. inner))()
- for i = 1, 300 do
- while true do
- local t = countf()
- local t1 = t[1]
- if t1 ~= t[256] then
- for j = 1, 256 do
- if t1 ~= t[j] then
- countTable[i] = j - 1
- break
- end
- end
- break
- end
- end
- end
- feed(mapToStr(countTable))
- digest()
- local function save()
- feed("save")
- feed(tostring(os.epoch("utc")))
- feed(tostring({}))
- digest()
- local entropyFile = fs.open(entropyPath, "wb")
- entropyFile.write(tostring(sha256.hmac("save", entropy)))
- entropy = tostring(sha256.digest(entropy))
- entropyFile.close()
- end
- save()
- local function seed(data)
- feed("seed")
- feed(tostring(os.epoch("utc")))
- feed(tostring({}))
- feed(mapToStr(data))
- digest()
- save()
- end
- local function random()
- feed("random")
- feed(tostring(os.epoch("utc")))
- feed(tostring({}))
- digest()
- save()
- local result = sha256.hmac("out", entropy)
- entropy = tostring(sha256.digest(entropy))
- return result
- end
- return {
- seed = seed,
- save = save,
- random = random
- }
- end)()
- -- Big integer arithmetic for 168-bit (and 336-bit) numbers
- -- Numbers are represented as little-endian tables of 24-bit integers
- local arith = (function()
- local function isEqual(a, b)
- return (
- a[1] == b[1]
- and a[2] == b[2]
- and a[3] == b[3]
- and a[4] == b[4]
- and a[5] == b[5]
- and a[6] == b[6]
- and a[7] == b[7]
- )
- end
- local function compare(a, b)
- for i = 7, 1, -1 do
- if a[i] > b[i] then
- return 1
- elseif a[i] < b[i] then
- return -1
- end
- end
- return 0
- end
- local function add(a, b)
- -- c7 may be greater than 2^24 before reduction
- local c1 = a[1] + b[1]
- local c2 = a[2] + b[2]
- local c3 = a[3] + b[3]
- local c4 = a[4] + b[4]
- local c5 = a[5] + b[5]
- local c6 = a[6] + b[6]
- local c7 = a[7] + b[7]
- if c1 > 0xffffff then
- c2 = c2 + 1
- c1 = c1 - 0x1000000
- end
- if c2 > 0xffffff then
- c3 = c3 + 1
- c2 = c2 - 0x1000000
- end
- if c3 > 0xffffff then
- c4 = c4 + 1
- c3 = c3 - 0x1000000
- end
- if c4 > 0xffffff then
- c5 = c5 + 1
- c4 = c4 - 0x1000000
- end
- if c5 > 0xffffff then
- c6 = c6 + 1
- c5 = c5 - 0x1000000
- end
- if c6 > 0xffffff then
- c7 = c7 + 1
- c6 = c6 - 0x1000000
- end
- return {c1, c2, c3, c4, c5, c6, c7}
- end
- local function sub(a, b)
- -- c7 may be negative before reduction
- local c1 = a[1] - b[1]
- local c2 = a[2] - b[2]
- local c3 = a[3] - b[3]
- local c4 = a[4] - b[4]
- local c5 = a[5] - b[5]
- local c6 = a[6] - b[6]
- local c7 = a[7] - b[7]
- if c1 < 0 then
- c2 = c2 - 1
- c1 = c1 + 0x1000000
- end
- if c2 < 0 then
- c3 = c3 - 1
- c2 = c2 + 0x1000000
- end
- if c3 < 0 then
- c4 = c4 - 1
- c3 = c3 + 0x1000000
- end
- if c4 < 0 then
- c5 = c5 - 1
- c4 = c4 + 0x1000000
- end
- if c5 < 0 then
- c6 = c6 - 1
- c5 = c5 + 0x1000000
- end
- if c6 < 0 then
- c7 = c7 - 1
- c6 = c6 + 0x1000000
- end
- return {c1, c2, c3, c4, c5, c6, c7}
- end
- local function rShift(a)
- local c1 = a[1]
- local c2 = a[2]
- local c3 = a[3]
- local c4 = a[4]
- local c5 = a[5]
- local c6 = a[6]
- local c7 = a[7]
- c1 = c1 / 2
- c1 = c1 - c1 % 1
- c1 = c1 + (c2 % 2) * 0x800000
- c2 = c2 / 2
- c2 = c2 - c2 % 1
- c2 = c2 + (c3 % 2) * 0x800000
- c3 = c3 / 2
- c3 = c3 - c3 % 1
- c3 = c3 + (c4 % 2) * 0x800000
- c4 = c4 / 2
- c4 = c4 - c4 % 1
- c4 = c4 + (c5 % 2) * 0x800000
- c5 = c5 / 2
- c5 = c5 - c5 % 1
- c5 = c5 + (c6 % 2) * 0x800000
- c6 = c6 / 2
- c6 = c6 - c6 % 1
- c6 = c6 + (c7 % 2) * 0x800000
- c7 = c7 / 2
- c7 = c7 - c7 % 1
- return {c1, c2, c3, c4, c5, c6, c7}
- end
- local function addDouble(a, b)
- -- a and b are 336-bit integers (14 words)
- local c1 = a[1] + b[1]
- local c2 = a[2] + b[2]
- local c3 = a[3] + b[3]
- local c4 = a[4] + b[4]
- local c5 = a[5] + b[5]
- local c6 = a[6] + b[6]
- local c7 = a[7] + b[7]
- local c8 = a[8] + b[8]
- local c9 = a[9] + b[9]
- local c10 = a[10] + b[10]
- local c11 = a[11] + b[11]
- local c12 = a[12] + b[12]
- local c13 = a[13] + b[13]
- local c14 = a[14] + b[14]
- if c1 > 0xffffff then
- c2 = c2 + 1
- c1 = c1 - 0x1000000
- end
- if c2 > 0xffffff then
- c3 = c3 + 1
- c2 = c2 - 0x1000000
- end
- if c3 > 0xffffff then
- c4 = c4 + 1
- c3 = c3 - 0x1000000
- end
- if c4 > 0xffffff then
- c5 = c5 + 1
- c4 = c4 - 0x1000000
- end
- if c5 > 0xffffff then
- c6 = c6 + 1
- c5 = c5 - 0x1000000
- end
- if c6 > 0xffffff then
- c7 = c7 + 1
- c6 = c6 - 0x1000000
- end
- if c7 > 0xffffff then
- c8 = c8 + 1
- c7 = c7 - 0x1000000
- end
- if c8 > 0xffffff then
- c9 = c9 + 1
- c8 = c8 - 0x1000000
- end
- if c9 > 0xffffff then
- c10 = c10 + 1
- c9 = c9 - 0x1000000
- end
- if c10 > 0xffffff then
- c11 = c11 + 1
- c10 = c10 - 0x1000000
- end
- if c11 > 0xffffff then
- c12 = c12 + 1
- c11 = c11 - 0x1000000
- end
- if c12 > 0xffffff then
- c13 = c13 + 1
- c12 = c12 - 0x1000000
- end
- if c13 > 0xffffff then
- c14 = c14 + 1
- c13 = c13 - 0x1000000
- end
- return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
- end
- local function mult(a, b, half_multiply)
- local a1, a2, a3, a4, a5, a6, a7 = unpack(a)
- local b1, b2, b3, b4, b5, b6, b7 = unpack(b)
- local c1 = a1 * b1
- local c2 = a1 * b2 + a2 * b1
- local c3 = a1 * b3 + a2 * b2 + a3 * b1
- local c4 = a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1
- local c5 = a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1
- local c6 = a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1
- local c7 = a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2
- + a7 * b1
- local c8, c9, c10, c11, c12, c13, c14
- if not half_multiply then
- c8 = a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2
- c9 = a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3
- c10 = a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4
- c11 = a5 * b7 + a6 * b6 + a7 * b5
- c12 = a6 * b7 + a7 * b6
- c13 = a7 * b7
- c14 = 0
- else
- c8 = 0
- end
- local temp
- temp = c1
- c1 = c1 % 0x1000000
- c2 = c2 + (temp - c1) / 0x1000000
- temp = c2
- c2 = c2 % 0x1000000
- c3 = c3 + (temp - c2) / 0x1000000
- temp = c3
- c3 = c3 % 0x1000000
- c4 = c4 + (temp - c3) / 0x1000000
- temp = c4
- c4 = c4 % 0x1000000
- c5 = c5 + (temp - c4) / 0x1000000
- temp = c5
- c5 = c5 % 0x1000000
- c6 = c6 + (temp - c5) / 0x1000000
- temp = c6
- c6 = c6 % 0x1000000
- c7 = c7 + (temp - c6) / 0x1000000
- temp = c7
- c7 = c7 % 0x1000000
- if not half_multiply then
- c8 = c8 + (temp - c7) / 0x1000000
- temp = c8
- c8 = c8 % 0x1000000
- c9 = c9 + (temp - c8) / 0x1000000
- temp = c9
- c9 = c9 % 0x1000000
- c10 = c10 + (temp - c9) / 0x1000000
- temp = c10
- c10 = c10 % 0x1000000
- c11 = c11 + (temp - c10) / 0x1000000
- temp = c11
- c11 = c11 % 0x1000000
- c12 = c12 + (temp - c11) / 0x1000000
- temp = c12
- c12 = c12 % 0x1000000
- c13 = c13 + (temp - c12) / 0x1000000
- temp = c13
- c13 = c13 % 0x1000000
- c14 = c14 + (temp - c13) / 0x1000000
- end
- return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
- end
- local function square(a)
- -- returns a 336-bit integer (14 words)
- local a1, a2, a3, a4, a5, a6, a7 = unpack(a)
- local c1 = a1 * a1
- local c2 = a1 * a2 * 2
- local c3 = a1 * a3 * 2 + a2 * a2
- local c4 = a1 * a4 * 2 + a2 * a3 * 2
- local c5 = a1 * a5 * 2 + a2 * a4 * 2 + a3 * a3
- local c6 = a1 * a6 * 2 + a2 * a5 * 2 + a3 * a4 * 2
- local c7 = a1 * a7 * 2 + a2 * a6 * 2 + a3 * a5 * 2 + a4 * a4
- local c8 = a2 * a7 * 2 + a3 * a6 * 2 + a4 * a5 * 2
- local c9 = a3 * a7 * 2 + a4 * a6 * 2 + a5 * a5
- local c10 = a4 * a7 * 2 + a5 * a6 * 2
- local c11 = a5 * a7 * 2 + a6 * a6
- local c12 = a6 * a7 * 2
- local c13 = a7 * a7
- local c14 = 0
- local temp
- temp = c1
- c1 = c1 % 0x1000000
- c2 = c2 + (temp - c1) / 0x1000000
- temp = c2
- c2 = c2 % 0x1000000
- c3 = c3 + (temp - c2) / 0x1000000
- temp = c3
- c3 = c3 % 0x1000000
- c4 = c4 + (temp - c3) / 0x1000000
- temp = c4
- c4 = c4 % 0x1000000
- c5 = c5 + (temp - c4) / 0x1000000
- temp = c5
- c5 = c5 % 0x1000000
- c6 = c6 + (temp - c5) / 0x1000000
- temp = c6
- c6 = c6 % 0x1000000
- c7 = c7 + (temp - c6) / 0x1000000
- temp = c7
- c7 = c7 % 0x1000000
- c8 = c8 + (temp - c7) / 0x1000000
- temp = c8
- c8 = c8 % 0x1000000
- c9 = c9 + (temp - c8) / 0x1000000
- temp = c9
- c9 = c9 % 0x1000000
- c10 = c10 + (temp - c9) / 0x1000000
- temp = c10
- c10 = c10 % 0x1000000
- c11 = c11 + (temp - c10) / 0x1000000
- temp = c11
- c11 = c11 % 0x1000000
- c12 = c12 + (temp - c11) / 0x1000000
- temp = c12
- c12 = c12 % 0x1000000
- c13 = c13 + (temp - c12) / 0x1000000
- temp = c13
- c13 = c13 % 0x1000000
- c14 = c14 + (temp - c13) / 0x1000000
- return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
- end
- local function encodeInt(a)
- local enc = {}
- for i = 1, 7 do
- local word = a[i]
- for j = 1, 3 do
- enc[#enc + 1] = word % 256
- word = math.floor(word / 256)
- end
- end
- return enc
- end
- local function decodeInt(enc)
- local a = {}
- local encCopy = {}
- for i = 1, 21 do
- local byte = enc[i]
- assert(type(byte) == "number", "integer decoding failure")
- assert(byte >= 0 and byte <= 255, "integer decoding failure")
- assert(byte % 1 == 0, "integer decoding failure")
- encCopy[i] = byte
- end
- for i = 1, 21, 3 do
- local word = 0
- for j = 2, 0, -1 do
- word = word * 256
- word = word + encCopy[i + j]
- end
- a[#a + 1] = word
- end
- return a
- end
- local function mods(d, w)
- local result = d[1] % 2^w
- if result >= 2^(w - 1) then
- result = result - 2^w
- end
- return result
- end
- -- Represents a 168-bit number as the (2^w)-ary Non-Adjacent Form
- local function NAF(d, w)
- local t = {}
- local d = {unpack(d)}
- for _ = 1, 168 do
- if d[1] % 2 == 1 then
- t[#t + 1] = mods(d, w)
- d = sub(d, {t[#t], 0, 0, 0, 0, 0, 0})
- else
- t[#t + 1] = 0
- end
- d = rShift(d)
- end
- return t
- end
- return {
- isEqual = isEqual,
- compare = compare,
- add = add,
- sub = sub,
- addDouble = addDouble,
- mult = mult,
- square = square,
- encodeInt = encodeInt,
- decodeInt = decodeInt,
- NAF = NAF
- }
- end)()
- -- Arithmetic on the finite field of integers modulo p
- -- Where p is the finite field modulus
- local modp = (function()
- local add = arith.add
- local sub = arith.sub
- local addDouble = arith.addDouble
- local mult = arith.mult
- local square = arith.square
- local p = {3, 0, 0, 0, 0, 0, 15761408}
- -- We're using the Montgomery Reduction for fast modular multiplication.
- -- https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
- -- r = 2^168
- -- p * pInverse = -1 (mod r)
- -- r2 = r * r (mod p)
- local pInverse = {5592405, 5592405, 5592405, 5592405, 5592405, 5592405, 14800213}
- local r2 = {13533400, 837116, 6278376, 13533388, 837116, 6278376, 7504076}
- local function multByP(a)
- local a1, a2, a3, a4, a5, a6, a7 = unpack(a)
- local c1 = a1 * 3
- local c2 = a2 * 3
- local c3 = a3 * 3
- local c4 = a4 * 3
- local c5 = a5 * 3
- local c6 = a6 * 3
- local c7 = a1 * 15761408
- c7 = c7 + a7 * 3
- local c8 = a2 * 15761408
- local c9 = a3 * 15761408
- local c10 = a4 * 15761408
- local c11 = a5 * 15761408
- local c12 = a6 * 15761408
- local c13 = a7 * 15761408
- local c14 = 0
- local temp
- temp = c1 / 0x1000000
- c2 = c2 + (temp - temp % 1)
- c1 = c1 % 0x1000000
- temp = c2 / 0x1000000
- c3 = c3 + (temp - temp % 1)
- c2 = c2 % 0x1000000
- temp = c3 / 0x1000000
- c4 = c4 + (temp - temp % 1)
- c3 = c3 % 0x1000000
- temp = c4 / 0x1000000
- c5 = c5 + (temp - temp % 1)
- c4 = c4 % 0x1000000
- temp = c5 / 0x1000000
- c6 = c6 + (temp - temp % 1)
- c5 = c5 % 0x1000000
- temp = c6 / 0x1000000
- c7 = c7 + (temp - temp % 1)
- c6 = c6 % 0x1000000
- temp = c7 / 0x1000000
- c8 = c8 + (temp - temp % 1)
- c7 = c7 % 0x1000000
- temp = c8 / 0x1000000
- c9 = c9 + (temp - temp % 1)
- c8 = c8 % 0x1000000
- temp = c9 / 0x1000000
- c10 = c10 + (temp - temp % 1)
- c9 = c9 % 0x1000000
- temp = c10 / 0x1000000
- c11 = c11 + (temp - temp % 1)
- c10 = c10 % 0x1000000
- temp = c11 / 0x1000000
- c12 = c12 + (temp - temp % 1)
- c11 = c11 % 0x1000000
- temp = c12 / 0x1000000
- c13 = c13 + (temp - temp % 1)
- c12 = c12 % 0x1000000
- temp = c13 / 0x1000000
- c14 = c14 + (temp - temp % 1)
- c13 = c13 % 0x1000000
- return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
- end
- -- Reduces a number from [0, 2p - 1] to [0, p - 1]
- local function reduceModP(a)
- -- a < p
- if a[7] < 15761408 or a[7] == 15761408 and a[1] < 3 then
- return {unpack(a)}
- end
- -- a > p
- local c1 = a[1]
- local c2 = a[2]
- local c3 = a[3]
- local c4 = a[4]
- local c5 = a[5]
- local c6 = a[6]
- local c7 = a[7]
- c1 = c1 - 3
- c7 = c7 - 15761408
- if c1 < 0 then
- c2 = c2 - 1
- c1 = c1 + 0x1000000
- end
- if c2 < 0 then
- c3 = c3 - 1
- c2 = c2 + 0x1000000
- end
- if c3 < 0 then
- c4 = c4 - 1
- c3 = c3 + 0x1000000
- end
- if c4 < 0 then
- c5 = c5 - 1
- c4 = c4 + 0x1000000
- end
- if c5 < 0 then
- c6 = c6 - 1
- c5 = c5 + 0x1000000
- end
- if c6 < 0 then
- c7 = c7 - 1
- c6 = c6 + 0x1000000
- end
- return {c1, c2, c3, c4, c5, c6, c7}
- end
- local function addModP(a, b)
- return reduceModP(add(a, b))
- end
- local function subModP(a, b)
- local result = sub(a, b)
- if result[7] < 0 then
- result = add(result, p)
- end
- return result
- end
- -- Montgomery REDC algorithn
- -- Reduces a number from [0, p^2 - 1] to [0, p - 1]
- local function REDC(T)
- local m = mult(T, pInverse, true)
- local t = {unpack(addDouble(T, multByP(m)), 8, 14)}
- return reduceModP(t)
- end
- local function multModP(a, b)
- -- Only works with a, b in Montgomery form
- return REDC(mult(a, b))
- end
- local function squareModP(a)
- -- Only works with a in Montgomery form
- return REDC(square(a))
- end
- local function montgomeryModP(a)
- return multModP(a, r2)
- end
- local function inverseMontgomeryModP(a)
- a = {unpack(a)}
- for i = 8, 14 do
- a[i] = 0
- end
- return REDC(a)
- end
- local ONE = montgomeryModP({1, 0, 0, 0, 0, 0, 0})
- local function expModP(base, exponentBinary)
- base = {unpack(base)}
- local result = {unpack(ONE)}
- for i = 1, 168 do
- if exponentBinary[i] == 1 then
- result = multModP(result, base)
- end
- base = squareModP(base)
- end
- return result
- end
- return {
- addModP = addModP,
- subModP = subModP,
- multModP = multModP,
- squareModP = squareModP,
- montgomeryModP = montgomeryModP,
- inverseMontgomeryModP = inverseMontgomeryModP,
- expModP = expModP
- }
- end)()
- -- Arithmetic on the Finite Field of Integers modulo q
- -- Where q is the generator's subgroup order.
- local modq = (function()
- local isEqual = arith.isEqual
- local compare = arith.compare
- local add = arith.add
- local sub = arith.sub
- local addDouble = arith.addDouble
- local mult = arith.mult
- local square = arith.square
- local encodeInt = arith.encodeInt
- local decodeInt = arith.decodeInt
- local modQMT
- local q = {9622359, 6699217, 13940450, 16775734, 16777215, 16777215, 3940351}
- local qMinusTwoBinary = {1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 1}
- -- We're using the Montgomery Reduction for fast modular multiplication.
- -- https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
- -- r = 2^168
- -- q * qInverse = -1 (mod r)
- -- r2 = r * r (mod q)
- local qInverse = {15218585, 5740955, 3271338, 9903997, 9067368, 7173545, 6988392}
- local r2 = {1336213, 11071705, 9716828, 11083885, 9188643, 1494868, 3306114}
- -- Reduces a number from [0, 2q - 1] to [0, q - 1]
- local function reduceModQ(a)
- local result = {unpack(a)}
- if compare(result, q) >= 0 then
- result = sub(result, q)
- end
- return setmetatable(result, modQMT)
- end
- local function addModQ(a, b)
- return reduceModQ(add(a, b))
- end
- local function subModQ(a, b)
- local result = sub(a, b)
- if result[7] < 0 then
- result = add(result, q)
- end
- return setmetatable(result, modQMT)
- end
- -- Montgomery REDC algorithn
- -- Reduces a number from [0, q^2 - 1] to [0, q - 1]
- local function REDC(T)
- local m = {unpack(mult({unpack(T, 1, 7)}, qInverse, true), 1, 7)}
- local t = {unpack(addDouble(T, mult(m, q)), 8, 14)}
- return reduceModQ(t)
- end
- local function multModQ(a, b)
- -- Only works with a, b in Montgomery form
- return REDC(mult(a, b))
- end
- local function squareModQ(a)
- -- Only works with a in Montgomery form
- return REDC(square(a))
- end
- local function montgomeryModQ(a)
- return multModQ(a, r2)
- end
- local function inverseMontgomeryModQ(a)
- local a = {unpack(a)}
- for i = 8, 14 do
- a[i] = 0
- end
- return REDC(a)
- end
- local ONE = montgomeryModQ({1, 0, 0, 0, 0, 0, 0})
- local function expModQ(base, exponentBinary)
- local base = {unpack(base)}
- local result = {unpack(ONE)}
- for i = 1, 168 do
- if exponentBinary[i] == 1 then
- result = multModQ(result, base)
- end
- base = squareModQ(base)
- end
- return result
- end
- local function intExpModQ(base, exponent)
- local base = {unpack(base)}
- local result = setmetatable({unpack(ONE)}, modQMT)
- if exponent < 0 then
- base = expModQ(base, qMinusTwoBinary)
- exponent = -exponent
- end
- while exponent > 0 do
- if exponent % 2 == 1 then
- result = multModQ(result, base)
- end
- base = squareModQ(base)
- exponent = exponent / 2
- exponent = exponent - exponent % 1
- end
- return result
- end
- local function encodeModQ(a)
- local result = encodeInt(a)
- return setmetatable(result, byteTableMT)
- end
- local function decodeModQ(s)
- local result = decodeInt(strToByteArr(mapToStr(s):sub(1, 21)))
- result[7] = result[7] % q[7]
- return setmetatable(result, modQMT)
- end
- local function randomModQ()
- while true do
- local s = {unpack(random.random(), 1, 21)}
- local result = decodeInt(s)
- if result[7] < q[7] then
- return setmetatable(result, modQMT)
- end
- end
- end
- local function hashModQ(data)
- return decodeModQ(sha256.digest(data))
- end
- modQMT = {
- __index = {
- encode = function(self)
- return encodeModQ(self)
- end
- },
- __tostring = function(self)
- return self:encode():toHex()
- end,
- __add = function(self, other)
- if type(self) == "number" then
- return other + self
- end
- if type(other) == "number" then
- assert(other < 2^24, "number operand too big")
- other = montgomeryModQ({other, 0, 0, 0, 0, 0, 0})
- end
- return addModQ(self, other)
- end,
- __sub = function(a, b)
- if type(a) == "number" then
- assert(a < 2^24, "number operand too big")
- a = montgomeryModQ({a, 0, 0, 0, 0, 0, 0})
- end
- if type(b) == "number" then
- assert(b < 2^24, "number operand too big")
- b = montgomeryModQ({b, 0, 0, 0, 0, 0, 0})
- end
- return subModQ(a, b)
- end,
- __unm = function(self)
- return subModQ(q, self)
- end,
- __eq = function(self, other)
- return isEqual(self, other)
- end,
- __mul = function(self, other)
- if type(self) == "number" then
- return other * self
- end
- -- EC point
- -- Use the point's metatable to handle multiplication
- if type(other) == "table" and type(other[1]) == "table" then
- return other * self
- end
- if type(other) == "number" then
- assert(other < 2^24, "number operand too big")
- other = montgomeryModQ({other, 0, 0, 0, 0, 0, 0})
- end
- return multModQ(self, other)
- end,
- __div = function(a, b)
- if type(a) == "number" then
- assert(a < 2^24, "number operand too big")
- a = montgomeryModQ({a, 0, 0, 0, 0, 0, 0})
- end
- if type(b) == "number" then
- assert(b < 2^24, "number operand too big")
- b = montgomeryModQ({b, 0, 0, 0, 0, 0, 0})
- end
- local bInv = expModQ(b, qMinusTwoBinary)
- return multModQ(a, bInv)
- end,
- __pow = function(self, other)
- return intExpModQ(self, other)
- end
- }
- return {
- hashModQ = hashModQ,
- randomModQ = randomModQ,
- decodeModQ = decodeModQ,
- inverseMontgomeryModQ = inverseMontgomeryModQ
- }
- end)()
- -- Elliptic curve arithmetic
- local curve = (function()
- ---- About the Curve Itself
- -- Field Size: 168 bits
- -- Field Modulus (p): 481 * 2^159 + 3
- -- Equation: x^2 + y^2 = 1 + 122 * x^2 * y^2
- -- Parameters: Edwards Curve with d = 122
- -- Curve Order (n): 351491143778082151827986174289773107581916088585564
- -- Cofactor (h): 4
- -- Generator Order (q): 87872785944520537956996543572443276895479022146391
- ---- About the Curve's Security
- -- Current best attack security: 81.777 bits (Small Subgroup + Rho)
- -- Rho Security: log2(0.884 * sqrt(q)) = 82.777 bits
- -- Transfer Security? Yes: p ~= q; k > 20
- -- Field Discriminant Security? Yes:
- -- t = 27978492958645335688000168
- -- s = 10
- -- |D| = 6231685068753619775430107799412237267322159383147 > 2^100
- -- Rigidity? No, not at all.
- -- XZ/YZ Ladder Security? No: Single coordinate ladders are insecure.
- -- Small Subgroup Security? No.
- -- Invalid Curve Security? Yes: Points are checked before every operation.
- -- Invalid Curve Twist Security? No: Don't use single coordinate ladders.
- -- Completeness? Yes: The curve is complete.
- -- Indistinguishability? Yes (Elligator 2), but not implemented.
- local isEqual = arith.isEqual
- local NAF = arith.NAF
- local encodeInt = arith.encodeInt
- local decodeInt = arith.decodeInt
- local multModP = modp.multModP
- local squareModP = modp.squareModP
- local addModP = modp.addModP
- local subModP = modp.subModP
- local montgomeryModP = modp.montgomeryModP
- local expModP = modp.expModP
- local inverseMontgomeryModQ = modq.inverseMontgomeryModQ
- local pointMT
- local ZERO = {0, 0, 0, 0, 0, 0, 0}
- local ONE = montgomeryModP({1, 0, 0, 0, 0, 0, 0})
- -- Curve Parameters
- local d = montgomeryModP({122, 0, 0, 0, 0, 0, 0})
- local p = {3, 0, 0, 0, 0, 0, 15761408}
- local pMinusTwoBinary = {1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1}
- local pMinusThreeOverFourBinary = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1}
- local G = {
- {6636044, 10381432, 15741790, 2914241, 5785600, 264923, 4550291},
- {13512827, 8449886, 5647959, 1135556, 5489843, 7177356, 8002203},
- {unpack(ONE)}
- }
- local O = {
- {unpack(ZERO)},
- {unpack(ONE)},
- {unpack(ONE)}
- }
- -- Projective Coordinates for Edwards curves for point addition/doubling.
- -- Points are represented as: (X:Y:Z) where x = X/Z and y = Y/Z
- -- The identity element is represented by (0:1:1)
- -- Point operation formulas are available on the EFD:
- -- https://www.hyperelliptic.org/EFD/g1p/auto-edwards-projective.html
- local function pointDouble(P1)
- -- 3M + 4S
- local X1, Y1, Z1 = unpack(P1)
- local b = addModP(X1, Y1)
- local B = squareModP(b)
- local C = squareModP(X1)
- local D = squareModP(Y1)
- local E = addModP(C, D)
- local H = squareModP(Z1)
- local J = subModP(E, addModP(H, H))
- local X3 = multModP(subModP(B, E), J)
- local Y3 = multModP(E, subModP(C, D))
- local Z3 = multModP(E, J)
- local P3 = {X3, Y3, Z3}
- return setmetatable(P3, pointMT)
- end
- local function pointAdd(P1, P2)
- -- 10M + 1S
- local X1, Y1, Z1 = unpack(P1)
- local X2, Y2, Z2 = unpack(P2)
- local A = multModP(Z1, Z2)
- local B = squareModP(A)
- local C = multModP(X1, X2)
- local D = multModP(Y1, Y2)
- local E = multModP(d, multModP(C, D))
- local F = subModP(B, E)
- local G = addModP(B, E)
- local X3 = multModP(A, multModP(F, subModP(multModP(addModP(X1, Y1), addModP(X2, Y2)), addModP(C, D))))
- local Y3 = multModP(A, multModP(G, subModP(D, C)))
- local Z3 = multModP(F, G)
- local P3 = {X3, Y3, Z3}
- return setmetatable(P3, pointMT)
- end
- local function pointNeg(P1)
- local X1, Y1, Z1 = unpack(P1)
- local X3 = subModP(ZERO, X1)
- local Y3 = {unpack(Y1)}
- local Z3 = {unpack(Z1)}
- local P3 = {X3, Y3, Z3}
- return setmetatable(P3, pointMT)
- end
- local function pointSub(P1, P2)
- return pointAdd(P1, pointNeg(P2))
- end
- -- Converts (X:Y:Z) into (X:Y:1) = (x:y:1)
- local function pointScale(P1)
- local X1, Y1, Z1 = unpack(P1)
- local A = expModP(Z1, pMinusTwoBinary)
- local X3 = multModP(X1, A)
- local Y3 = multModP(Y1, A)
- local Z3 = {unpack(ONE)}
- local P3 = {X3, Y3, Z3}
- return setmetatable(P3, pointMT)
- end
- local function pointIsEqual(P1, P2)
- local X1, Y1, Z1 = unpack(P1)
- local X2, Y2, Z2 = unpack(P2)
- local A1 = multModP(X1, Z2)
- local B1 = multModP(Y1, Z2)
- local A2 = multModP(X2, Z1)
- local B2 = multModP(Y2, Z1)
- return isEqual(A1, A2) and isEqual(B1, B2)
- end
- -- Checks if a projective point satisfies the curve equation
- local function pointIsOnCurve(P1)
- local X1, Y1, Z1 = unpack(P1)
- local X12 = squareModP(X1)
- local Y12 = squareModP(Y1)
- local Z12 = squareModP(Z1)
- local Z14 = squareModP(Z12)
- local a = addModP(X12, Y12)
- a = multModP(a, Z12)
- local b = multModP(d, multModP(X12, Y12))
- b = addModP(Z14, b)
- return isEqual(a, b)
- end
- local function pointIsInf(P1)
- return isEqual(P1[1], ZERO)
- end
- -- W-ary Non-Adjacent Form (wNAF) method for scalar multiplication:
- -- https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#w-ary_non-adjacent_form_(wNAF)_method
- local function scalarMult(multiplier, P1)
- -- w = 5
- local naf = NAF(multiplier, 5)
- local PTable = {P1}
- local P2 = pointDouble(P1)
- local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}}
- for i = 3, 31, 2 do
- PTable[i] = pointAdd(PTable[i - 2], P2)
- end
- for i = #naf, 1, -1 do
- Q = pointDouble(Q)
- if naf[i] > 0 then
- Q = pointAdd(Q, PTable[naf[i]])
- elseif naf[i] < 0 then
- Q = pointSub(Q, PTable[-naf[i]])
- end
- end
- return setmetatable(Q, pointMT)
- end
- -- Lookup table 4-ary NAF method for scalar multiplication by G.
- -- Precomputations for the regular NAF method are done before the multiplication.
- local GTable = {G}
- for i = 2, 168 do
- GTable[i] = pointDouble(GTable[i - 1])
- end
- local function scalarMultG(multiplier)
- local naf = NAF(multiplier, 2)
- local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}}
- for i = 1, 168 do
- if naf[i] == 1 then
- Q = pointAdd(Q, GTable[i])
- elseif naf[i] == -1 then
- Q = pointSub(Q, GTable[i])
- end
- end
- return setmetatable(Q, pointMT)
- end
- -- Point compression and encoding.
- -- Compresses curve points to 22 bytes.
- local function pointEncode(P1)
- P1 = pointScale(P1)
- local result = {}
- local x, y = unpack(P1)
- -- Encode y
- result = encodeInt(y)
- -- Encode one bit from x
- result[22] = x[1] % 2
- return setmetatable(result, byteTableMT)
- end
- local function pointDecode(enc)
- enc = strToByteArr(mapToStr(enc):sub(1, 22))
- -- Decode y
- local y = decodeInt(enc)
- y[7] = y[7] % p[7]
- -- Find {x, -x} using curve equation
- local y2 = squareModP(y)
- local u = subModP(y2, ONE)
- local v = subModP(multModP(d, y2), ONE)
- local u2 = squareModP(u)
- local u3 = multModP(u, u2)
- local u5 = multModP(u3, u2)
- local v3 = multModP(v, squareModP(v))
- local w = multModP(u5, v3)
- local x = multModP(u3, multModP(v, expModP(w, pMinusThreeOverFourBinary)))
- -- Use enc[22] to find x from {x, -x}
- if x[1] % 2 ~= enc[22] then
- x = subModP(ZERO, x)
- end
- local P3 = {x, y, {unpack(ONE)}}
- return setmetatable(P3, pointMT)
- end
- pointMT = {
- __index = {
- isOnCurve = function(self)
- return pointIsOnCurve(self)
- end,
- isInf = function(self)
- return self:isOnCurve() and pointIsInf(self)
- end,
- encode = function(self)
- return pointEncode(self)
- end
- },
- __tostring = function(self)
- return self:encode():toHex()
- end,
- __add = function(P1, P2)
- assert(P1:isOnCurve(), "invalid point")
- assert(P2:isOnCurve(), "invalid point")
- return pointAdd(P1, P2)
- end,
- __sub = function(P1, P2)
- assert(P1:isOnCurve(), "invalid point")
- assert(P2:isOnCurve(), "invalid point")
- return pointSub(P1, P2)
- end,
- __unm = function(self)
- assert(self:isOnCurve(), "invalid point")
- return pointNeg(self)
- end,
- __eq = function(P1, P2)
- assert(P1:isOnCurve(), "invalid point")
- assert(P2:isOnCurve(), "invalid point")
- return pointIsEqual(P1, P2)
- end,
- __mul = function(P1, s)
- if type(P1) == "number" then
- return s * P1
- end
- if type(s) == "number" then
- assert(s < 2^24, "number multiplier too big")
- s = {s, 0, 0, 0, 0, 0, 0}
- else
- s = inverseMontgomeryModQ(s)
- end
- if P1 == G then
- return scalarMultG(s)
- else
- return scalarMult(s, P1)
- end
- end
- }
- G = setmetatable(G, pointMT)
- O = setmetatable(O, pointMT)
- return {
- G = G,
- O = O,
- pointDecode = pointDecode
- }
- end)()
- local function getNonceFromEpoch()
- local nonce = {}
- local epoch = os.epoch("utc")
- for _ = 1, 12 do
- nonce[#nonce + 1] = epoch % 256
- epoch = epoch / 256
- epoch = epoch - epoch % 1
- end
- return nonce
- end
- local function encrypt(data, key)
- key = mapToStr(key)
- local encKey = sha256.hmac("encKey", key)
- local macKey = sha256.hmac("macKey", key)
- local nonce = getNonceFromEpoch()
- local ciphertext = chacha20.crypt(mapToStr(data), encKey, nonce)
- local result = nonce
- for i = 1, #ciphertext do
- result[#result + 1] = ciphertext[i]
- end
- local mac = sha256.hmac(result, macKey)
- for i = 1, #mac do
- result[#result + 1] = mac[i]
- end
- return setmetatable(result, byteTableMT)
- end
- local function decrypt(data, key)
- data = mapToStr(data)
- key = mapToStr(key)
- local encKey = sha256.hmac("encKey", key)
- local macKey = sha256.hmac("macKey", key)
- local mac = sha256.hmac(data:sub(1, -33), macKey)
- assert(mac:isEqual(strToByteArr(data:sub(-32))), "invalid mac")
- local result = chacha20.crypt(data:sub(13, -33), encKey, data:sub(1, 12))
- return setmetatable(result, byteTableMT)
- end
- local function keypair(seed)
- local x
- if seed then
- x = modq.hashModQ(mapToStr(seed))
- else
- x = modq.randomModQ()
- end
- local Y = curve.G * x
- local privateKey = x:encode()
- local publicKey = Y:encode()
- return privateKey, publicKey
- end
- local function exchange(privateKey, publicKey)
- local x = modq.decodeModQ(mapToStr(privateKey))
- local Y = curve.pointDecode(mapToStr(publicKey))
- local Z = Y * x
- local sharedSecret = sha256.digest(Z:encode())
- return sharedSecret
- end
- local function sign(privateKey, message)
- local x = modq.decodeModQ(mapToStr(privateKey))
- local k = modq.randomModQ()
- local R = curve.G * k
- local e = modq.hashModQ(mapToStr(message) .. tostring(R))
- local s = k - x * e
- e = e:encode()
- s = s:encode()
- local result = e
- for i = 1, #s do
- result[#result + 1] = s[i]
- end
- return setmetatable(result, byteTableMT)
- end
- local function verify(publicKey, message, signature)
- signature = mapToStr(signature)
- local Y = curve.pointDecode(mapToStr(publicKey))
- local e = modq.decodeModQ(signature:sub(1, 21))
- local s = modq.decodeModQ(signature:sub(22))
- local Rv = curve.G * s + Y * e
- local ev = modq.hashModQ(mapToStr(message) .. tostring(Rv))
- return ev == e
- end
- return {
- chacha20 = chacha20,
- sha256 = sha256,
- random = random,
- encrypt = encrypt,
- decrypt = decrypt,
- keypair = keypair,
- exchange = exchange,
- sign = sign,
- verify = verify
- }
Advertisement
Comments
-
- the decrypt function dose not work continues to say invalid mac don't know why
Add Comment
Please, Sign In to add comment
Advertisement