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