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 | } |