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