View difference between Paste ID: ykK4UMa6 and ZGJGBJdg
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
}