SHOW:
|
|
- or go back to the newest paste.
| 1 | -- Elliptic Curve Cryptography in Computercraft | |
| 2 | ||
| 3 | ---- Update (Jun 7 2023) | |
| 4 | -- Fix string inputs not working on signatures | |
| 5 | -- Switch internal byte arrays to strings on most places | |
| 6 | -- Improve entropy gathering by using counting | |
| 7 | -- Other general improvements to syntax | |
| 8 | ---- Update (Jun 4 2021) | |
| 9 | -- Fix compatibility with CraftOS-PC | |
| 10 | ---- Update (Jul 30 2020) | |
| 11 | -- Make randomModQ and use it instead of hashing from random.random() | |
| 12 | ---- Update (Feb 10 2020) | |
| 13 | -- Make a more robust encoding/decoding implementation | |
| 14 | ---- Update (Dec 30 2019) | |
| 15 | -- Fix rng not accumulating entropy from loop | |
| 16 | -- (older versions should be fine from other sources + stored in disk) | |
| 17 | ---- Update (Dec 28 2019) | |
| 18 | -- Slightly better integer multiplication and squaring | |
| 19 | -- Fix global variable declarations in modQ division and verify() (no security concerns) | |
| 20 | -- Small tweaks from SquidDev's illuaminate (https://github.com/SquidDev/illuaminate/) | |
| 21 | ||
| 22 | local function mapToStr(t) | |
| 23 | if type(t) == "table" then | |
| 24 | return string.char(unpack(t)) | |
| 25 | else | |
| 26 | return tostring(t) | |
| 27 | end | |
| 28 | end | |
| 29 | ||
| 30 | local byteTableMT = {
| |
| 31 | __tostring = mapToStr, | |
| 32 | __index = {
| |
| 33 | toHex = function(self) return ("%02x"):rep(#self):format(unpack(self)) end,
| |
| 34 | isEqual = function(self, t) | |
| 35 | if type(t) ~= "table" then return false end | |
| 36 | if #self ~= #t then return false end | |
| 37 | local ret = 0 | |
| 38 | for i = 1, #self do | |
| 39 | ret = bit32.bor(ret, bit32.bxor(self[i], t[i])) | |
| 40 | end | |
| 41 | return ret == 0 | |
| 42 | end | |
| 43 | } | |
| 44 | } | |
| 45 | ||
| 46 | local function strToByteArr(s) | |
| 47 | return setmetatable({s:byte(1, -1)}, byteTableMT)
| |
| 48 | end | |
| 49 | ||
| 50 | -- SHA-256, HMAC and PBKDF2 functions in ComputerCraft | |
| 51 | -- By Anavrins | |
| 52 | -- For help and details, you can PM me on the CC forums | |
| 53 | -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact | |
| 54 | -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins | |
| 55 | -- http://pastebin.com/6UV4qfNF | |
| 56 | -- Last update: October 10, 2017 | |
| 57 | local sha256 = (function() | |
| 58 | local mod32 = 2^32 | |
| 59 | local band = bit32 and bit32.band or bit.band | |
| 60 | local bnot = bit32 and bit32.bnot or bit.bnot | |
| 61 | local bxor = bit32 and bit32.bxor or bit.bxor | |
| 62 | local blshift = bit32 and bit32.lshift or bit.blshift | |
| 63 | local upack = unpack | |
| 64 | ||
| 65 | local function rrotate(n, b) | |
| 66 | local s = n/(2^b) | |
| 67 | local f = s%1 | |
| 68 | return (s-f) + f*mod32 | |
| 69 | end | |
| 70 | local function brshift(int, by) -- Thanks bit32 for bad rshift | |
| 71 | local s = int / (2^by) | |
| 72 | return s - s%1 | |
| 73 | end | |
| 74 | ||
| 75 | local H = {
| |
| 76 | 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a, | |
| 77 | 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19, | |
| 78 | } | |
| 79 | ||
| 80 | local K = {
| |
| 81 | 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5, | |
| 82 | 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174, | |
| 83 | 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da, | |
| 84 | 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967, | |
| 85 | 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85, | |
| 86 | 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070, | |
| 87 | 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3, | |
| 88 | 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2, | |
| 89 | } | |
| 90 | ||
| 91 | local function counter(incr) | |
| 92 | local t1, t2 = 0, 0 | |
| 93 | if 0xFFFFFFFF - t1 < incr then | |
| 94 | t2 = t2 + 1 | |
| 95 | t1 = incr - (0xFFFFFFFF - t1) - 1 | |
| 96 | else t1 = t1 + incr | |
| 97 | end | |
| 98 | return t2, t1 | |
| 99 | end | |
| 100 | ||
| 101 | local function BE_toInt(bs, i) | |
| 102 | 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) | |
| 103 | end | |
| 104 | ||
| 105 | local function preprocess(data) | |
| 106 | local len = #data | |
| 107 | local proc = {}
| |
| 108 | data[#data+1] = 0x80 | |
| 109 | while #data%64~=56 do data[#data+1] = 0 end | |
| 110 | local blocks = math.ceil(#data/64) | |
| 111 | for i = 1, blocks do | |
| 112 | proc[i] = {}
| |
| 113 | for j = 1, 16 do | |
| 114 | proc[i][j] = BE_toInt(data, 1+((i-1)*64)+((j-1)*4)) | |
| 115 | end | |
| 116 | end | |
| 117 | proc[blocks][15], proc[blocks][16] = counter(len*8) | |
| 118 | return proc | |
| 119 | end | |
| 120 | ||
| 121 | local function digestblock(w, C) | |
| 122 | for j = 17, 64 do | |
| 123 | local s0 = bxor(bxor(rrotate(w[j-15], 7), rrotate(w[j-15], 18)), brshift(w[j-15], 3)) | |
| 124 | local s1 = bxor(bxor(rrotate(w[j-2], 17), rrotate(w[j-2], 19)), brshift(w[j-2], 10)) | |
| 125 | w[j] = (w[j-16] + s0 + w[j-7] + s1)%mod32 | |
| 126 | end | |
| 127 | local a, b, c, d, e, f, g, h = upack(C) | |
| 128 | for j = 1, 64 do | |
| 129 | local S1 = bxor(bxor(rrotate(e, 6), rrotate(e, 11)), rrotate(e, 25)) | |
| 130 | local ch = bxor(band(e, f), band(bnot(e), g)) | |
| 131 | local temp1 = (h + S1 + ch + K[j] + w[j])%mod32 | |
| 132 | local S0 = bxor(bxor(rrotate(a, 2), rrotate(a, 13)), rrotate(a, 22)) | |
| 133 | local maj = bxor(bxor(band(a, b), band(a, c)), band(b, c)) | |
| 134 | local temp2 = (S0 + maj)%mod32 | |
| 135 | h, g, f, e, d, c, b, a = g, f, e, (d+temp1)%mod32, c, b, a, (temp1+temp2)%mod32 | |
| 136 | end | |
| 137 | C[1] = (C[1] + a)%mod32 | |
| 138 | C[2] = (C[2] + b)%mod32 | |
| 139 | C[3] = (C[3] + c)%mod32 | |
| 140 | C[4] = (C[4] + d)%mod32 | |
| 141 | C[5] = (C[5] + e)%mod32 | |
| 142 | C[6] = (C[6] + f)%mod32 | |
| 143 | C[7] = (C[7] + g)%mod32 | |
| 144 | C[8] = (C[8] + h)%mod32 | |
| 145 | return C | |
| 146 | end | |
| 147 | ||
| 148 | local function toBytes(t, n) | |
| 149 | local b = {}
| |
| 150 | for i = 1, n do | |
| 151 | b[(i-1)*4+1] = band(brshift(t[i], 24), 0xFF) | |
| 152 | b[(i-1)*4+2] = band(brshift(t[i], 16), 0xFF) | |
| 153 | b[(i-1)*4+3] = band(brshift(t[i], 8), 0xFF) | |
| 154 | b[(i-1)*4+4] = band(t[i], 0xFF) | |
| 155 | end | |
| 156 | return setmetatable(b, byteTableMT) | |
| 157 | end | |
| 158 | ||
| 159 | local function digest(data) | |
| 160 | data = preprocess(strToByteArr(mapToStr(data or ""))) | |
| 161 | local C = {upack(H)}
| |
| 162 | for i = 1, #data do C = digestblock(data[i], C) end | |
| 163 | return toBytes(C, 8) | |
| 164 | end | |
| 165 | ||
| 166 | local function hmac(data, key) | |
| 167 | data = strToByteArr(mapToStr(data)) | |
| 168 | key = mapToStr(key) | |
| 169 | ||
| 170 | local blocksize = 64 | |
| 171 | ||
| 172 | key = #key > blocksize and digest(key) or strToByteArr(key) | |
| 173 | ||
| 174 | local ipad = {}
| |
| 175 | local opad = {}
| |
| 176 | local padded_key = {}
| |
| 177 | ||
| 178 | for i = 1, blocksize do | |
| 179 | ipad[i] = bxor(0x36, key[i] or 0) | |
| 180 | opad[i] = bxor(0x5C, key[i] or 0) | |
| 181 | end | |
| 182 | ||
| 183 | for i = 1, #data do | |
| 184 | ipad[blocksize+i] = data[i] | |
| 185 | end | |
| 186 | ||
| 187 | ipad = digest(ipad) | |
| 188 | ||
| 189 | for i = 1, blocksize do | |
| 190 | padded_key[i] = opad[i] | |
| 191 | padded_key[blocksize+i] = ipad[i] | |
| 192 | end | |
| 193 | ||
| 194 | return digest(padded_key) | |
| 195 | end | |
| 196 | ||
| 197 | local function pbkdf2(pass, salt, iter, dklen) | |
| 198 | salt = strToByteArr(mapToStr(salt)) | |
| 199 | local hashlen = 32 | |
| 200 | iter = tonumber(iter) or 1000 | |
| 201 | dklen = tonumber(dklen) or 32 | |
| 202 | local block = 1 | |
| 203 | local out = {}
| |
| 204 | ||
| 205 | while dklen > 0 do | |
| 206 | local ikey = {}
| |
| 207 | local isalt = {upack(salt)}
| |
| 208 | local clen = dklen > hashlen and hashlen or dklen | |
| 209 | ||
| 210 | isalt[#isalt+1] = band(brshift(block, 24), 0xFF) | |
| 211 | isalt[#isalt+1] = band(brshift(block, 16), 0xFF) | |
| 212 | isalt[#isalt+1] = band(brshift(block, 8), 0xFF) | |
| 213 | isalt[#isalt+1] = band(block, 0xFF) | |
| 214 | ||
| 215 | for j = 1, iter do | |
| 216 | isalt = hmac(isalt, mapToStr(pass)) | |
| 217 | for k = 1, clen do ikey[k] = bxor(isalt[k], ikey[k] or 0) end | |
| 218 | if j % 200 == 0 then os.queueEvent("PBKDF2", j) coroutine.yield("PBKDF2") end
| |
| 219 | end | |
| 220 | dklen = dklen - clen | |
| 221 | block = block+1 | |
| 222 | for k = 1, clen do out[#out+1] = ikey[k] end | |
| 223 | end | |
| 224 | ||
| 225 | return setmetatable(out, byteTableMT) | |
| 226 | end | |
| 227 | ||
| 228 | return {
| |
| 229 | digest = digest, | |
| 230 | hmac = hmac, | |
| 231 | pbkdf2 = pbkdf2 | |
| 232 | } | |
| 233 | end)() | |
| 234 | ||
| 235 | -- Chacha20 cipher in ComputerCraft | |
| 236 | -- By Anavrins | |
| 237 | -- For help and details, you can PM me on the CC forums | |
| 238 | -- You may use this code in your projects without asking me, as long as credit is given and this header is kept intact | |
| 239 | -- http://www.computercraft.info/forums2/index.php?/user/12870-anavrins | |
| 240 | -- http://pastebin.com/GPzf9JSa | |
| 241 | -- Last update: April 17, 2017 | |
| 242 | local chacha20 = (function() | |
| 243 | local bxor = bit32.bxor | |
| 244 | local band = bit32.band | |
| 245 | local blshift = bit32.lshift | |
| 246 | local brshift = bit32.arshift | |
| 247 | ||
| 248 | local mod = 2^32 | |
| 249 | local tau = {("expand 16-byte k"):byte(1,-1)}
| |
| 250 | local sigma = {("expand 32-byte k"):byte(1,-1)}
| |
| 251 | ||
| 252 | local function rotl(n, b) | |
| 253 | local s = n/(2^(32-b)) | |
| 254 | local f = s%1 | |
| 255 | return (s-f) + f*mod | |
| 256 | end | |
| 257 | ||
| 258 | local function quarterRound(s, a, b, c, d) | |
| 259 | s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 16) | |
| 260 | s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 12) | |
| 261 | s[a] = (s[a]+s[b])%mod; s[d] = rotl(bxor(s[d], s[a]), 8) | |
| 262 | s[c] = (s[c]+s[d])%mod; s[b] = rotl(bxor(s[b], s[c]), 7) | |
| 263 | return s | |
| 264 | end | |
| 265 | ||
| 266 | local function hashBlock(state, rnd) | |
| 267 | local s = {unpack(state)}
| |
| 268 | for i = 1, rnd do | |
| 269 | local r = i%2==1 | |
| 270 | s = r and quarterRound(s, 1, 5, 9, 13) or quarterRound(s, 1, 6, 11, 16) | |
| 271 | s = r and quarterRound(s, 2, 6, 10, 14) or quarterRound(s, 2, 7, 12, 13) | |
| 272 | s = r and quarterRound(s, 3, 7, 11, 15) or quarterRound(s, 3, 8, 9, 14) | |
| 273 | s = r and quarterRound(s, 4, 8, 12, 16) or quarterRound(s, 4, 5, 10, 15) | |
| 274 | end | |
| 275 | for i = 1, 16 do s[i] = (s[i]+state[i])%mod end | |
| 276 | return s | |
| 277 | end | |
| 278 | ||
| 279 | local function LE_toInt(bs, i) | |
| 280 | return (bs[i+1] or 0)+ | |
| 281 | blshift((bs[i+2] or 0), 8)+ | |
| 282 | blshift((bs[i+3] or 0), 16)+ | |
| 283 | blshift((bs[i+4] or 0), 24) | |
| 284 | end | |
| 285 | ||
| 286 | local function initState(key, nonce, counter) | |
| 287 | local isKey256 = #key == 32 | |
| 288 | local const = isKey256 and sigma or tau | |
| 289 | local state = {}
| |
| 290 | ||
| 291 | state[ 1] = LE_toInt(const, 0) | |
| 292 | state[ 2] = LE_toInt(const, 4) | |
| 293 | state[ 3] = LE_toInt(const, 8) | |
| 294 | state[ 4] = LE_toInt(const, 12) | |
| 295 | ||
| 296 | state[ 5] = LE_toInt(key, 0) | |
| 297 | state[ 6] = LE_toInt(key, 4) | |
| 298 | state[ 7] = LE_toInt(key, 8) | |
| 299 | state[ 8] = LE_toInt(key, 12) | |
| 300 | state[ 9] = LE_toInt(key, isKey256 and 16 or 0) | |
| 301 | state[10] = LE_toInt(key, isKey256 and 20 or 4) | |
| 302 | state[11] = LE_toInt(key, isKey256 and 24 or 8) | |
| 303 | state[12] = LE_toInt(key, isKey256 and 28 or 12) | |
| 304 | ||
| 305 | state[13] = counter | |
| 306 | state[14] = LE_toInt(nonce, 0) | |
| 307 | state[15] = LE_toInt(nonce, 4) | |
| 308 | state[16] = LE_toInt(nonce, 8) | |
| 309 | ||
| 310 | return state | |
| 311 | end | |
| 312 | ||
| 313 | local function serialize(state) | |
| 314 | local r = {}
| |
| 315 | for i = 1, 16 do | |
| 316 | r[#r+1] = band(state[i], 0xFF) | |
| 317 | r[#r+1] = band(brshift(state[i], 8), 0xFF) | |
| 318 | r[#r+1] = band(brshift(state[i], 16), 0xFF) | |
| 319 | r[#r+1] = band(brshift(state[i], 24), 0xFF) | |
| 320 | end | |
| 321 | return r | |
| 322 | end | |
| 323 | ||
| 324 | local function crypt(data, key, nonce, cntr, round) | |
| 325 | data = strToByteArr(mapToStr(data)) | |
| 326 | key = strToByteArr(mapToStr(key)) | |
| 327 | nonce = strToByteArr(mapToStr(nonce)) | |
| 328 | assert(#key == 16 or #key == 32, "ChaCha20: Invalid key length ("..#key.."), must be 16 or 32")
| |
| 329 | assert(#nonce == 12, "ChaCha20: Invalid nonce length ("..#nonce.."), must be 12")
| |
| 330 | cntr = tonumber(cntr) or 1 | |
| 331 | round = tonumber(round) or 20 | |
| 332 | ||
| 333 | local out = {}
| |
| 334 | local state = initState(key, nonce, cntr) | |
| 335 | local blockAmt = math.floor(#data/64) | |
| 336 | for i = 0, blockAmt do | |
| 337 | local ks = serialize(hashBlock(state, round)) | |
| 338 | state[13] = (state[13]+1) % mod | |
| 339 | ||
| 340 | local block = {}
| |
| 341 | for j = 1, 64 do | |
| 342 | block[j] = data[((i)*64)+j] | |
| 343 | end | |
| 344 | for j = 1, #block do | |
| 345 | out[#out+1] = bxor(block[j], ks[j]) | |
| 346 | end | |
| 347 | ||
| 348 | if i % 1000 == 0 then | |
| 349 | os.queueEvent("")
| |
| 350 | os.pullEvent("")
| |
| 351 | end | |
| 352 | end | |
| 353 | return setmetatable(out, byteTableMT) | |
| 354 | end | |
| 355 | ||
| 356 | return {
| |
| 357 | crypt = crypt | |
| 358 | } | |
| 359 | end)() | |
| 360 | ||
| 361 | -- random.lua - Random Byte Generator | |
| 362 | local random = (function() | |
| 363 | local entropy = "" | |
| 364 | local accumulator = "" | |
| 365 | local entropyPath = "/.random" | |
| 366 | ||
| 367 | local function feed(data) | |
| 368 | accumulator = accumulator .. (data or "") | |
| 369 | end | |
| 370 | ||
| 371 | local function digest() | |
| 372 | entropy = tostring(sha256.digest(entropy .. accumulator)) | |
| 373 | accumulator = "" | |
| 374 | end | |
| 375 | ||
| 376 | if fs.exists(entropyPath) then | |
| 377 | local entropyFile = fs.open(entropyPath, "rb") | |
| 378 | feed(entropyFile.readAll()) | |
| 379 | entropyFile.close() | |
| 380 | end | |
| 381 | ||
| 382 | -- Add context. | |
| 383 | feed("init")
| |
| 384 | feed(tostring(math.random(1, 2^31 - 1))) | |
| 385 | feed("|")
| |
| 386 | feed(tostring(math.random(1, 2^31 - 1))) | |
| 387 | feed("|")
| |
| 388 | feed(tostring(math.random(1, 2^4))) | |
| 389 | feed("|")
| |
| 390 | - | feed(tostring(os.epoch("utc")))
|
| 390 | + | -- feed(tostring(os.epoch("utc")))
|
| 391 | feed("|")
| |
| 392 | feed(tostring({}))
| |
| 393 | feed(tostring({}))
| |
| 394 | digest() | |
| 395 | - | feed(tostring(os.epoch("utc")))
|
| 395 | + | -- feed(tostring(os.epoch("utc")))
|
| 396 | digest() | |
| 397 | ||
| 398 | -- Add entropy by counting. | |
| 399 | local countTable = {}
| |
| 400 | local inner = "function()return{" .. ("e'utc',"):rep(256) .. "}end"
| |
| 401 | local countf = assert(load("local e=os.epoch return " .. inner))()
| |
| 402 | for i = 1, 300 do | |
| 403 | while true do | |
| 404 | local t = countf() | |
| 405 | local t1 = t[1] | |
| 406 | if t1 ~= t[256] then | |
| 407 | for j = 1, 256 do | |
| 408 | if t1 ~= t[j] then | |
| 409 | countTable[i] = j - 1 | |
| 410 | break | |
| 411 | end | |
| 412 | end | |
| 413 | break | |
| 414 | end | |
| 415 | end | |
| 416 | end | |
| 417 | ||
| 418 | feed(mapToStr(countTable)) | |
| 419 | digest() | |
| 420 | ||
| 421 | local function save() | |
| 422 | feed("save")
| |
| 423 | - | feed(tostring(os.epoch("utc")))
|
| 423 | + | --feed(tostring(os.epoch("utc")))
|
| 424 | feed(tostring({}))
| |
| 425 | digest() | |
| 426 | ||
| 427 | local entropyFile = fs.open(entropyPath, "wb") | |
| 428 | entropyFile.write(tostring(sha256.hmac("save", entropy)))
| |
| 429 | entropy = tostring(sha256.digest(entropy)) | |
| 430 | entropyFile.close() | |
| 431 | end | |
| 432 | save() | |
| 433 | ||
| 434 | local function seed(data) | |
| 435 | feed("seed")
| |
| 436 | - | feed(tostring(os.epoch("utc")))
|
| 436 | + | --feed(tostring(os.epoch("utc")))
|
| 437 | feed(tostring({}))
| |
| 438 | feed(mapToStr(data)) | |
| 439 | digest() | |
| 440 | save() | |
| 441 | end | |
| 442 | ||
| 443 | local function random() | |
| 444 | feed("random")
| |
| 445 | - | feed(tostring(os.epoch("utc")))
|
| 445 | + | --feed(tostring(os.epoch("utc")))
|
| 446 | feed(tostring({}))
| |
| 447 | digest() | |
| 448 | save() | |
| 449 | ||
| 450 | local result = sha256.hmac("out", entropy)
| |
| 451 | entropy = tostring(sha256.digest(entropy)) | |
| 452 | ||
| 453 | return result | |
| 454 | end | |
| 455 | ||
| 456 | return {
| |
| 457 | seed = seed, | |
| 458 | save = save, | |
| 459 | random = random | |
| 460 | } | |
| 461 | end)() | |
| 462 | ||
| 463 | -- Big integer arithmetic for 168-bit (and 336-bit) numbers | |
| 464 | -- Numbers are represented as little-endian tables of 24-bit integers | |
| 465 | local arith = (function() | |
| 466 | local function isEqual(a, b) | |
| 467 | return ( | |
| 468 | a[1] == b[1] | |
| 469 | and a[2] == b[2] | |
| 470 | and a[3] == b[3] | |
| 471 | and a[4] == b[4] | |
| 472 | and a[5] == b[5] | |
| 473 | and a[6] == b[6] | |
| 474 | and a[7] == b[7] | |
| 475 | ) | |
| 476 | end | |
| 477 | ||
| 478 | local function compare(a, b) | |
| 479 | for i = 7, 1, -1 do | |
| 480 | if a[i] > b[i] then | |
| 481 | return 1 | |
| 482 | elseif a[i] < b[i] then | |
| 483 | return -1 | |
| 484 | end | |
| 485 | end | |
| 486 | ||
| 487 | return 0 | |
| 488 | end | |
| 489 | ||
| 490 | local function add(a, b) | |
| 491 | -- c7 may be greater than 2^24 before reduction | |
| 492 | local c1 = a[1] + b[1] | |
| 493 | local c2 = a[2] + b[2] | |
| 494 | local c3 = a[3] + b[3] | |
| 495 | local c4 = a[4] + b[4] | |
| 496 | local c5 = a[5] + b[5] | |
| 497 | local c6 = a[6] + b[6] | |
| 498 | local c7 = a[7] + b[7] | |
| 499 | ||
| 500 | if c1 > 0xffffff then | |
| 501 | c2 = c2 + 1 | |
| 502 | c1 = c1 - 0x1000000 | |
| 503 | end | |
| 504 | if c2 > 0xffffff then | |
| 505 | c3 = c3 + 1 | |
| 506 | c2 = c2 - 0x1000000 | |
| 507 | end | |
| 508 | if c3 > 0xffffff then | |
| 509 | c4 = c4 + 1 | |
| 510 | c3 = c3 - 0x1000000 | |
| 511 | end | |
| 512 | if c4 > 0xffffff then | |
| 513 | c5 = c5 + 1 | |
| 514 | c4 = c4 - 0x1000000 | |
| 515 | end | |
| 516 | if c5 > 0xffffff then | |
| 517 | c6 = c6 + 1 | |
| 518 | c5 = c5 - 0x1000000 | |
| 519 | end | |
| 520 | if c6 > 0xffffff then | |
| 521 | c7 = c7 + 1 | |
| 522 | c6 = c6 - 0x1000000 | |
| 523 | end | |
| 524 | ||
| 525 | return {c1, c2, c3, c4, c5, c6, c7}
| |
| 526 | end | |
| 527 | ||
| 528 | local function sub(a, b) | |
| 529 | -- c7 may be negative before reduction | |
| 530 | local c1 = a[1] - b[1] | |
| 531 | local c2 = a[2] - b[2] | |
| 532 | local c3 = a[3] - b[3] | |
| 533 | local c4 = a[4] - b[4] | |
| 534 | local c5 = a[5] - b[5] | |
| 535 | local c6 = a[6] - b[6] | |
| 536 | local c7 = a[7] - b[7] | |
| 537 | ||
| 538 | if c1 < 0 then | |
| 539 | c2 = c2 - 1 | |
| 540 | c1 = c1 + 0x1000000 | |
| 541 | end | |
| 542 | if c2 < 0 then | |
| 543 | c3 = c3 - 1 | |
| 544 | c2 = c2 + 0x1000000 | |
| 545 | end | |
| 546 | if c3 < 0 then | |
| 547 | c4 = c4 - 1 | |
| 548 | c3 = c3 + 0x1000000 | |
| 549 | end | |
| 550 | if c4 < 0 then | |
| 551 | c5 = c5 - 1 | |
| 552 | c4 = c4 + 0x1000000 | |
| 553 | end | |
| 554 | if c5 < 0 then | |
| 555 | c6 = c6 - 1 | |
| 556 | c5 = c5 + 0x1000000 | |
| 557 | end | |
| 558 | if c6 < 0 then | |
| 559 | c7 = c7 - 1 | |
| 560 | c6 = c6 + 0x1000000 | |
| 561 | end | |
| 562 | ||
| 563 | return {c1, c2, c3, c4, c5, c6, c7}
| |
| 564 | end | |
| 565 | ||
| 566 | local function rShift(a) | |
| 567 | local c1 = a[1] | |
| 568 | local c2 = a[2] | |
| 569 | local c3 = a[3] | |
| 570 | local c4 = a[4] | |
| 571 | local c5 = a[5] | |
| 572 | local c6 = a[6] | |
| 573 | local c7 = a[7] | |
| 574 | ||
| 575 | c1 = c1 / 2 | |
| 576 | c1 = c1 - c1 % 1 | |
| 577 | c1 = c1 + (c2 % 2) * 0x800000 | |
| 578 | c2 = c2 / 2 | |
| 579 | c2 = c2 - c2 % 1 | |
| 580 | c2 = c2 + (c3 % 2) * 0x800000 | |
| 581 | c3 = c3 / 2 | |
| 582 | c3 = c3 - c3 % 1 | |
| 583 | c3 = c3 + (c4 % 2) * 0x800000 | |
| 584 | c4 = c4 / 2 | |
| 585 | c4 = c4 - c4 % 1 | |
| 586 | c4 = c4 + (c5 % 2) * 0x800000 | |
| 587 | c5 = c5 / 2 | |
| 588 | c5 = c5 - c5 % 1 | |
| 589 | c5 = c5 + (c6 % 2) * 0x800000 | |
| 590 | c6 = c6 / 2 | |
| 591 | c6 = c6 - c6 % 1 | |
| 592 | c6 = c6 + (c7 % 2) * 0x800000 | |
| 593 | c7 = c7 / 2 | |
| 594 | c7 = c7 - c7 % 1 | |
| 595 | ||
| 596 | return {c1, c2, c3, c4, c5, c6, c7}
| |
| 597 | end | |
| 598 | ||
| 599 | local function addDouble(a, b) | |
| 600 | -- a and b are 336-bit integers (14 words) | |
| 601 | local c1 = a[1] + b[1] | |
| 602 | local c2 = a[2] + b[2] | |
| 603 | local c3 = a[3] + b[3] | |
| 604 | local c4 = a[4] + b[4] | |
| 605 | local c5 = a[5] + b[5] | |
| 606 | local c6 = a[6] + b[6] | |
| 607 | local c7 = a[7] + b[7] | |
| 608 | local c8 = a[8] + b[8] | |
| 609 | local c9 = a[9] + b[9] | |
| 610 | local c10 = a[10] + b[10] | |
| 611 | local c11 = a[11] + b[11] | |
| 612 | local c12 = a[12] + b[12] | |
| 613 | local c13 = a[13] + b[13] | |
| 614 | local c14 = a[14] + b[14] | |
| 615 | ||
| 616 | if c1 > 0xffffff then | |
| 617 | c2 = c2 + 1 | |
| 618 | c1 = c1 - 0x1000000 | |
| 619 | end | |
| 620 | if c2 > 0xffffff then | |
| 621 | c3 = c3 + 1 | |
| 622 | c2 = c2 - 0x1000000 | |
| 623 | end | |
| 624 | if c3 > 0xffffff then | |
| 625 | c4 = c4 + 1 | |
| 626 | c3 = c3 - 0x1000000 | |
| 627 | end | |
| 628 | if c4 > 0xffffff then | |
| 629 | c5 = c5 + 1 | |
| 630 | c4 = c4 - 0x1000000 | |
| 631 | end | |
| 632 | if c5 > 0xffffff then | |
| 633 | c6 = c6 + 1 | |
| 634 | c5 = c5 - 0x1000000 | |
| 635 | end | |
| 636 | if c6 > 0xffffff then | |
| 637 | c7 = c7 + 1 | |
| 638 | c6 = c6 - 0x1000000 | |
| 639 | end | |
| 640 | if c7 > 0xffffff then | |
| 641 | c8 = c8 + 1 | |
| 642 | c7 = c7 - 0x1000000 | |
| 643 | end | |
| 644 | if c8 > 0xffffff then | |
| 645 | c9 = c9 + 1 | |
| 646 | c8 = c8 - 0x1000000 | |
| 647 | end | |
| 648 | if c9 > 0xffffff then | |
| 649 | c10 = c10 + 1 | |
| 650 | c9 = c9 - 0x1000000 | |
| 651 | end | |
| 652 | if c10 > 0xffffff then | |
| 653 | c11 = c11 + 1 | |
| 654 | c10 = c10 - 0x1000000 | |
| 655 | end | |
| 656 | if c11 > 0xffffff then | |
| 657 | c12 = c12 + 1 | |
| 658 | c11 = c11 - 0x1000000 | |
| 659 | end | |
| 660 | if c12 > 0xffffff then | |
| 661 | c13 = c13 + 1 | |
| 662 | c12 = c12 - 0x1000000 | |
| 663 | end | |
| 664 | if c13 > 0xffffff then | |
| 665 | c14 = c14 + 1 | |
| 666 | c13 = c13 - 0x1000000 | |
| 667 | end | |
| 668 | ||
| 669 | return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
| |
| 670 | end | |
| 671 | ||
| 672 | local function mult(a, b, half_multiply) | |
| 673 | local a1, a2, a3, a4, a5, a6, a7 = unpack(a) | |
| 674 | local b1, b2, b3, b4, b5, b6, b7 = unpack(b) | |
| 675 | ||
| 676 | local c1 = a1 * b1 | |
| 677 | local c2 = a1 * b2 + a2 * b1 | |
| 678 | local c3 = a1 * b3 + a2 * b2 + a3 * b1 | |
| 679 | local c4 = a1 * b4 + a2 * b3 + a3 * b2 + a4 * b1 | |
| 680 | local c5 = a1 * b5 + a2 * b4 + a3 * b3 + a4 * b2 + a5 * b1 | |
| 681 | local c6 = a1 * b6 + a2 * b5 + a3 * b4 + a4 * b3 + a5 * b2 + a6 * b1 | |
| 682 | local c7 = a1 * b7 + a2 * b6 + a3 * b5 + a4 * b4 + a5 * b3 + a6 * b2 | |
| 683 | + a7 * b1 | |
| 684 | local c8, c9, c10, c11, c12, c13, c14 | |
| 685 | if not half_multiply then | |
| 686 | c8 = a2 * b7 + a3 * b6 + a4 * b5 + a5 * b4 + a6 * b3 + a7 * b2 | |
| 687 | c9 = a3 * b7 + a4 * b6 + a5 * b5 + a6 * b4 + a7 * b3 | |
| 688 | c10 = a4 * b7 + a5 * b6 + a6 * b5 + a7 * b4 | |
| 689 | c11 = a5 * b7 + a6 * b6 + a7 * b5 | |
| 690 | c12 = a6 * b7 + a7 * b6 | |
| 691 | c13 = a7 * b7 | |
| 692 | c14 = 0 | |
| 693 | else | |
| 694 | c8 = 0 | |
| 695 | end | |
| 696 | ||
| 697 | local temp | |
| 698 | temp = c1 | |
| 699 | c1 = c1 % 0x1000000 | |
| 700 | c2 = c2 + (temp - c1) / 0x1000000 | |
| 701 | temp = c2 | |
| 702 | c2 = c2 % 0x1000000 | |
| 703 | c3 = c3 + (temp - c2) / 0x1000000 | |
| 704 | temp = c3 | |
| 705 | c3 = c3 % 0x1000000 | |
| 706 | c4 = c4 + (temp - c3) / 0x1000000 | |
| 707 | temp = c4 | |
| 708 | c4 = c4 % 0x1000000 | |
| 709 | c5 = c5 + (temp - c4) / 0x1000000 | |
| 710 | temp = c5 | |
| 711 | c5 = c5 % 0x1000000 | |
| 712 | c6 = c6 + (temp - c5) / 0x1000000 | |
| 713 | temp = c6 | |
| 714 | c6 = c6 % 0x1000000 | |
| 715 | c7 = c7 + (temp - c6) / 0x1000000 | |
| 716 | temp = c7 | |
| 717 | c7 = c7 % 0x1000000 | |
| 718 | if not half_multiply then | |
| 719 | c8 = c8 + (temp - c7) / 0x1000000 | |
| 720 | temp = c8 | |
| 721 | c8 = c8 % 0x1000000 | |
| 722 | c9 = c9 + (temp - c8) / 0x1000000 | |
| 723 | temp = c9 | |
| 724 | c9 = c9 % 0x1000000 | |
| 725 | c10 = c10 + (temp - c9) / 0x1000000 | |
| 726 | temp = c10 | |
| 727 | c10 = c10 % 0x1000000 | |
| 728 | c11 = c11 + (temp - c10) / 0x1000000 | |
| 729 | temp = c11 | |
| 730 | c11 = c11 % 0x1000000 | |
| 731 | c12 = c12 + (temp - c11) / 0x1000000 | |
| 732 | temp = c12 | |
| 733 | c12 = c12 % 0x1000000 | |
| 734 | c13 = c13 + (temp - c12) / 0x1000000 | |
| 735 | temp = c13 | |
| 736 | c13 = c13 % 0x1000000 | |
| 737 | c14 = c14 + (temp - c13) / 0x1000000 | |
| 738 | end | |
| 739 | ||
| 740 | return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
| |
| 741 | end | |
| 742 | ||
| 743 | local function square(a) | |
| 744 | -- returns a 336-bit integer (14 words) | |
| 745 | local a1, a2, a3, a4, a5, a6, a7 = unpack(a) | |
| 746 | ||
| 747 | local c1 = a1 * a1 | |
| 748 | local c2 = a1 * a2 * 2 | |
| 749 | local c3 = a1 * a3 * 2 + a2 * a2 | |
| 750 | local c4 = a1 * a4 * 2 + a2 * a3 * 2 | |
| 751 | local c5 = a1 * a5 * 2 + a2 * a4 * 2 + a3 * a3 | |
| 752 | local c6 = a1 * a6 * 2 + a2 * a5 * 2 + a3 * a4 * 2 | |
| 753 | local c7 = a1 * a7 * 2 + a2 * a6 * 2 + a3 * a5 * 2 + a4 * a4 | |
| 754 | local c8 = a2 * a7 * 2 + a3 * a6 * 2 + a4 * a5 * 2 | |
| 755 | local c9 = a3 * a7 * 2 + a4 * a6 * 2 + a5 * a5 | |
| 756 | local c10 = a4 * a7 * 2 + a5 * a6 * 2 | |
| 757 | local c11 = a5 * a7 * 2 + a6 * a6 | |
| 758 | local c12 = a6 * a7 * 2 | |
| 759 | local c13 = a7 * a7 | |
| 760 | local c14 = 0 | |
| 761 | ||
| 762 | local temp | |
| 763 | temp = c1 | |
| 764 | c1 = c1 % 0x1000000 | |
| 765 | c2 = c2 + (temp - c1) / 0x1000000 | |
| 766 | temp = c2 | |
| 767 | c2 = c2 % 0x1000000 | |
| 768 | c3 = c3 + (temp - c2) / 0x1000000 | |
| 769 | temp = c3 | |
| 770 | c3 = c3 % 0x1000000 | |
| 771 | c4 = c4 + (temp - c3) / 0x1000000 | |
| 772 | temp = c4 | |
| 773 | c4 = c4 % 0x1000000 | |
| 774 | c5 = c5 + (temp - c4) / 0x1000000 | |
| 775 | temp = c5 | |
| 776 | c5 = c5 % 0x1000000 | |
| 777 | c6 = c6 + (temp - c5) / 0x1000000 | |
| 778 | temp = c6 | |
| 779 | c6 = c6 % 0x1000000 | |
| 780 | c7 = c7 + (temp - c6) / 0x1000000 | |
| 781 | temp = c7 | |
| 782 | c7 = c7 % 0x1000000 | |
| 783 | c8 = c8 + (temp - c7) / 0x1000000 | |
| 784 | temp = c8 | |
| 785 | c8 = c8 % 0x1000000 | |
| 786 | c9 = c9 + (temp - c8) / 0x1000000 | |
| 787 | temp = c9 | |
| 788 | c9 = c9 % 0x1000000 | |
| 789 | c10 = c10 + (temp - c9) / 0x1000000 | |
| 790 | temp = c10 | |
| 791 | c10 = c10 % 0x1000000 | |
| 792 | c11 = c11 + (temp - c10) / 0x1000000 | |
| 793 | temp = c11 | |
| 794 | c11 = c11 % 0x1000000 | |
| 795 | c12 = c12 + (temp - c11) / 0x1000000 | |
| 796 | temp = c12 | |
| 797 | c12 = c12 % 0x1000000 | |
| 798 | c13 = c13 + (temp - c12) / 0x1000000 | |
| 799 | temp = c13 | |
| 800 | c13 = c13 % 0x1000000 | |
| 801 | c14 = c14 + (temp - c13) / 0x1000000 | |
| 802 | ||
| 803 | return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
| |
| 804 | end | |
| 805 | ||
| 806 | local function encodeInt(a) | |
| 807 | local enc = {}
| |
| 808 | ||
| 809 | for i = 1, 7 do | |
| 810 | local word = a[i] | |
| 811 | for j = 1, 3 do | |
| 812 | enc[#enc + 1] = word % 256 | |
| 813 | word = math.floor(word / 256) | |
| 814 | end | |
| 815 | end | |
| 816 | ||
| 817 | return enc | |
| 818 | end | |
| 819 | ||
| 820 | local function decodeInt(enc) | |
| 821 | local a = {}
| |
| 822 | local encCopy = {}
| |
| 823 | ||
| 824 | for i = 1, 21 do | |
| 825 | local byte = enc[i] | |
| 826 | assert(type(byte) == "number", "integer decoding failure") | |
| 827 | assert(byte >= 0 and byte <= 255, "integer decoding failure") | |
| 828 | assert(byte % 1 == 0, "integer decoding failure") | |
| 829 | encCopy[i] = byte | |
| 830 | end | |
| 831 | ||
| 832 | for i = 1, 21, 3 do | |
| 833 | local word = 0 | |
| 834 | for j = 2, 0, -1 do | |
| 835 | word = word * 256 | |
| 836 | word = word + encCopy[i + j] | |
| 837 | end | |
| 838 | a[#a + 1] = word | |
| 839 | end | |
| 840 | ||
| 841 | return a | |
| 842 | end | |
| 843 | ||
| 844 | local function mods(d, w) | |
| 845 | local result = d[1] % 2^w | |
| 846 | ||
| 847 | if result >= 2^(w - 1) then | |
| 848 | result = result - 2^w | |
| 849 | end | |
| 850 | ||
| 851 | return result | |
| 852 | end | |
| 853 | ||
| 854 | -- Represents a 168-bit number as the (2^w)-ary Non-Adjacent Form | |
| 855 | local function NAF(d, w) | |
| 856 | local t = {}
| |
| 857 | local d = {unpack(d)}
| |
| 858 | ||
| 859 | for _ = 1, 168 do | |
| 860 | if d[1] % 2 == 1 then | |
| 861 | t[#t + 1] = mods(d, w) | |
| 862 | d = sub(d, {t[#t], 0, 0, 0, 0, 0, 0})
| |
| 863 | else | |
| 864 | t[#t + 1] = 0 | |
| 865 | end | |
| 866 | ||
| 867 | d = rShift(d) | |
| 868 | end | |
| 869 | ||
| 870 | return t | |
| 871 | end | |
| 872 | ||
| 873 | return {
| |
| 874 | isEqual = isEqual, | |
| 875 | compare = compare, | |
| 876 | add = add, | |
| 877 | sub = sub, | |
| 878 | addDouble = addDouble, | |
| 879 | mult = mult, | |
| 880 | square = square, | |
| 881 | encodeInt = encodeInt, | |
| 882 | decodeInt = decodeInt, | |
| 883 | NAF = NAF | |
| 884 | } | |
| 885 | end)() | |
| 886 | ||
| 887 | -- Arithmetic on the finite field of integers modulo p | |
| 888 | -- Where p is the finite field modulus | |
| 889 | local modp = (function() | |
| 890 | local add = arith.add | |
| 891 | local sub = arith.sub | |
| 892 | local addDouble = arith.addDouble | |
| 893 | local mult = arith.mult | |
| 894 | local square = arith.square | |
| 895 | ||
| 896 | local p = {3, 0, 0, 0, 0, 0, 15761408}
| |
| 897 | ||
| 898 | -- We're using the Montgomery Reduction for fast modular multiplication. | |
| 899 | -- https://en.wikipedia.org/wiki/Montgomery_modular_multiplication | |
| 900 | -- r = 2^168 | |
| 901 | -- p * pInverse = -1 (mod r) | |
| 902 | -- r2 = r * r (mod p) | |
| 903 | local pInverse = {5592405, 5592405, 5592405, 5592405, 5592405, 5592405, 14800213}
| |
| 904 | local r2 = {13533400, 837116, 6278376, 13533388, 837116, 6278376, 7504076}
| |
| 905 | ||
| 906 | local function multByP(a) | |
| 907 | local a1, a2, a3, a4, a5, a6, a7 = unpack(a) | |
| 908 | ||
| 909 | local c1 = a1 * 3 | |
| 910 | local c2 = a2 * 3 | |
| 911 | local c3 = a3 * 3 | |
| 912 | local c4 = a4 * 3 | |
| 913 | local c5 = a5 * 3 | |
| 914 | local c6 = a6 * 3 | |
| 915 | local c7 = a1 * 15761408 | |
| 916 | c7 = c7 + a7 * 3 | |
| 917 | local c8 = a2 * 15761408 | |
| 918 | local c9 = a3 * 15761408 | |
| 919 | local c10 = a4 * 15761408 | |
| 920 | local c11 = a5 * 15761408 | |
| 921 | local c12 = a6 * 15761408 | |
| 922 | local c13 = a7 * 15761408 | |
| 923 | local c14 = 0 | |
| 924 | ||
| 925 | local temp | |
| 926 | temp = c1 / 0x1000000 | |
| 927 | c2 = c2 + (temp - temp % 1) | |
| 928 | c1 = c1 % 0x1000000 | |
| 929 | temp = c2 / 0x1000000 | |
| 930 | c3 = c3 + (temp - temp % 1) | |
| 931 | c2 = c2 % 0x1000000 | |
| 932 | temp = c3 / 0x1000000 | |
| 933 | c4 = c4 + (temp - temp % 1) | |
| 934 | c3 = c3 % 0x1000000 | |
| 935 | temp = c4 / 0x1000000 | |
| 936 | c5 = c5 + (temp - temp % 1) | |
| 937 | c4 = c4 % 0x1000000 | |
| 938 | temp = c5 / 0x1000000 | |
| 939 | c6 = c6 + (temp - temp % 1) | |
| 940 | c5 = c5 % 0x1000000 | |
| 941 | temp = c6 / 0x1000000 | |
| 942 | c7 = c7 + (temp - temp % 1) | |
| 943 | c6 = c6 % 0x1000000 | |
| 944 | temp = c7 / 0x1000000 | |
| 945 | c8 = c8 + (temp - temp % 1) | |
| 946 | c7 = c7 % 0x1000000 | |
| 947 | temp = c8 / 0x1000000 | |
| 948 | c9 = c9 + (temp - temp % 1) | |
| 949 | c8 = c8 % 0x1000000 | |
| 950 | temp = c9 / 0x1000000 | |
| 951 | c10 = c10 + (temp - temp % 1) | |
| 952 | c9 = c9 % 0x1000000 | |
| 953 | temp = c10 / 0x1000000 | |
| 954 | c11 = c11 + (temp - temp % 1) | |
| 955 | c10 = c10 % 0x1000000 | |
| 956 | temp = c11 / 0x1000000 | |
| 957 | c12 = c12 + (temp - temp % 1) | |
| 958 | c11 = c11 % 0x1000000 | |
| 959 | temp = c12 / 0x1000000 | |
| 960 | c13 = c13 + (temp - temp % 1) | |
| 961 | c12 = c12 % 0x1000000 | |
| 962 | temp = c13 / 0x1000000 | |
| 963 | c14 = c14 + (temp - temp % 1) | |
| 964 | c13 = c13 % 0x1000000 | |
| 965 | ||
| 966 | return {c1, c2, c3, c4, c5, c6, c7, c8, c9, c10, c11, c12, c13, c14}
| |
| 967 | end | |
| 968 | ||
| 969 | -- Reduces a number from [0, 2p - 1] to [0, p - 1] | |
| 970 | local function reduceModP(a) | |
| 971 | -- a < p | |
| 972 | if a[7] < 15761408 or a[7] == 15761408 and a[1] < 3 then | |
| 973 | return {unpack(a)}
| |
| 974 | end | |
| 975 | ||
| 976 | -- a > p | |
| 977 | local c1 = a[1] | |
| 978 | local c2 = a[2] | |
| 979 | local c3 = a[3] | |
| 980 | local c4 = a[4] | |
| 981 | local c5 = a[5] | |
| 982 | local c6 = a[6] | |
| 983 | local c7 = a[7] | |
| 984 | ||
| 985 | c1 = c1 - 3 | |
| 986 | c7 = c7 - 15761408 | |
| 987 | ||
| 988 | if c1 < 0 then | |
| 989 | c2 = c2 - 1 | |
| 990 | c1 = c1 + 0x1000000 | |
| 991 | end | |
| 992 | if c2 < 0 then | |
| 993 | c3 = c3 - 1 | |
| 994 | c2 = c2 + 0x1000000 | |
| 995 | end | |
| 996 | if c3 < 0 then | |
| 997 | c4 = c4 - 1 | |
| 998 | c3 = c3 + 0x1000000 | |
| 999 | end | |
| 1000 | if c4 < 0 then | |
| 1001 | c5 = c5 - 1 | |
| 1002 | c4 = c4 + 0x1000000 | |
| 1003 | end | |
| 1004 | if c5 < 0 then | |
| 1005 | c6 = c6 - 1 | |
| 1006 | c5 = c5 + 0x1000000 | |
| 1007 | end | |
| 1008 | if c6 < 0 then | |
| 1009 | c7 = c7 - 1 | |
| 1010 | c6 = c6 + 0x1000000 | |
| 1011 | end | |
| 1012 | ||
| 1013 | return {c1, c2, c3, c4, c5, c6, c7}
| |
| 1014 | end | |
| 1015 | ||
| 1016 | local function addModP(a, b) | |
| 1017 | return reduceModP(add(a, b)) | |
| 1018 | end | |
| 1019 | ||
| 1020 | local function subModP(a, b) | |
| 1021 | local result = sub(a, b) | |
| 1022 | ||
| 1023 | if result[7] < 0 then | |
| 1024 | result = add(result, p) | |
| 1025 | end | |
| 1026 | ||
| 1027 | return result | |
| 1028 | end | |
| 1029 | ||
| 1030 | -- Montgomery REDC algorithn | |
| 1031 | -- Reduces a number from [0, p^2 - 1] to [0, p - 1] | |
| 1032 | local function REDC(T) | |
| 1033 | local m = mult(T, pInverse, true) | |
| 1034 | local t = {unpack(addDouble(T, multByP(m)), 8, 14)}
| |
| 1035 | ||
| 1036 | return reduceModP(t) | |
| 1037 | end | |
| 1038 | ||
| 1039 | local function multModP(a, b) | |
| 1040 | -- Only works with a, b in Montgomery form | |
| 1041 | return REDC(mult(a, b)) | |
| 1042 | end | |
| 1043 | ||
| 1044 | local function squareModP(a) | |
| 1045 | -- Only works with a in Montgomery form | |
| 1046 | return REDC(square(a)) | |
| 1047 | end | |
| 1048 | ||
| 1049 | local function montgomeryModP(a) | |
| 1050 | return multModP(a, r2) | |
| 1051 | end | |
| 1052 | ||
| 1053 | local function inverseMontgomeryModP(a) | |
| 1054 | a = {unpack(a)}
| |
| 1055 | ||
| 1056 | for i = 8, 14 do | |
| 1057 | a[i] = 0 | |
| 1058 | end | |
| 1059 | ||
| 1060 | return REDC(a) | |
| 1061 | end | |
| 1062 | ||
| 1063 | local ONE = montgomeryModP({1, 0, 0, 0, 0, 0, 0})
| |
| 1064 | ||
| 1065 | local function expModP(base, exponentBinary) | |
| 1066 | base = {unpack(base)}
| |
| 1067 | local result = {unpack(ONE)}
| |
| 1068 | ||
| 1069 | for i = 1, 168 do | |
| 1070 | if exponentBinary[i] == 1 then | |
| 1071 | result = multModP(result, base) | |
| 1072 | end | |
| 1073 | base = squareModP(base) | |
| 1074 | end | |
| 1075 | ||
| 1076 | return result | |
| 1077 | end | |
| 1078 | ||
| 1079 | return {
| |
| 1080 | addModP = addModP, | |
| 1081 | subModP = subModP, | |
| 1082 | multModP = multModP, | |
| 1083 | squareModP = squareModP, | |
| 1084 | montgomeryModP = montgomeryModP, | |
| 1085 | inverseMontgomeryModP = inverseMontgomeryModP, | |
| 1086 | expModP = expModP | |
| 1087 | } | |
| 1088 | end)() | |
| 1089 | ||
| 1090 | -- Arithmetic on the Finite Field of Integers modulo q | |
| 1091 | -- Where q is the generator's subgroup order. | |
| 1092 | local modq = (function() | |
| 1093 | local isEqual = arith.isEqual | |
| 1094 | local compare = arith.compare | |
| 1095 | local add = arith.add | |
| 1096 | local sub = arith.sub | |
| 1097 | local addDouble = arith.addDouble | |
| 1098 | local mult = arith.mult | |
| 1099 | local square = arith.square | |
| 1100 | local encodeInt = arith.encodeInt | |
| 1101 | local decodeInt = arith.decodeInt | |
| 1102 | ||
| 1103 | local modQMT | |
| 1104 | ||
| 1105 | local q = {9622359, 6699217, 13940450, 16775734, 16777215, 16777215, 3940351}
| |
| 1106 | 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}
| |
| 1107 | ||
| 1108 | -- We're using the Montgomery Reduction for fast modular multiplication. | |
| 1109 | -- https://en.wikipedia.org/wiki/Montgomery_modular_multiplication | |
| 1110 | -- r = 2^168 | |
| 1111 | -- q * qInverse = -1 (mod r) | |
| 1112 | -- r2 = r * r (mod q) | |
| 1113 | local qInverse = {15218585, 5740955, 3271338, 9903997, 9067368, 7173545, 6988392}
| |
| 1114 | local r2 = {1336213, 11071705, 9716828, 11083885, 9188643, 1494868, 3306114}
| |
| 1115 | ||
| 1116 | -- Reduces a number from [0, 2q - 1] to [0, q - 1] | |
| 1117 | local function reduceModQ(a) | |
| 1118 | local result = {unpack(a)}
| |
| 1119 | ||
| 1120 | if compare(result, q) >= 0 then | |
| 1121 | result = sub(result, q) | |
| 1122 | end | |
| 1123 | ||
| 1124 | return setmetatable(result, modQMT) | |
| 1125 | end | |
| 1126 | ||
| 1127 | local function addModQ(a, b) | |
| 1128 | return reduceModQ(add(a, b)) | |
| 1129 | end | |
| 1130 | ||
| 1131 | local function subModQ(a, b) | |
| 1132 | local result = sub(a, b) | |
| 1133 | ||
| 1134 | if result[7] < 0 then | |
| 1135 | result = add(result, q) | |
| 1136 | end | |
| 1137 | ||
| 1138 | return setmetatable(result, modQMT) | |
| 1139 | end | |
| 1140 | ||
| 1141 | -- Montgomery REDC algorithn | |
| 1142 | -- Reduces a number from [0, q^2 - 1] to [0, q - 1] | |
| 1143 | local function REDC(T) | |
| 1144 | local m = {unpack(mult({unpack(T, 1, 7)}, qInverse, true), 1, 7)}
| |
| 1145 | local t = {unpack(addDouble(T, mult(m, q)), 8, 14)}
| |
| 1146 | ||
| 1147 | return reduceModQ(t) | |
| 1148 | end | |
| 1149 | ||
| 1150 | local function multModQ(a, b) | |
| 1151 | -- Only works with a, b in Montgomery form | |
| 1152 | return REDC(mult(a, b)) | |
| 1153 | end | |
| 1154 | ||
| 1155 | local function squareModQ(a) | |
| 1156 | -- Only works with a in Montgomery form | |
| 1157 | return REDC(square(a)) | |
| 1158 | end | |
| 1159 | ||
| 1160 | local function montgomeryModQ(a) | |
| 1161 | return multModQ(a, r2) | |
| 1162 | end | |
| 1163 | ||
| 1164 | local function inverseMontgomeryModQ(a) | |
| 1165 | local a = {unpack(a)}
| |
| 1166 | ||
| 1167 | for i = 8, 14 do | |
| 1168 | a[i] = 0 | |
| 1169 | end | |
| 1170 | ||
| 1171 | return REDC(a) | |
| 1172 | end | |
| 1173 | ||
| 1174 | local ONE = montgomeryModQ({1, 0, 0, 0, 0, 0, 0})
| |
| 1175 | ||
| 1176 | local function expModQ(base, exponentBinary) | |
| 1177 | local base = {unpack(base)}
| |
| 1178 | local result = {unpack(ONE)}
| |
| 1179 | ||
| 1180 | for i = 1, 168 do | |
| 1181 | if exponentBinary[i] == 1 then | |
| 1182 | result = multModQ(result, base) | |
| 1183 | end | |
| 1184 | base = squareModQ(base) | |
| 1185 | end | |
| 1186 | ||
| 1187 | return result | |
| 1188 | end | |
| 1189 | ||
| 1190 | local function intExpModQ(base, exponent) | |
| 1191 | local base = {unpack(base)}
| |
| 1192 | local result = setmetatable({unpack(ONE)}, modQMT)
| |
| 1193 | ||
| 1194 | if exponent < 0 then | |
| 1195 | base = expModQ(base, qMinusTwoBinary) | |
| 1196 | exponent = -exponent | |
| 1197 | end | |
| 1198 | ||
| 1199 | while exponent > 0 do | |
| 1200 | if exponent % 2 == 1 then | |
| 1201 | result = multModQ(result, base) | |
| 1202 | end | |
| 1203 | base = squareModQ(base) | |
| 1204 | exponent = exponent / 2 | |
| 1205 | exponent = exponent - exponent % 1 | |
| 1206 | end | |
| 1207 | ||
| 1208 | return result | |
| 1209 | end | |
| 1210 | ||
| 1211 | local function encodeModQ(a) | |
| 1212 | local result = encodeInt(a) | |
| 1213 | ||
| 1214 | return setmetatable(result, byteTableMT) | |
| 1215 | end | |
| 1216 | ||
| 1217 | local function decodeModQ(s) | |
| 1218 | local result = decodeInt(strToByteArr(mapToStr(s):sub(1, 21))) | |
| 1219 | result[7] = result[7] % q[7] | |
| 1220 | ||
| 1221 | return setmetatable(result, modQMT) | |
| 1222 | end | |
| 1223 | ||
| 1224 | local function randomModQ() | |
| 1225 | while true do | |
| 1226 | local s = {unpack(random.random(), 1, 21)}
| |
| 1227 | local result = decodeInt(s) | |
| 1228 | if result[7] < q[7] then | |
| 1229 | return setmetatable(result, modQMT) | |
| 1230 | end | |
| 1231 | end | |
| 1232 | end | |
| 1233 | ||
| 1234 | local function hashModQ(data) | |
| 1235 | return decodeModQ(sha256.digest(data)) | |
| 1236 | end | |
| 1237 | ||
| 1238 | modQMT = {
| |
| 1239 | __index = {
| |
| 1240 | encode = function(self) | |
| 1241 | return encodeModQ(self) | |
| 1242 | end | |
| 1243 | }, | |
| 1244 | ||
| 1245 | __tostring = function(self) | |
| 1246 | return self:encode():toHex() | |
| 1247 | end, | |
| 1248 | ||
| 1249 | __add = function(self, other) | |
| 1250 | if type(self) == "number" then | |
| 1251 | return other + self | |
| 1252 | end | |
| 1253 | ||
| 1254 | if type(other) == "number" then | |
| 1255 | assert(other < 2^24, "number operand too big") | |
| 1256 | other = montgomeryModQ({other, 0, 0, 0, 0, 0, 0})
| |
| 1257 | end | |
| 1258 | ||
| 1259 | return addModQ(self, other) | |
| 1260 | end, | |
| 1261 | ||
| 1262 | __sub = function(a, b) | |
| 1263 | if type(a) == "number" then | |
| 1264 | assert(a < 2^24, "number operand too big") | |
| 1265 | a = montgomeryModQ({a, 0, 0, 0, 0, 0, 0})
| |
| 1266 | end | |
| 1267 | ||
| 1268 | if type(b) == "number" then | |
| 1269 | assert(b < 2^24, "number operand too big") | |
| 1270 | b = montgomeryModQ({b, 0, 0, 0, 0, 0, 0})
| |
| 1271 | end | |
| 1272 | ||
| 1273 | return subModQ(a, b) | |
| 1274 | end, | |
| 1275 | ||
| 1276 | __unm = function(self) | |
| 1277 | return subModQ(q, self) | |
| 1278 | end, | |
| 1279 | ||
| 1280 | __eq = function(self, other) | |
| 1281 | return isEqual(self, other) | |
| 1282 | end, | |
| 1283 | ||
| 1284 | __mul = function(self, other) | |
| 1285 | if type(self) == "number" then | |
| 1286 | return other * self | |
| 1287 | end | |
| 1288 | ||
| 1289 | -- EC point | |
| 1290 | -- Use the point's metatable to handle multiplication | |
| 1291 | if type(other) == "table" and type(other[1]) == "table" then | |
| 1292 | return other * self | |
| 1293 | end | |
| 1294 | ||
| 1295 | if type(other) == "number" then | |
| 1296 | assert(other < 2^24, "number operand too big") | |
| 1297 | other = montgomeryModQ({other, 0, 0, 0, 0, 0, 0})
| |
| 1298 | end | |
| 1299 | ||
| 1300 | return multModQ(self, other) | |
| 1301 | end, | |
| 1302 | ||
| 1303 | __div = function(a, b) | |
| 1304 | if type(a) == "number" then | |
| 1305 | assert(a < 2^24, "number operand too big") | |
| 1306 | a = montgomeryModQ({a, 0, 0, 0, 0, 0, 0})
| |
| 1307 | end | |
| 1308 | ||
| 1309 | if type(b) == "number" then | |
| 1310 | assert(b < 2^24, "number operand too big") | |
| 1311 | b = montgomeryModQ({b, 0, 0, 0, 0, 0, 0})
| |
| 1312 | end | |
| 1313 | ||
| 1314 | local bInv = expModQ(b, qMinusTwoBinary) | |
| 1315 | ||
| 1316 | return multModQ(a, bInv) | |
| 1317 | end, | |
| 1318 | ||
| 1319 | __pow = function(self, other) | |
| 1320 | return intExpModQ(self, other) | |
| 1321 | end | |
| 1322 | } | |
| 1323 | ||
| 1324 | return {
| |
| 1325 | hashModQ = hashModQ, | |
| 1326 | randomModQ = randomModQ, | |
| 1327 | decodeModQ = decodeModQ, | |
| 1328 | inverseMontgomeryModQ = inverseMontgomeryModQ | |
| 1329 | } | |
| 1330 | end)() | |
| 1331 | ||
| 1332 | -- Elliptic curve arithmetic | |
| 1333 | local curve = (function() | |
| 1334 | ---- About the Curve Itself | |
| 1335 | -- Field Size: 168 bits | |
| 1336 | -- Field Modulus (p): 481 * 2^159 + 3 | |
| 1337 | -- Equation: x^2 + y^2 = 1 + 122 * x^2 * y^2 | |
| 1338 | -- Parameters: Edwards Curve with d = 122 | |
| 1339 | -- Curve Order (n): 351491143778082151827986174289773107581916088585564 | |
| 1340 | -- Cofactor (h): 4 | |
| 1341 | -- Generator Order (q): 87872785944520537956996543572443276895479022146391 | |
| 1342 | ---- About the Curve's Security | |
| 1343 | -- Current best attack security: 81.777 bits (Small Subgroup + Rho) | |
| 1344 | -- Rho Security: log2(0.884 * sqrt(q)) = 82.777 bits | |
| 1345 | -- Transfer Security? Yes: p ~= q; k > 20 | |
| 1346 | -- Field Discriminant Security? Yes: | |
| 1347 | -- t = 27978492958645335688000168 | |
| 1348 | -- s = 10 | |
| 1349 | -- |D| = 6231685068753619775430107799412237267322159383147 > 2^100 | |
| 1350 | -- Rigidity? No, not at all. | |
| 1351 | -- XZ/YZ Ladder Security? No: Single coordinate ladders are insecure. | |
| 1352 | -- Small Subgroup Security? No. | |
| 1353 | -- Invalid Curve Security? Yes: Points are checked before every operation. | |
| 1354 | -- Invalid Curve Twist Security? No: Don't use single coordinate ladders. | |
| 1355 | -- Completeness? Yes: The curve is complete. | |
| 1356 | -- Indistinguishability? Yes (Elligator 2), but not implemented. | |
| 1357 | ||
| 1358 | local isEqual = arith.isEqual | |
| 1359 | local NAF = arith.NAF | |
| 1360 | local encodeInt = arith.encodeInt | |
| 1361 | local decodeInt = arith.decodeInt | |
| 1362 | local multModP = modp.multModP | |
| 1363 | local squareModP = modp.squareModP | |
| 1364 | local addModP = modp.addModP | |
| 1365 | local subModP = modp.subModP | |
| 1366 | local montgomeryModP = modp.montgomeryModP | |
| 1367 | local expModP = modp.expModP | |
| 1368 | local inverseMontgomeryModQ = modq.inverseMontgomeryModQ | |
| 1369 | ||
| 1370 | local pointMT | |
| 1371 | local ZERO = {0, 0, 0, 0, 0, 0, 0}
| |
| 1372 | local ONE = montgomeryModP({1, 0, 0, 0, 0, 0, 0})
| |
| 1373 | ||
| 1374 | -- Curve Parameters | |
| 1375 | local d = montgomeryModP({122, 0, 0, 0, 0, 0, 0})
| |
| 1376 | local p = {3, 0, 0, 0, 0, 0, 15761408}
| |
| 1377 | 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}
| |
| 1378 | 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}
| |
| 1379 | local G = {
| |
| 1380 | {6636044, 10381432, 15741790, 2914241, 5785600, 264923, 4550291},
| |
| 1381 | {13512827, 8449886, 5647959, 1135556, 5489843, 7177356, 8002203},
| |
| 1382 | {unpack(ONE)}
| |
| 1383 | } | |
| 1384 | local O = {
| |
| 1385 | {unpack(ZERO)},
| |
| 1386 | {unpack(ONE)},
| |
| 1387 | {unpack(ONE)}
| |
| 1388 | } | |
| 1389 | ||
| 1390 | -- Projective Coordinates for Edwards curves for point addition/doubling. | |
| 1391 | -- Points are represented as: (X:Y:Z) where x = X/Z and y = Y/Z | |
| 1392 | -- The identity element is represented by (0:1:1) | |
| 1393 | -- Point operation formulas are available on the EFD: | |
| 1394 | -- https://www.hyperelliptic.org/EFD/g1p/auto-edwards-projective.html | |
| 1395 | local function pointDouble(P1) | |
| 1396 | -- 3M + 4S | |
| 1397 | local X1, Y1, Z1 = unpack(P1) | |
| 1398 | ||
| 1399 | local b = addModP(X1, Y1) | |
| 1400 | local B = squareModP(b) | |
| 1401 | local C = squareModP(X1) | |
| 1402 | local D = squareModP(Y1) | |
| 1403 | local E = addModP(C, D) | |
| 1404 | local H = squareModP(Z1) | |
| 1405 | local J = subModP(E, addModP(H, H)) | |
| 1406 | local X3 = multModP(subModP(B, E), J) | |
| 1407 | local Y3 = multModP(E, subModP(C, D)) | |
| 1408 | local Z3 = multModP(E, J) | |
| 1409 | local P3 = {X3, Y3, Z3}
| |
| 1410 | ||
| 1411 | return setmetatable(P3, pointMT) | |
| 1412 | end | |
| 1413 | ||
| 1414 | local function pointAdd(P1, P2) | |
| 1415 | -- 10M + 1S | |
| 1416 | local X1, Y1, Z1 = unpack(P1) | |
| 1417 | local X2, Y2, Z2 = unpack(P2) | |
| 1418 | ||
| 1419 | local A = multModP(Z1, Z2) | |
| 1420 | local B = squareModP(A) | |
| 1421 | local C = multModP(X1, X2) | |
| 1422 | local D = multModP(Y1, Y2) | |
| 1423 | local E = multModP(d, multModP(C, D)) | |
| 1424 | local F = subModP(B, E) | |
| 1425 | local G = addModP(B, E) | |
| 1426 | local X3 = multModP(A, multModP(F, subModP(multModP(addModP(X1, Y1), addModP(X2, Y2)), addModP(C, D)))) | |
| 1427 | local Y3 = multModP(A, multModP(G, subModP(D, C))) | |
| 1428 | local Z3 = multModP(F, G) | |
| 1429 | local P3 = {X3, Y3, Z3}
| |
| 1430 | ||
| 1431 | return setmetatable(P3, pointMT) | |
| 1432 | end | |
| 1433 | ||
| 1434 | local function pointNeg(P1) | |
| 1435 | local X1, Y1, Z1 = unpack(P1) | |
| 1436 | ||
| 1437 | local X3 = subModP(ZERO, X1) | |
| 1438 | local Y3 = {unpack(Y1)}
| |
| 1439 | local Z3 = {unpack(Z1)}
| |
| 1440 | local P3 = {X3, Y3, Z3}
| |
| 1441 | ||
| 1442 | return setmetatable(P3, pointMT) | |
| 1443 | end | |
| 1444 | ||
| 1445 | local function pointSub(P1, P2) | |
| 1446 | return pointAdd(P1, pointNeg(P2)) | |
| 1447 | end | |
| 1448 | ||
| 1449 | -- Converts (X:Y:Z) into (X:Y:1) = (x:y:1) | |
| 1450 | local function pointScale(P1) | |
| 1451 | local X1, Y1, Z1 = unpack(P1) | |
| 1452 | ||
| 1453 | local A = expModP(Z1, pMinusTwoBinary) | |
| 1454 | local X3 = multModP(X1, A) | |
| 1455 | local Y3 = multModP(Y1, A) | |
| 1456 | local Z3 = {unpack(ONE)}
| |
| 1457 | local P3 = {X3, Y3, Z3}
| |
| 1458 | ||
| 1459 | return setmetatable(P3, pointMT) | |
| 1460 | end | |
| 1461 | ||
| 1462 | local function pointIsEqual(P1, P2) | |
| 1463 | local X1, Y1, Z1 = unpack(P1) | |
| 1464 | local X2, Y2, Z2 = unpack(P2) | |
| 1465 | ||
| 1466 | local A1 = multModP(X1, Z2) | |
| 1467 | local B1 = multModP(Y1, Z2) | |
| 1468 | local A2 = multModP(X2, Z1) | |
| 1469 | local B2 = multModP(Y2, Z1) | |
| 1470 | ||
| 1471 | return isEqual(A1, A2) and isEqual(B1, B2) | |
| 1472 | end | |
| 1473 | ||
| 1474 | -- Checks if a projective point satisfies the curve equation | |
| 1475 | local function pointIsOnCurve(P1) | |
| 1476 | local X1, Y1, Z1 = unpack(P1) | |
| 1477 | ||
| 1478 | local X12 = squareModP(X1) | |
| 1479 | local Y12 = squareModP(Y1) | |
| 1480 | local Z12 = squareModP(Z1) | |
| 1481 | local Z14 = squareModP(Z12) | |
| 1482 | local a = addModP(X12, Y12) | |
| 1483 | a = multModP(a, Z12) | |
| 1484 | local b = multModP(d, multModP(X12, Y12)) | |
| 1485 | b = addModP(Z14, b) | |
| 1486 | ||
| 1487 | return isEqual(a, b) | |
| 1488 | end | |
| 1489 | ||
| 1490 | local function pointIsInf(P1) | |
| 1491 | return isEqual(P1[1], ZERO) | |
| 1492 | end | |
| 1493 | ||
| 1494 | -- W-ary Non-Adjacent Form (wNAF) method for scalar multiplication: | |
| 1495 | -- https://en.wikipedia.org/wiki/Elliptic_curve_point_multiplication#w-ary_non-adjacent_form_(wNAF)_method | |
| 1496 | local function scalarMult(multiplier, P1) | |
| 1497 | -- w = 5 | |
| 1498 | local naf = NAF(multiplier, 5) | |
| 1499 | local PTable = {P1}
| |
| 1500 | local P2 = pointDouble(P1) | |
| 1501 | local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}}
| |
| 1502 | ||
| 1503 | for i = 3, 31, 2 do | |
| 1504 | PTable[i] = pointAdd(PTable[i - 2], P2) | |
| 1505 | end | |
| 1506 | ||
| 1507 | for i = #naf, 1, -1 do | |
| 1508 | Q = pointDouble(Q) | |
| 1509 | if naf[i] > 0 then | |
| 1510 | Q = pointAdd(Q, PTable[naf[i]]) | |
| 1511 | elseif naf[i] < 0 then | |
| 1512 | Q = pointSub(Q, PTable[-naf[i]]) | |
| 1513 | end | |
| 1514 | end | |
| 1515 | ||
| 1516 | return setmetatable(Q, pointMT) | |
| 1517 | end | |
| 1518 | ||
| 1519 | -- Lookup table 4-ary NAF method for scalar multiplication by G. | |
| 1520 | -- Precomputations for the regular NAF method are done before the multiplication. | |
| 1521 | local GTable = {G}
| |
| 1522 | for i = 2, 168 do | |
| 1523 | GTable[i] = pointDouble(GTable[i - 1]) | |
| 1524 | end | |
| 1525 | ||
| 1526 | local function scalarMultG(multiplier) | |
| 1527 | local naf = NAF(multiplier, 2) | |
| 1528 | local Q = {{unpack(ZERO)}, {unpack(ONE)}, {unpack(ONE)}}
| |
| 1529 | ||
| 1530 | for i = 1, 168 do | |
| 1531 | if naf[i] == 1 then | |
| 1532 | Q = pointAdd(Q, GTable[i]) | |
| 1533 | elseif naf[i] == -1 then | |
| 1534 | Q = pointSub(Q, GTable[i]) | |
| 1535 | end | |
| 1536 | end | |
| 1537 | ||
| 1538 | return setmetatable(Q, pointMT) | |
| 1539 | end | |
| 1540 | ||
| 1541 | -- Point compression and encoding. | |
| 1542 | -- Compresses curve points to 22 bytes. | |
| 1543 | local function pointEncode(P1) | |
| 1544 | P1 = pointScale(P1) | |
| 1545 | local result = {}
| |
| 1546 | local x, y = unpack(P1) | |
| 1547 | ||
| 1548 | -- Encode y | |
| 1549 | result = encodeInt(y) | |
| 1550 | -- Encode one bit from x | |
| 1551 | result[22] = x[1] % 2 | |
| 1552 | ||
| 1553 | return setmetatable(result, byteTableMT) | |
| 1554 | end | |
| 1555 | ||
| 1556 | local function pointDecode(enc) | |
| 1557 | enc = strToByteArr(mapToStr(enc):sub(1, 22)) | |
| 1558 | -- Decode y | |
| 1559 | local y = decodeInt(enc) | |
| 1560 | y[7] = y[7] % p[7] | |
| 1561 | -- Find {x, -x} using curve equation
| |
| 1562 | local y2 = squareModP(y) | |
| 1563 | local u = subModP(y2, ONE) | |
| 1564 | local v = subModP(multModP(d, y2), ONE) | |
| 1565 | local u2 = squareModP(u) | |
| 1566 | local u3 = multModP(u, u2) | |
| 1567 | local u5 = multModP(u3, u2) | |
| 1568 | local v3 = multModP(v, squareModP(v)) | |
| 1569 | local w = multModP(u5, v3) | |
| 1570 | local x = multModP(u3, multModP(v, expModP(w, pMinusThreeOverFourBinary))) | |
| 1571 | -- Use enc[22] to find x from {x, -x}
| |
| 1572 | if x[1] % 2 ~= enc[22] then | |
| 1573 | x = subModP(ZERO, x) | |
| 1574 | end | |
| 1575 | local P3 = {x, y, {unpack(ONE)}}
| |
| 1576 | ||
| 1577 | return setmetatable(P3, pointMT) | |
| 1578 | end | |
| 1579 | ||
| 1580 | pointMT = {
| |
| 1581 | __index = {
| |
| 1582 | isOnCurve = function(self) | |
| 1583 | return pointIsOnCurve(self) | |
| 1584 | end, | |
| 1585 | ||
| 1586 | isInf = function(self) | |
| 1587 | return self:isOnCurve() and pointIsInf(self) | |
| 1588 | end, | |
| 1589 | ||
| 1590 | encode = function(self) | |
| 1591 | return pointEncode(self) | |
| 1592 | end | |
| 1593 | }, | |
| 1594 | ||
| 1595 | __tostring = function(self) | |
| 1596 | return self:encode():toHex() | |
| 1597 | end, | |
| 1598 | ||
| 1599 | __add = function(P1, P2) | |
| 1600 | assert(P1:isOnCurve(), "invalid point") | |
| 1601 | assert(P2:isOnCurve(), "invalid point") | |
| 1602 | ||
| 1603 | return pointAdd(P1, P2) | |
| 1604 | end, | |
| 1605 | ||
| 1606 | __sub = function(P1, P2) | |
| 1607 | assert(P1:isOnCurve(), "invalid point") | |
| 1608 | assert(P2:isOnCurve(), "invalid point") | |
| 1609 | ||
| 1610 | return pointSub(P1, P2) | |
| 1611 | end, | |
| 1612 | ||
| 1613 | __unm = function(self) | |
| 1614 | assert(self:isOnCurve(), "invalid point") | |
| 1615 | ||
| 1616 | return pointNeg(self) | |
| 1617 | end, | |
| 1618 | ||
| 1619 | __eq = function(P1, P2) | |
| 1620 | assert(P1:isOnCurve(), "invalid point") | |
| 1621 | assert(P2:isOnCurve(), "invalid point") | |
| 1622 | ||
| 1623 | return pointIsEqual(P1, P2) | |
| 1624 | end, | |
| 1625 | ||
| 1626 | __mul = function(P1, s) | |
| 1627 | if type(P1) == "number" then | |
| 1628 | return s * P1 | |
| 1629 | end | |
| 1630 | ||
| 1631 | if type(s) == "number" then | |
| 1632 | assert(s < 2^24, "number multiplier too big") | |
| 1633 | s = {s, 0, 0, 0, 0, 0, 0}
| |
| 1634 | else | |
| 1635 | s = inverseMontgomeryModQ(s) | |
| 1636 | end | |
| 1637 | ||
| 1638 | if P1 == G then | |
| 1639 | return scalarMultG(s) | |
| 1640 | else | |
| 1641 | return scalarMult(s, P1) | |
| 1642 | end | |
| 1643 | end | |
| 1644 | } | |
| 1645 | ||
| 1646 | G = setmetatable(G, pointMT) | |
| 1647 | O = setmetatable(O, pointMT) | |
| 1648 | ||
| 1649 | return {
| |
| 1650 | G = G, | |
| 1651 | O = O, | |
| 1652 | pointDecode = pointDecode | |
| 1653 | } | |
| 1654 | end)() | |
| 1655 | ||
| 1656 | local function getNonceFromEpoch() | |
| 1657 | local nonce = {}
| |
| 1658 | local epoch = os.epoch("utc")
| |
| 1659 | for _ = 1, 12 do | |
| 1660 | nonce[#nonce + 1] = epoch % 256 | |
| 1661 | epoch = epoch / 256 | |
| 1662 | epoch = epoch - epoch % 1 | |
| 1663 | end | |
| 1664 | ||
| 1665 | return nonce | |
| 1666 | end | |
| 1667 | ||
| 1668 | local function encrypt(data, key) | |
| 1669 | key = mapToStr(key) | |
| 1670 | local encKey = sha256.hmac("encKey", key)
| |
| 1671 | local macKey = sha256.hmac("macKey", key)
| |
| 1672 | local nonce = getNonceFromEpoch() | |
| 1673 | local ciphertext = chacha20.crypt(mapToStr(data), encKey, nonce) | |
| 1674 | local result = nonce | |
| 1675 | for i = 1, #ciphertext do | |
| 1676 | result[#result + 1] = ciphertext[i] | |
| 1677 | end | |
| 1678 | local mac = sha256.hmac(result, macKey) | |
| 1679 | for i = 1, #mac do | |
| 1680 | result[#result + 1] = mac[i] | |
| 1681 | end | |
| 1682 | ||
| 1683 | return setmetatable(result, byteTableMT) | |
| 1684 | end | |
| 1685 | ||
| 1686 | local function decrypt(data, key) | |
| 1687 | data = mapToStr(data) | |
| 1688 | key = mapToStr(key) | |
| 1689 | local encKey = sha256.hmac("encKey", key)
| |
| 1690 | local macKey = sha256.hmac("macKey", key)
| |
| 1691 | local mac = sha256.hmac(data:sub(1, -33), macKey) | |
| 1692 | assert(mac:isEqual(strToByteArr(data:sub(-32))), "invalid mac") | |
| 1693 | local result = chacha20.crypt(data:sub(13, -33), encKey, data:sub(1, 12)) | |
| 1694 | ||
| 1695 | return setmetatable(result, byteTableMT) | |
| 1696 | end | |
| 1697 | ||
| 1698 | local function keypair(seed) | |
| 1699 | local x | |
| 1700 | if seed then | |
| 1701 | x = modq.hashModQ(mapToStr(seed)) | |
| 1702 | else | |
| 1703 | x = modq.randomModQ() | |
| 1704 | end | |
| 1705 | local Y = curve.G * x | |
| 1706 | ||
| 1707 | local privateKey = x:encode() | |
| 1708 | local publicKey = Y:encode() | |
| 1709 | ||
| 1710 | return privateKey, publicKey | |
| 1711 | end | |
| 1712 | ||
| 1713 | local function exchange(privateKey, publicKey) | |
| 1714 | local x = modq.decodeModQ(mapToStr(privateKey)) | |
| 1715 | local Y = curve.pointDecode(mapToStr(publicKey)) | |
| 1716 | ||
| 1717 | local Z = Y * x | |
| 1718 | ||
| 1719 | local sharedSecret = sha256.digest(Z:encode()) | |
| 1720 | ||
| 1721 | return sharedSecret | |
| 1722 | end | |
| 1723 | ||
| 1724 | local function sign(privateKey, message) | |
| 1725 | local x = modq.decodeModQ(mapToStr(privateKey)) | |
| 1726 | local k = modq.randomModQ() | |
| 1727 | local R = curve.G * k | |
| 1728 | local e = modq.hashModQ(mapToStr(message) .. tostring(R)) | |
| 1729 | local s = k - x * e | |
| 1730 | ||
| 1731 | e = e:encode() | |
| 1732 | s = s:encode() | |
| 1733 | ||
| 1734 | local result = e | |
| 1735 | for i = 1, #s do | |
| 1736 | result[#result + 1] = s[i] | |
| 1737 | end | |
| 1738 | ||
| 1739 | return setmetatable(result, byteTableMT) | |
| 1740 | end | |
| 1741 | ||
| 1742 | local function verify(publicKey, message, signature) | |
| 1743 | signature = mapToStr(signature) | |
| 1744 | local Y = curve.pointDecode(mapToStr(publicKey)) | |
| 1745 | local e = modq.decodeModQ(signature:sub(1, 21)) | |
| 1746 | local s = modq.decodeModQ(signature:sub(22)) | |
| 1747 | local Rv = curve.G * s + Y * e | |
| 1748 | local ev = modq.hashModQ(mapToStr(message) .. tostring(Rv)) | |
| 1749 | ||
| 1750 | return ev == e | |
| 1751 | end | |
| 1752 | ||
| 1753 | return {
| |
| 1754 | chacha20 = chacha20, | |
| 1755 | sha256 = sha256, | |
| 1756 | random = random, | |
| 1757 | encrypt = encrypt, | |
| 1758 | decrypt = decrypt, | |
| 1759 | keypair = keypair, | |
| 1760 | exchange = exchange, | |
| 1761 | sign = sign, | |
| 1762 | verify = verify | |
| 1763 | } | |
| 1764 |