@quenty/ellipticcurvecryptography 1.11.0 → 1.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +21 -87
- package/deploy.nevermore.json +10 -0
- package/package.json +2 -2
- package/src/Shared/EllipticCurveCryptography/arith.lua +33 -23
- package/src/Shared/EllipticCurveCryptography/chacha20.lua +31 -20
- package/src/Shared/EllipticCurveCryptography/curve.lua +8 -8
- package/src/Shared/EllipticCurveCryptography/init.lua +49 -40
- package/src/Shared/EllipticCurveCryptography/modp.lua +23 -20
- package/src/Shared/EllipticCurveCryptography/modq.lua +53 -51
- package/src/Shared/EllipticCurveCryptography/random.lua +9 -8
- package/src/Shared/EllipticCurveCryptography/sha256.lua +43 -33
- package/src/Shared/EllipticCurveCryptography/twoPower.lua +4 -4
- package/test/default.project.json +25 -0
- package/test/scripts/Server/ServerMain.server.lua +13 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--!
|
|
1
|
+
--!strict
|
|
2
2
|
--!native
|
|
3
3
|
|
|
4
4
|
--[=[
|
|
@@ -39,6 +39,9 @@ local modq = require(script.modq)
|
|
|
39
39
|
local random = require(script.random)
|
|
40
40
|
local sha256 = require(script.sha256)
|
|
41
41
|
local util = require(script.util)
|
|
42
|
+
|
|
43
|
+
type ByteTable = typeof(setmetatable({} :: { number }, {} :: typeof(util.byteTableMT)))
|
|
44
|
+
|
|
42
45
|
local EllipticCurveCryptography = {}
|
|
43
46
|
|
|
44
47
|
EllipticCurveCryptography.chacha20 = chacha20
|
|
@@ -52,7 +55,7 @@ EllipticCurveCryptography._byteMetatable = assert(util.byteTableMT, "No byteTabl
|
|
|
52
55
|
@param key any
|
|
53
56
|
@return boolean
|
|
54
57
|
]=]
|
|
55
|
-
function EllipticCurveCryptography.isByteTable(key)
|
|
58
|
+
function EllipticCurveCryptography.isByteTable(key: any): boolean
|
|
56
59
|
return type(key) == "table" and getmetatable(key) == EllipticCurveCryptography._byteMetatable
|
|
57
60
|
end
|
|
58
61
|
|
|
@@ -61,14 +64,14 @@ end
|
|
|
61
64
|
|
|
62
65
|
@param value any
|
|
63
66
|
]=]
|
|
64
|
-
function EllipticCurveCryptography.createByteTable(value)
|
|
67
|
+
function EllipticCurveCryptography.createByteTable(value: { number }): ByteTable
|
|
65
68
|
assert(type(value) == "table", "Bad value")
|
|
66
69
|
|
|
67
70
|
return setmetatable(table.clone(value), EllipticCurveCryptography._byteMetatable)
|
|
68
71
|
end
|
|
69
72
|
|
|
70
|
-
function EllipticCurveCryptography._getNonceFromEpoch()
|
|
71
|
-
local nonce = table.create(12)
|
|
73
|
+
function EllipticCurveCryptography._getNonceFromEpoch(): { number }
|
|
74
|
+
local nonce: { number } = table.create(12)
|
|
72
75
|
local epoch = DateTime.now().UnixTimestampMillis
|
|
73
76
|
for i = 1, 12 do
|
|
74
77
|
nonce[i] = epoch % 256
|
|
@@ -98,23 +101,23 @@ end
|
|
|
98
101
|
@param key ByteTable
|
|
99
102
|
@return ByteTable
|
|
100
103
|
]=]
|
|
101
|
-
function EllipticCurveCryptography.encrypt(data, key)
|
|
104
|
+
function EllipticCurveCryptography.encrypt(data: string | ByteTable, key: ByteTable): ByteTable
|
|
102
105
|
assert(type(data) == "string" or EllipticCurveCryptography.isByteTable(data), "Bad data")
|
|
103
106
|
assert(EllipticCurveCryptography.isByteTable(key), "Bad key")
|
|
104
107
|
|
|
105
|
-
local encKey = sha256.hmac("encKey", key)
|
|
106
|
-
local macKey = sha256.hmac("macKey", key)
|
|
108
|
+
local encKey = sha256.hmac("encKey", (key :: any) :: { number })
|
|
109
|
+
local macKey = sha256.hmac("macKey", (key :: any) :: { number })
|
|
107
110
|
local nonce = EllipticCurveCryptography._getNonceFromEpoch()
|
|
108
111
|
|
|
109
|
-
local ciphertext = chacha20.crypt(data, encKey, nonce)
|
|
112
|
+
local ciphertext = chacha20.crypt(data :: any, (encKey :: any) :: { number }, nonce)
|
|
110
113
|
|
|
111
|
-
local result = nonce
|
|
112
|
-
for _, value in ipairs(ciphertext) do
|
|
114
|
+
local result: { number } = nonce
|
|
115
|
+
for _, value in ipairs(ciphertext :: any) do
|
|
113
116
|
table.insert(result, value)
|
|
114
117
|
end
|
|
115
118
|
|
|
116
|
-
local mac = sha256.hmac(result, macKey)
|
|
117
|
-
for _, value in ipairs(mac) do
|
|
119
|
+
local mac = sha256.hmac(result, (macKey :: any) :: { number })
|
|
120
|
+
for _, value in ipairs((mac :: any) :: { number }) do
|
|
118
121
|
table.insert(result, value)
|
|
119
122
|
end
|
|
120
123
|
|
|
@@ -135,21 +138,22 @@ end
|
|
|
135
138
|
@param key ByteTable
|
|
136
139
|
@return ByteTable
|
|
137
140
|
]=]
|
|
138
|
-
function EllipticCurveCryptography.decrypt(data, key)
|
|
141
|
+
function EllipticCurveCryptography.decrypt(data: string | ByteTable, key: ByteTable): ByteTable
|
|
139
142
|
assert(type(data) == "string" or EllipticCurveCryptography.isByteTable(data), "Bad data")
|
|
140
143
|
assert(EllipticCurveCryptography.isByteTable(key), "Bad key")
|
|
141
144
|
|
|
142
|
-
local actualData = type(data) == "table" and { table.unpack(data
|
|
143
|
-
|
|
144
|
-
local
|
|
145
|
-
local
|
|
145
|
+
local actualData: { number } = type(data) == "table" and { table.unpack(data :: any) }
|
|
146
|
+
or { string.byte(tostring(data), 1, -1) }
|
|
147
|
+
local encKey = sha256.hmac("encKey", (key :: any) :: { number })
|
|
148
|
+
local macKey = sha256.hmac("macKey", (key :: any) :: { number })
|
|
149
|
+
local mac = sha256.hmac({ table.unpack(actualData, 1, #actualData - 32) }, (macKey :: any) :: { number })
|
|
146
150
|
local messageMac = { table.unpack(actualData, #actualData - 31) }
|
|
147
|
-
assert(mac:isEqual(messageMac), "invalid mac")
|
|
151
|
+
assert((mac :: any):isEqual(messageMac), "invalid mac")
|
|
148
152
|
local nonce = { table.unpack(actualData, 1, 12) }
|
|
149
153
|
local ciphertext = { table.unpack(actualData, 13, #actualData - 32) }
|
|
150
|
-
local result = chacha20.crypt(ciphertext, encKey, nonce)
|
|
154
|
+
local result: ByteTable = chacha20.crypt(ciphertext, (encKey :: any) :: { number }, nonce)
|
|
151
155
|
|
|
152
|
-
return
|
|
156
|
+
return result
|
|
153
157
|
end
|
|
154
158
|
|
|
155
159
|
--[=[
|
|
@@ -167,10 +171,10 @@ end
|
|
|
167
171
|
@return ByteTable -- privateKey
|
|
168
172
|
@return ByteTable -- publicKey
|
|
169
173
|
]=]
|
|
170
|
-
function EllipticCurveCryptography.keypair(seed)
|
|
174
|
+
function EllipticCurveCryptography.keypair(seed: number): (ByteTable, ByteTable)
|
|
171
175
|
assert(type(seed) == "number", "Bad seed")
|
|
172
176
|
|
|
173
|
-
local x
|
|
177
|
+
local x: any
|
|
174
178
|
if seed then
|
|
175
179
|
x = modq.hashModQ(seed)
|
|
176
180
|
else
|
|
@@ -179,8 +183,8 @@ function EllipticCurveCryptography.keypair(seed)
|
|
|
179
183
|
|
|
180
184
|
local Y = curve.G * x
|
|
181
185
|
|
|
182
|
-
local privateKey = x:encode()
|
|
183
|
-
local publicKey = Y:encode()
|
|
186
|
+
local privateKey: ByteTable = x:encode()
|
|
187
|
+
local publicKey: ByteTable = Y:encode()
|
|
184
188
|
|
|
185
189
|
return privateKey, publicKey
|
|
186
190
|
end
|
|
@@ -203,15 +207,15 @@ end
|
|
|
203
207
|
@param publicKey ByteTable
|
|
204
208
|
@return ByteTable
|
|
205
209
|
]=]
|
|
206
|
-
function EllipticCurveCryptography.exchange(privateKey, publicKey)
|
|
210
|
+
function EllipticCurveCryptography.exchange(privateKey: ByteTable, publicKey: ByteTable): ByteTable
|
|
207
211
|
assert(EllipticCurveCryptography.isByteTable(privateKey), "Bad privateKey")
|
|
208
212
|
assert(EllipticCurveCryptography.isByteTable(publicKey), "Bad publicKey")
|
|
209
213
|
|
|
210
214
|
local x = modq.decodeModQ(privateKey)
|
|
211
|
-
local Y = curve.pointDecode(publicKey)
|
|
215
|
+
local Y = curve.pointDecode(publicKey :: any)
|
|
212
216
|
local Z = Y * x
|
|
213
217
|
|
|
214
|
-
local sharedSecret = sha256.digest(Z:encode())
|
|
218
|
+
local sharedSecret: ByteTable = sha256.digest(Z:encode())
|
|
215
219
|
|
|
216
220
|
return sharedSecret
|
|
217
221
|
end
|
|
@@ -227,24 +231,25 @@ end
|
|
|
227
231
|
@param message string | EncodedMessage
|
|
228
232
|
@return ByteTable
|
|
229
233
|
]=]
|
|
230
|
-
function EllipticCurveCryptography.sign(privateKey, message)
|
|
234
|
+
function EllipticCurveCryptography.sign(privateKey: ByteTable, message: string | ByteTable): ByteTable
|
|
231
235
|
assert(EllipticCurveCryptography.isByteTable(privateKey), "Bad privateKey")
|
|
232
236
|
assert(type(message) == "string" or EllipticCurveCryptography.isByteTable(message), "Bad message")
|
|
233
237
|
|
|
234
|
-
local actualMessage = type(message) == "table" and string.char(table.unpack(message)) or tostring(message)
|
|
235
|
-
local actualPrivateKey = type(privateKey) == "table" and string.char(table.unpack(privateKey))
|
|
238
|
+
local actualMessage = type(message) == "table" and string.char(table.unpack(message :: any)) or tostring(message)
|
|
239
|
+
local actualPrivateKey = type(privateKey) == "table" and string.char(table.unpack(privateKey :: any))
|
|
236
240
|
or tostring(privateKey)
|
|
237
241
|
|
|
238
|
-
local x = modq.decodeModQ(actualPrivateKey)
|
|
239
|
-
local k = modq.randomModQ()
|
|
242
|
+
local x: any = modq.decodeModQ(actualPrivateKey)
|
|
243
|
+
local k: any = modq.randomModQ()
|
|
240
244
|
local R = curve.G * k
|
|
241
|
-
local e = modq.hashModQ(actualMessage .. tostring(R))
|
|
245
|
+
local e: any = modq.hashModQ(actualMessage .. tostring(R))
|
|
242
246
|
local s = k - x * e
|
|
243
247
|
|
|
244
248
|
e = e:encode()
|
|
245
249
|
s = s:encode()
|
|
246
250
|
|
|
247
|
-
local result
|
|
251
|
+
local result: { number } = e
|
|
252
|
+
local result_len = #e
|
|
248
253
|
for index, value in ipairs(s) do
|
|
249
254
|
result[result_len + index] = value
|
|
250
255
|
end
|
|
@@ -265,16 +270,20 @@ end
|
|
|
265
270
|
@param message ByteTable | string
|
|
266
271
|
@param signature ByteTable
|
|
267
272
|
]=]
|
|
268
|
-
function EllipticCurveCryptography.verify(
|
|
273
|
+
function EllipticCurveCryptography.verify(
|
|
274
|
+
publicKey: ByteTable,
|
|
275
|
+
message: string | ByteTable,
|
|
276
|
+
signature: ByteTable
|
|
277
|
+
): boolean
|
|
269
278
|
assert(EllipticCurveCryptography.isByteTable(publicKey), "Bad privateKey")
|
|
270
279
|
assert(type(message) == "string" or EllipticCurveCryptography.isByteTable(message), "Bad message")
|
|
271
280
|
assert(EllipticCurveCryptography.isByteTable(signature), "Bad signature")
|
|
272
281
|
|
|
273
|
-
local actualMessage = type(message) == "table" and string.char(table.unpack(message)) or tostring(message)
|
|
282
|
+
local actualMessage = type(message) == "table" and string.char(table.unpack(message :: any)) or tostring(message)
|
|
274
283
|
local sigLen = #signature
|
|
275
|
-
local Y = curve.pointDecode(publicKey)
|
|
276
|
-
local e = modq.decodeModQ({ table.unpack(signature, 1, sigLen / 2) })
|
|
277
|
-
local s = modq.decodeModQ({ table.unpack(signature, sigLen / 2 + 1) })
|
|
284
|
+
local Y = curve.pointDecode(publicKey :: any)
|
|
285
|
+
local e = modq.decodeModQ({ table.unpack(signature :: any, 1, sigLen / 2) })
|
|
286
|
+
local s = modq.decodeModQ({ table.unpack(signature :: any, sigLen / 2 + 1) })
|
|
278
287
|
local Rv = curve.G * s + Y * e
|
|
279
288
|
local ev = modq.hashModQ(actualMessage .. tostring(Rv))
|
|
280
289
|
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--!
|
|
1
|
+
--!strict
|
|
2
2
|
-- Arithmetic on the finite field of integers modulo p
|
|
3
3
|
-- Where p is the finite field modulus
|
|
4
4
|
local arith = require(script.Parent.arith)
|
|
@@ -8,17 +8,20 @@ local addDouble = arith.addDouble
|
|
|
8
8
|
local mult = arith.mult
|
|
9
9
|
local square = arith.square
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
type BigInt = { number }
|
|
12
|
+
type DoubleInt = { number }
|
|
13
|
+
|
|
14
|
+
local p: BigInt = { 3, 0, 0, 0, 0, 0, 15761408 }
|
|
12
15
|
|
|
13
16
|
-- We're using the Montgomery Reduction for fast modular multiplication.
|
|
14
17
|
-- https://en.wikipedia.org/wiki/Montgomery_modular_multiplication
|
|
15
18
|
-- r = 2^168
|
|
16
19
|
-- p * pInverse = -1 (mod r)
|
|
17
20
|
-- r2 = r * r (mod p)
|
|
18
|
-
local pInverse = { 5592405, 5592405, 5592405, 5592405, 5592405, 5592405, 14800213 }
|
|
19
|
-
local r2 = { 13533400, 837116, 6278376, 13533388, 837116, 6278376, 7504076 }
|
|
21
|
+
local pInverse: BigInt = { 5592405, 5592405, 5592405, 5592405, 5592405, 5592405, 14800213 }
|
|
22
|
+
local r2: BigInt = { 13533400, 837116, 6278376, 13533388, 837116, 6278376, 7504076 }
|
|
20
23
|
|
|
21
|
-
local function multByP(a)
|
|
24
|
+
local function multByP(a: BigInt): DoubleInt
|
|
22
25
|
local a1, a2, a3, a4, a5, a6, a7 = a[1], a[2], a[3], a[4], a[5], a[6], a[7]
|
|
23
26
|
|
|
24
27
|
local c1 = a1 * 3
|
|
@@ -82,7 +85,7 @@ local function multByP(a)
|
|
|
82
85
|
end
|
|
83
86
|
|
|
84
87
|
-- Reduces a number from [0, 2p - 1] to [0, p - 1]
|
|
85
|
-
local function reduceModP(a)
|
|
88
|
+
local function reduceModP(a: BigInt): BigInt
|
|
86
89
|
-- a < p
|
|
87
90
|
if a[7] < 15761408 or a[7] == 15761408 and a[1] < 3 then
|
|
88
91
|
return { table.unpack(a) }
|
|
@@ -133,11 +136,11 @@ local function reduceModP(a)
|
|
|
133
136
|
return { c1, c2, c3, c4, c5, c6, c7 }
|
|
134
137
|
end
|
|
135
138
|
|
|
136
|
-
local function addModP(a, b)
|
|
139
|
+
local function addModP(a: BigInt, b: BigInt): BigInt
|
|
137
140
|
return reduceModP(add(a, b))
|
|
138
141
|
end
|
|
139
142
|
|
|
140
|
-
local function subModP(a, b)
|
|
143
|
+
local function subModP(a: BigInt, b: BigInt): BigInt
|
|
141
144
|
local result = sub(a, b)
|
|
142
145
|
|
|
143
146
|
if result[7] < 0 then
|
|
@@ -149,29 +152,29 @@ end
|
|
|
149
152
|
|
|
150
153
|
-- Montgomery REDC algorithn
|
|
151
154
|
-- Reduces a number from [0, p^2 - 1] to [0, p - 1]
|
|
152
|
-
local function REDC(T)
|
|
155
|
+
local function REDC(T: DoubleInt): BigInt
|
|
153
156
|
local m = mult(T, pInverse, true)
|
|
154
|
-
local t = { table.unpack(addDouble(T, multByP(m)), 8, 14) }
|
|
157
|
+
local t: BigInt = { table.unpack(addDouble(T, multByP(m)), 8, 14) }
|
|
155
158
|
|
|
156
159
|
return reduceModP(t)
|
|
157
160
|
end
|
|
158
161
|
|
|
159
|
-
local function multModP(a, b)
|
|
162
|
+
local function multModP(a: BigInt, b: BigInt): BigInt
|
|
160
163
|
-- Only works with a, b in Montgomery form
|
|
161
|
-
return REDC(mult(a, b))
|
|
164
|
+
return REDC(mult(a, b, false))
|
|
162
165
|
end
|
|
163
166
|
|
|
164
|
-
local function squareModP(a)
|
|
167
|
+
local function squareModP(a: BigInt): BigInt
|
|
165
168
|
-- Only works with a in Montgomery form
|
|
166
169
|
return REDC(square(a))
|
|
167
170
|
end
|
|
168
171
|
|
|
169
|
-
local function montgomeryModP(a)
|
|
172
|
+
local function montgomeryModP(a: BigInt): BigInt
|
|
170
173
|
return multModP(a, r2)
|
|
171
174
|
end
|
|
172
175
|
|
|
173
|
-
local function inverseMontgomeryModP(a)
|
|
174
|
-
local newA = { table.unpack(a) }
|
|
176
|
+
local function inverseMontgomeryModP(a: BigInt): BigInt
|
|
177
|
+
local newA: DoubleInt = { table.unpack(a) }
|
|
175
178
|
|
|
176
179
|
for i = 8, 14 do
|
|
177
180
|
newA[i] = 0
|
|
@@ -180,11 +183,11 @@ local function inverseMontgomeryModP(a)
|
|
|
180
183
|
return REDC(newA)
|
|
181
184
|
end
|
|
182
185
|
|
|
183
|
-
local ONE = montgomeryModP({ 1, 0, 0, 0, 0, 0, 0 })
|
|
186
|
+
local ONE: BigInt = montgomeryModP({ 1, 0, 0, 0, 0, 0, 0 })
|
|
184
187
|
|
|
185
|
-
local function expModP(base, exponentBinary)
|
|
186
|
-
local newBase = { table.unpack(base) }
|
|
187
|
-
local result = { table.unpack(ONE) }
|
|
188
|
+
local function expModP(base: BigInt, exponentBinary: { number }): BigInt
|
|
189
|
+
local newBase: BigInt = { table.unpack(base) }
|
|
190
|
+
local result: BigInt = { table.unpack(ONE) }
|
|
188
191
|
|
|
189
192
|
for i = 1, 168 do
|
|
190
193
|
if exponentBinary[i] == 1 then
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
--!
|
|
1
|
+
--!strict
|
|
2
2
|
--!native
|
|
3
3
|
|
|
4
4
|
-- Arithmetic on the Finite Field of Integers modulo q
|
|
@@ -8,6 +8,8 @@ local random = require(script.Parent.random)
|
|
|
8
8
|
local sha256 = require(script.Parent.sha256)
|
|
9
9
|
local util = require(script.Parent.util)
|
|
10
10
|
|
|
11
|
+
type BigInt = { number }
|
|
12
|
+
|
|
11
13
|
local isEqual = arith.isEqual
|
|
12
14
|
local compare = arith.compare
|
|
13
15
|
local add = arith.add
|
|
@@ -18,11 +20,11 @@ local square = arith.square
|
|
|
18
20
|
local encodeInt = arith.encodeInt
|
|
19
21
|
local decodeInt = arith.decodeInt
|
|
20
22
|
|
|
21
|
-
local modQMT
|
|
23
|
+
local modQMT: any
|
|
22
24
|
|
|
23
|
-
local q = { 9622359, 6699217, 13940450, 16775734, 16777215, 16777215, 3940351 }
|
|
25
|
+
local q: BigInt = { 9622359, 6699217, 13940450, 16775734, 16777215, 16777215, 3940351 }
|
|
24
26
|
-- this isn't an optimization, it just shortens the amount of time I have to scroll
|
|
25
|
-
local qMinusTwoBinary = table.create(166, 1)
|
|
27
|
+
local qMinusTwoBinary: { number } = table.create(166, 1)
|
|
26
28
|
qMinusTwoBinary[2] = 0
|
|
27
29
|
qMinusTwoBinary[4] = 0
|
|
28
30
|
qMinusTwoBinary[6] = 0
|
|
@@ -76,59 +78,59 @@ qMinusTwoBinary[162] = 0
|
|
|
76
78
|
-- r = 2^168
|
|
77
79
|
-- q * qInverse = -1 (mod r)
|
|
78
80
|
-- r2 = r * r (mod q)
|
|
79
|
-
local qInverse = { 15218585, 5740955, 3271338, 9903997, 9067368, 7173545, 6988392 }
|
|
80
|
-
local r2 = { 1336213, 11071705, 9716828, 11083885, 9188643, 1494868, 3306114 }
|
|
81
|
+
local qInverse: BigInt = { 15218585, 5740955, 3271338, 9903997, 9067368, 7173545, 6988392 }
|
|
82
|
+
local r2: BigInt = { 1336213, 11071705, 9716828, 11083885, 9188643, 1494868, 3306114 }
|
|
81
83
|
|
|
82
84
|
-- Reduces a number from [0, 2q - 1] to [0, q - 1]
|
|
83
|
-
local function reduceModQ(a)
|
|
84
|
-
local result = { table.unpack(a) }
|
|
85
|
+
local function reduceModQ(a: BigInt): BigInt
|
|
86
|
+
local result: BigInt = { table.unpack(a) }
|
|
85
87
|
|
|
86
88
|
if compare(result, q) >= 0 then
|
|
87
89
|
result = sub(result, q)
|
|
88
90
|
end
|
|
89
91
|
|
|
90
|
-
return setmetatable(result, modQMT)
|
|
92
|
+
return setmetatable(result, modQMT) :: any
|
|
91
93
|
end
|
|
92
94
|
|
|
93
|
-
local function addModQ(a, b)
|
|
95
|
+
local function addModQ(a: BigInt, b: BigInt): BigInt
|
|
94
96
|
return reduceModQ(add(a, b))
|
|
95
97
|
end
|
|
96
98
|
|
|
97
|
-
local function subModQ(a, b)
|
|
98
|
-
local result = sub(a, b)
|
|
99
|
+
local function subModQ(a: BigInt, b: BigInt): BigInt
|
|
100
|
+
local result: BigInt = sub(a, b)
|
|
99
101
|
|
|
100
102
|
if result[7] < 0 then
|
|
101
103
|
result = add(result, q)
|
|
102
104
|
end
|
|
103
105
|
|
|
104
|
-
return setmetatable(result, modQMT)
|
|
106
|
+
return setmetatable(result, modQMT) :: any
|
|
105
107
|
end
|
|
106
108
|
|
|
107
109
|
-- Montgomery REDC algorithn
|
|
108
110
|
-- Reduces a number from [0, q^2 - 1] to [0, q - 1]
|
|
109
|
-
local function REDC(T)
|
|
110
|
-
local m = { table.unpack(mult({ table.unpack(T, 1, 7) }, qInverse, true), 1, 7) }
|
|
111
|
-
local t = { table.unpack(addDouble(T, mult(m, q)), 8, 14) }
|
|
111
|
+
local function REDC(T: { number }): BigInt
|
|
112
|
+
local m: BigInt = { table.unpack(mult({ table.unpack(T, 1, 7) }, qInverse, true), 1, 7) }
|
|
113
|
+
local t: BigInt = { table.unpack(addDouble(T, mult(m, q, false)), 8, 14) }
|
|
112
114
|
|
|
113
115
|
return reduceModQ(t)
|
|
114
116
|
end
|
|
115
117
|
|
|
116
|
-
local function multModQ(a, b)
|
|
118
|
+
local function multModQ(a: BigInt, b: BigInt): BigInt
|
|
117
119
|
-- Only works with a, b in Montgomery form
|
|
118
|
-
return REDC(mult(a, b))
|
|
120
|
+
return REDC(mult(a, b, false))
|
|
119
121
|
end
|
|
120
122
|
|
|
121
|
-
local function squareModQ(a)
|
|
123
|
+
local function squareModQ(a: BigInt): BigInt
|
|
122
124
|
-- Only works with a in Montgomery form
|
|
123
125
|
return REDC(square(a))
|
|
124
126
|
end
|
|
125
127
|
|
|
126
|
-
local function montgomeryModQ(a)
|
|
128
|
+
local function montgomeryModQ(a: BigInt): BigInt
|
|
127
129
|
return multModQ(a, r2)
|
|
128
130
|
end
|
|
129
131
|
|
|
130
|
-
local function inverseMontgomeryModQ(a)
|
|
131
|
-
local newA = { table.unpack(a) }
|
|
132
|
+
local function inverseMontgomeryModQ(a: BigInt): BigInt
|
|
133
|
+
local newA: { number } = { table.unpack(a) }
|
|
132
134
|
|
|
133
135
|
for i = 8, 14 do
|
|
134
136
|
newA[i] = 0
|
|
@@ -137,11 +139,11 @@ local function inverseMontgomeryModQ(a)
|
|
|
137
139
|
return REDC(newA)
|
|
138
140
|
end
|
|
139
141
|
|
|
140
|
-
local ONE = montgomeryModQ({ 1, 0, 0, 0, 0, 0, 0 })
|
|
142
|
+
local ONE: BigInt = montgomeryModQ({ 1, 0, 0, 0, 0, 0, 0 })
|
|
141
143
|
|
|
142
|
-
local function expModQ(base, exponentBinary)
|
|
143
|
-
local newBase = { table.unpack(base) }
|
|
144
|
-
local result = { table.unpack(ONE) }
|
|
144
|
+
local function expModQ(base: BigInt, exponentBinary: { number }): BigInt
|
|
145
|
+
local newBase: BigInt = { table.unpack(base) }
|
|
146
|
+
local result: BigInt = { table.unpack(ONE) }
|
|
145
147
|
|
|
146
148
|
for i = 1, 168 do
|
|
147
149
|
if exponentBinary[i] == 1 then
|
|
@@ -154,9 +156,9 @@ local function expModQ(base, exponentBinary)
|
|
|
154
156
|
return result
|
|
155
157
|
end
|
|
156
158
|
|
|
157
|
-
local function intExpModQ(base, exponent)
|
|
158
|
-
local newBase = { table.unpack(base) }
|
|
159
|
-
local result = setmetatable({ table.unpack(ONE) }, modQMT)
|
|
159
|
+
local function intExpModQ(base: BigInt, exponent: number): BigInt
|
|
160
|
+
local newBase: BigInt = { table.unpack(base) }
|
|
161
|
+
local result: BigInt = setmetatable({ table.unpack(ONE) }, modQMT) :: any
|
|
160
162
|
|
|
161
163
|
if exponent < 0 then
|
|
162
164
|
newBase = expModQ(newBase, qMinusTwoBinary)
|
|
@@ -175,46 +177,46 @@ local function intExpModQ(base, exponent)
|
|
|
175
177
|
return result
|
|
176
178
|
end
|
|
177
179
|
|
|
178
|
-
local function encodeModQ(a)
|
|
180
|
+
local function encodeModQ(a: BigInt): { number }
|
|
179
181
|
local result = encodeInt(a)
|
|
180
182
|
|
|
181
|
-
return setmetatable(result, util.byteTableMT)
|
|
183
|
+
return setmetatable(result, util.byteTableMT) :: any
|
|
182
184
|
end
|
|
183
185
|
|
|
184
|
-
local function decodeModQ(s)
|
|
185
|
-
s = type(s) == "table" and { table.unpack(s, 1, 21) } or { string.byte(tostring(s), 1, 21) }
|
|
186
|
-
local result = decodeInt(s)
|
|
186
|
+
local function decodeModQ(s: any): BigInt
|
|
187
|
+
s = type(s) == "table" and { table.unpack(s :: { number }, 1, 21) } or { string.byte(tostring(s), 1, 21) }
|
|
188
|
+
local result: BigInt = decodeInt(s)
|
|
187
189
|
result[7] %= q[7]
|
|
188
190
|
|
|
189
|
-
return setmetatable(result, modQMT)
|
|
191
|
+
return setmetatable(result, modQMT) :: any
|
|
190
192
|
end
|
|
191
193
|
|
|
192
|
-
local function randomModQ()
|
|
194
|
+
local function randomModQ(): BigInt
|
|
193
195
|
while true do
|
|
194
|
-
local s = { table.unpack(random.random(), 1, 21) }
|
|
195
|
-
local result = decodeInt(s)
|
|
196
|
+
local s: { number } = { table.unpack(random.random() :: any, 1, 21) }
|
|
197
|
+
local result: BigInt = decodeInt(s)
|
|
196
198
|
if result[7] < q[7] then
|
|
197
|
-
return setmetatable(result, modQMT)
|
|
199
|
+
return setmetatable(result, modQMT) :: any
|
|
198
200
|
end
|
|
199
201
|
end
|
|
200
202
|
end
|
|
201
203
|
|
|
202
|
-
local function hashModQ(data)
|
|
204
|
+
local function hashModQ(data: any): BigInt
|
|
203
205
|
return decodeModQ(sha256.digest(data))
|
|
204
206
|
end
|
|
205
207
|
|
|
206
208
|
modQMT = {
|
|
207
209
|
__index = {
|
|
208
|
-
encode = function(self)
|
|
210
|
+
encode = function(self: BigInt): { number }
|
|
209
211
|
return encodeModQ(self)
|
|
210
212
|
end,
|
|
211
213
|
},
|
|
212
214
|
|
|
213
|
-
__tostring = function(self)
|
|
214
|
-
return self:encode():toHex()
|
|
215
|
+
__tostring = function(self: BigInt): string
|
|
216
|
+
return (self :: any):encode():toHex()
|
|
215
217
|
end,
|
|
216
218
|
|
|
217
|
-
__add = function(self, other)
|
|
219
|
+
__add = function(self: any, other: any): BigInt
|
|
218
220
|
if type(self) == "number" then
|
|
219
221
|
return other + self
|
|
220
222
|
end
|
|
@@ -227,7 +229,7 @@ modQMT = {
|
|
|
227
229
|
return addModQ(self, other)
|
|
228
230
|
end,
|
|
229
231
|
|
|
230
|
-
__sub = function(a, b)
|
|
232
|
+
__sub = function(a: any, b: any): BigInt
|
|
231
233
|
if type(a) == "number" then
|
|
232
234
|
assert(a < 16777216, "number operand too big")
|
|
233
235
|
a = montgomeryModQ({ a, 0, 0, 0, 0, 0, 0 })
|
|
@@ -241,15 +243,15 @@ modQMT = {
|
|
|
241
243
|
return subModQ(a, b)
|
|
242
244
|
end,
|
|
243
245
|
|
|
244
|
-
__unm = function(self)
|
|
246
|
+
__unm = function(self: BigInt): BigInt
|
|
245
247
|
return subModQ(q, self)
|
|
246
248
|
end,
|
|
247
249
|
|
|
248
|
-
__eq = function(self, other)
|
|
250
|
+
__eq = function(self: BigInt, other: BigInt): boolean
|
|
249
251
|
return isEqual(self, other)
|
|
250
252
|
end,
|
|
251
253
|
|
|
252
|
-
__mul = function(self, other)
|
|
254
|
+
__mul = function(self: any, other: any): BigInt
|
|
253
255
|
if type(self) == "number" then
|
|
254
256
|
return other * self
|
|
255
257
|
end
|
|
@@ -268,7 +270,7 @@ modQMT = {
|
|
|
268
270
|
return multModQ(self, other)
|
|
269
271
|
end,
|
|
270
272
|
|
|
271
|
-
__div = function(a, b)
|
|
273
|
+
__div = function(a: any, b: any): BigInt
|
|
272
274
|
if type(a) == "number" then
|
|
273
275
|
assert(a < 16777216, "number operand too big")
|
|
274
276
|
a = montgomeryModQ({ a, 0, 0, 0, 0, 0, 0 })
|
|
@@ -279,12 +281,12 @@ modQMT = {
|
|
|
279
281
|
b = montgomeryModQ({ b, 0, 0, 0, 0, 0, 0 })
|
|
280
282
|
end
|
|
281
283
|
|
|
282
|
-
local bInv = expModQ(b, qMinusTwoBinary)
|
|
284
|
+
local bInv: BigInt = expModQ(b, qMinusTwoBinary)
|
|
283
285
|
|
|
284
286
|
return multModQ(a, bInv)
|
|
285
287
|
end,
|
|
286
288
|
|
|
287
|
-
__pow = function(self, other)
|
|
289
|
+
__pow = function(self: BigInt, other: number): BigInt
|
|
288
290
|
return intExpModQ(self, other)
|
|
289
291
|
end,
|
|
290
292
|
}
|
|
@@ -1,15 +1,16 @@
|
|
|
1
|
-
--!
|
|
1
|
+
--!strict
|
|
2
2
|
--!native
|
|
3
3
|
|
|
4
4
|
-- random.lua - Random Byte Generator
|
|
5
5
|
local sha256 = require(script.Parent.sha256)
|
|
6
6
|
|
|
7
|
-
local entropy = ""
|
|
8
|
-
local accumulator
|
|
7
|
+
local entropy: string = ""
|
|
8
|
+
local accumulator: { string } = {}
|
|
9
|
+
local accumulator_len: number = 0
|
|
9
10
|
|
|
10
11
|
local random = {}
|
|
11
12
|
|
|
12
|
-
local function feed(data)
|
|
13
|
+
local function feed(data: any)
|
|
13
14
|
accumulator_len += 1
|
|
14
15
|
accumulator[accumulator_len] = tostring(data or "")
|
|
15
16
|
end
|
|
@@ -21,7 +22,7 @@ local function digest()
|
|
|
21
22
|
accumulator_len = 0
|
|
22
23
|
end
|
|
23
24
|
|
|
24
|
-
local entropyInitialized = false
|
|
25
|
+
local entropyInitialized: boolean = false
|
|
25
26
|
|
|
26
27
|
-- Defer this initialization until requested
|
|
27
28
|
local function ensureEntropyInit()
|
|
@@ -55,7 +56,7 @@ local function ensureEntropyInit()
|
|
|
55
56
|
-- TODO: Suppress this warning
|
|
56
57
|
warn(
|
|
57
58
|
string.format(
|
|
58
|
-
"[EllipticCurveCryptography.random] - Generating entropy took %
|
|
59
|
+
"[EllipticCurveCryptography.random] - Generating entropy took %g ms",
|
|
59
60
|
1000 * (os.clock() - startTime)
|
|
60
61
|
)
|
|
61
62
|
)
|
|
@@ -72,7 +73,7 @@ function random.save()
|
|
|
72
73
|
entropy = tostring(sha256.digest(entropy))
|
|
73
74
|
end
|
|
74
75
|
|
|
75
|
-
function random.seed(data)
|
|
76
|
+
function random.seed(data: any)
|
|
76
77
|
ensureEntropyInit()
|
|
77
78
|
|
|
78
79
|
feed("seed")
|
|
@@ -83,7 +84,7 @@ function random.seed(data)
|
|
|
83
84
|
random.save()
|
|
84
85
|
end
|
|
85
86
|
|
|
86
|
-
function random.random()
|
|
87
|
+
function random.random(): any
|
|
87
88
|
ensureEntropyInit()
|
|
88
89
|
|
|
89
90
|
feed("random")
|