@novaqore/ai 0.1.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/LICENSE +21 -0
- package/README.md +306 -0
- package/index.js +186 -0
- package/lib/kyber1024.js +1114 -0
- package/lib/sha3.js +1 -0
- package/lib/sponge/index.js +1 -0
- package/lib/sponge/permute/chi/index.js +1 -0
- package/lib/sponge/permute/copy/index.js +1 -0
- package/lib/sponge/permute/index.js +1 -0
- package/lib/sponge/permute/iota/index.js +1 -0
- package/lib/sponge/permute/iota/round-constants/index.js +1 -0
- package/lib/sponge/permute/rho-pi/index.js +1 -0
- package/lib/sponge/permute/rho-pi/pi-shuffles/index.js +1 -0
- package/lib/sponge/permute/rho-pi/rho-offsets/index.js +1 -0
- package/lib/sponge/permute/theta/index.js +1 -0
- package/package.json +73 -0
package/lib/kyber1024.js
ADDED
|
@@ -0,0 +1,1114 @@
|
|
|
1
|
+
/*****************************************************************************************************************************/
|
|
2
|
+
// imports
|
|
3
|
+
const { SHA3, SHAKE } = require('./sha3');
|
|
4
|
+
const webcrypto = require('crypto').webcrypto;
|
|
5
|
+
/*****************************************************************************************************************************/
|
|
6
|
+
const nttZetas = [
|
|
7
|
+
2285, 2571, 2970, 1812, 1493, 1422, 287, 202, 3158, 622, 1577, 182, 962,
|
|
8
|
+
2127, 1855, 1468, 573, 2004, 264, 383, 2500, 1458, 1727, 3199, 2648, 1017,
|
|
9
|
+
732, 608, 1787, 411, 3124, 1758, 1223, 652, 2777, 1015, 2036, 1491, 3047,
|
|
10
|
+
1785, 516, 3321, 3009, 2663, 1711, 2167, 126, 1469, 2476, 3239, 3058, 830,
|
|
11
|
+
107, 1908, 3082, 2378, 2931, 961, 1821, 2604, 448, 2264, 677, 2054, 2226,
|
|
12
|
+
430, 555, 843, 2078, 871, 1550, 105, 422, 587, 177, 3094, 3038, 2869, 1574,
|
|
13
|
+
1653, 3083, 778, 1159, 3182, 2552, 1483, 2727, 1119, 1739, 644, 2457, 349,
|
|
14
|
+
418, 329, 3173, 3254, 817, 1097, 603, 610, 1322, 2044, 1864, 384, 2114, 3193,
|
|
15
|
+
1218, 1994, 2455, 220, 2142, 1670, 2144, 1799, 2051, 794, 1819, 2475, 2459,
|
|
16
|
+
478, 3221, 3021, 996, 991, 958, 1869, 1522, 1628];
|
|
17
|
+
|
|
18
|
+
const nttZetasInv = [
|
|
19
|
+
1701, 1807, 1460, 2371, 2338, 2333, 308, 108, 2851, 870, 854, 1510, 2535,
|
|
20
|
+
1278, 1530, 1185, 1659, 1187, 3109, 874, 1335, 2111, 136, 1215, 2945, 1465,
|
|
21
|
+
1285, 2007, 2719, 2726, 2232, 2512, 75, 156, 3000, 2911, 2980, 872, 2685,
|
|
22
|
+
1590, 2210, 602, 1846, 777, 147, 2170, 2551, 246, 1676, 1755, 460, 291, 235,
|
|
23
|
+
3152, 2742, 2907, 3224, 1779, 2458, 1251, 2486, 2774, 2899, 1103, 1275, 2652,
|
|
24
|
+
1065, 2881, 725, 1508, 2368, 398, 951, 247, 1421, 3222, 2499, 271, 90, 853,
|
|
25
|
+
1860, 3203, 1162, 1618, 666, 320, 8, 2813, 1544, 282, 1838, 1293, 2314, 552,
|
|
26
|
+
2677, 2106, 1571, 205, 2918, 1542, 2721, 2597, 2312, 681, 130, 1602, 1871,
|
|
27
|
+
829, 2946, 3065, 1325, 2756, 1861, 1474, 1202, 2367, 3147, 1752, 2707, 171,
|
|
28
|
+
3127, 3042, 1907, 1836, 1517, 359, 758, 1441];
|
|
29
|
+
|
|
30
|
+
const paramsK = 4;
|
|
31
|
+
const paramsN = 256;
|
|
32
|
+
const paramsQ = 3329;
|
|
33
|
+
const paramsQinv = 62209;
|
|
34
|
+
const paramsETA = 2;
|
|
35
|
+
/*****************************************************************************************************************************/
|
|
36
|
+
// CRYSTALS-KYBER JAVASCRIPT
|
|
37
|
+
|
|
38
|
+
// 1. KeyGen
|
|
39
|
+
var KeyGen1024 = function() {
|
|
40
|
+
// IND-CPA keypair
|
|
41
|
+
let indcpakeys = indcpaKeyGen();
|
|
42
|
+
|
|
43
|
+
let pk = indcpakeys[0];
|
|
44
|
+
let sk = indcpakeys[1];
|
|
45
|
+
|
|
46
|
+
// FO transform to make IND-CCA2
|
|
47
|
+
|
|
48
|
+
// get hash of pk
|
|
49
|
+
const buffer1 = Buffer.from(pk);
|
|
50
|
+
const hash1 = new SHA3(256);
|
|
51
|
+
hash1.update(buffer1);
|
|
52
|
+
let pkh = hash1.digest();
|
|
53
|
+
|
|
54
|
+
// read 32 random values (0-255) into a 32 byte array
|
|
55
|
+
let rnd = new Uint8Array(32);
|
|
56
|
+
webcrypto.getRandomValues(rnd); // web api cryptographically strong random values
|
|
57
|
+
|
|
58
|
+
// concatenate to form IND-CCA2 private key: sk + pk + h(pk) + rnd
|
|
59
|
+
for (let i = 0; i < pk.length; i++) {
|
|
60
|
+
sk.push(pk[i]);
|
|
61
|
+
}
|
|
62
|
+
for (let i = 0; i < pkh.length; i++) {
|
|
63
|
+
sk.push(pkh[i]);
|
|
64
|
+
}
|
|
65
|
+
for (let i = 0; i < rnd.length; i++) {
|
|
66
|
+
sk.push(rnd[i]);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
let keys = new Array(2);
|
|
70
|
+
keys[0] = pk;
|
|
71
|
+
keys[1] = sk;
|
|
72
|
+
return keys;
|
|
73
|
+
}
|
|
74
|
+
/*****************************************************************************************************************************/
|
|
75
|
+
// 2. Encrypt
|
|
76
|
+
var Encrypt1024 = function(pk) {
|
|
77
|
+
|
|
78
|
+
// random 32 bytes
|
|
79
|
+
let m = new Uint8Array(32);
|
|
80
|
+
webcrypto.getRandomValues(m); // web api cryptographically strong random values
|
|
81
|
+
|
|
82
|
+
// hash m with SHA3-256
|
|
83
|
+
const buffer1 = Buffer.from(m);
|
|
84
|
+
const hash1 = new SHA3(256);
|
|
85
|
+
hash1.update(buffer1);
|
|
86
|
+
let mh = hash1.digest();
|
|
87
|
+
|
|
88
|
+
// hash pk with SHA3-256
|
|
89
|
+
const buffer2 = Buffer.from(pk);
|
|
90
|
+
const hash2 = new SHA3(256);
|
|
91
|
+
hash2.update(buffer2);
|
|
92
|
+
let pkh = hash2.digest();
|
|
93
|
+
|
|
94
|
+
// hash mh and pkh with SHA3-512
|
|
95
|
+
const buffer3 = Buffer.from(mh);
|
|
96
|
+
const buffer4 = Buffer.from(pkh);
|
|
97
|
+
const hash3 = new SHA3(512);
|
|
98
|
+
hash3.update(buffer3).update(buffer4);
|
|
99
|
+
let kr = new Uint8Array(hash3.digest());
|
|
100
|
+
let kr1 = kr.slice(0, 32);
|
|
101
|
+
let kr2 = kr.slice(32, 64);
|
|
102
|
+
|
|
103
|
+
// generate ciphertext c
|
|
104
|
+
let c = indcpaEncrypt(pk, mh, kr2);
|
|
105
|
+
|
|
106
|
+
// hash ciphertext with SHA3-256
|
|
107
|
+
const buffer5 = Buffer.from(c);
|
|
108
|
+
const hash4 = new SHA3(256);
|
|
109
|
+
hash4.update(buffer5);
|
|
110
|
+
let ch = hash4.digest();
|
|
111
|
+
|
|
112
|
+
// hash kr1 and ch with SHAKE-256
|
|
113
|
+
const buffer6 = Buffer.from(kr1);
|
|
114
|
+
const buffer7 = Buffer.from(ch);
|
|
115
|
+
const hash5 = new SHAKE(256);
|
|
116
|
+
hash5.update(buffer6).update(buffer7);
|
|
117
|
+
let ss = hash5.digest();
|
|
118
|
+
|
|
119
|
+
// output (c, ss)
|
|
120
|
+
let result = new Array(2);
|
|
121
|
+
result[0] = c;
|
|
122
|
+
result[1] = ss;
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
}
|
|
126
|
+
/*****************************************************************************************************************************/
|
|
127
|
+
// 3. Decrypt
|
|
128
|
+
var Decrypt1024 = function(c, privateKey) {
|
|
129
|
+
|
|
130
|
+
// extract sk, pk, pkh and z
|
|
131
|
+
let sk = privateKey.slice(0, 1536); // indcpa secret key
|
|
132
|
+
let pk = privateKey.slice(1536, 3104); // indcpa public key
|
|
133
|
+
let pkh = privateKey.slice(3104, 3136); // sha3-256 hash
|
|
134
|
+
let z = privateKey.slice(3136, 3168);
|
|
135
|
+
|
|
136
|
+
// IND-CPA decrypt
|
|
137
|
+
let m = indcpaDecrypt(c, sk);
|
|
138
|
+
|
|
139
|
+
// hash m and pkh with SHA3-512
|
|
140
|
+
const buffer1 = Buffer.from(m);
|
|
141
|
+
const buffer2 = Buffer.from(pkh);
|
|
142
|
+
const hash1 = new SHA3(512);
|
|
143
|
+
hash1.update(buffer1).update(buffer2);
|
|
144
|
+
let kr = new Uint8Array(hash1.digest());
|
|
145
|
+
let kr1 = kr.slice(0, 32);
|
|
146
|
+
let kr2 = kr.slice(32, 64);
|
|
147
|
+
|
|
148
|
+
// IND-CPA encrypt
|
|
149
|
+
let cmp = indcpaEncrypt(pk, m, kr2);
|
|
150
|
+
|
|
151
|
+
// compare c and cmp
|
|
152
|
+
let fail = ArrayCompare(c, cmp) - 1;
|
|
153
|
+
|
|
154
|
+
// hash c with SHA3-256
|
|
155
|
+
const buffer3 = Buffer.from(c);
|
|
156
|
+
const hash2 = new SHA3(256);
|
|
157
|
+
hash2.update(buffer3);
|
|
158
|
+
let ch = hash2.digest();
|
|
159
|
+
|
|
160
|
+
let ss = [];
|
|
161
|
+
if (!fail){
|
|
162
|
+
// hash kr1 and ch with SHAKE-256
|
|
163
|
+
const buffer4 = Buffer.from(kr1);
|
|
164
|
+
const buffer5 = Buffer.from(ch);
|
|
165
|
+
const hash3 = new SHAKE(256);
|
|
166
|
+
hash3.update(buffer4).update(buffer5);
|
|
167
|
+
ss = hash3.digest();
|
|
168
|
+
}
|
|
169
|
+
else{
|
|
170
|
+
// hash z and ch with SHAKE-256
|
|
171
|
+
const buffer6 = Buffer.from(z);
|
|
172
|
+
const buffer7 = Buffer.from(ch);
|
|
173
|
+
const hash4 = new SHAKE(256);
|
|
174
|
+
hash4.update(buffer6).update(buffer7);
|
|
175
|
+
ss = hash4.digest();
|
|
176
|
+
}
|
|
177
|
+
return ss;
|
|
178
|
+
}
|
|
179
|
+
/*****************************************************************************************************************************/
|
|
180
|
+
// indcpaKeyGen generates public and private keys for the CPA-secure
|
|
181
|
+
// public-key encryption scheme underlying Kyber.
|
|
182
|
+
function indcpaKeyGen() {
|
|
183
|
+
|
|
184
|
+
// random bytes for seed
|
|
185
|
+
let rnd = new Uint8Array(32);
|
|
186
|
+
webcrypto.getRandomValues(rnd); // web api cryptographically strong random values
|
|
187
|
+
|
|
188
|
+
// hash rnd with SHA3-512
|
|
189
|
+
const buffer1 = Buffer.from(rnd);
|
|
190
|
+
const hash1 = new SHA3(512);
|
|
191
|
+
hash1.update(buffer1);
|
|
192
|
+
let seed = new Uint8Array(hash1.digest());
|
|
193
|
+
let publicSeed = seed.slice(0, 32);
|
|
194
|
+
let noiseSeed = seed.slice(32, 64);
|
|
195
|
+
|
|
196
|
+
// generate public matrix A (already in NTT form)
|
|
197
|
+
let a = generateMatrixA(publicSeed, false, paramsK);
|
|
198
|
+
|
|
199
|
+
// sample secret s
|
|
200
|
+
let s = new Array(paramsK);
|
|
201
|
+
let nonce = 0;
|
|
202
|
+
for (let i = 0; i < paramsK; i++) {
|
|
203
|
+
s[i] = sample(noiseSeed, nonce);
|
|
204
|
+
nonce = nonce + 1;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// sample noise e
|
|
208
|
+
let e = new Array(paramsK);
|
|
209
|
+
for (let i = 0; i < paramsK; i++) {
|
|
210
|
+
e[i] = sample(noiseSeed, nonce);
|
|
211
|
+
nonce = nonce + 1;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
// perform number theoretic transform on secret s
|
|
215
|
+
for (let i = 0; i < paramsK; i++) {
|
|
216
|
+
s[i] = ntt(s[i]);
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// perform number theoretic transform on error/noise e
|
|
220
|
+
for (let i = 0; i < paramsK; i++) {
|
|
221
|
+
e[i] = ntt(e[i]);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// barrett reduction
|
|
225
|
+
for (let i = 0; i < paramsK; i++) {
|
|
226
|
+
s[i] = reduce(s[i]);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// KEY COMPUTATION
|
|
230
|
+
// A.s + e = pk
|
|
231
|
+
|
|
232
|
+
// calculate A.s
|
|
233
|
+
let pk = new Array(paramsK);
|
|
234
|
+
for (let i = 0; i < paramsK; i++) {
|
|
235
|
+
// montgomery reduction
|
|
236
|
+
pk[i] = polyToMont(multiply(a[i], s));
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// calculate addition of e
|
|
240
|
+
for (let i = 0; i < paramsK; i++) {
|
|
241
|
+
pk[i] = add(pk[i], e[i]);
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
// barrett reduction
|
|
245
|
+
for (let i = 0; i < paramsK; i++) {
|
|
246
|
+
pk[i] = reduce(pk[i]);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
// ENCODE KEYS
|
|
250
|
+
let keys = new Array(2);
|
|
251
|
+
|
|
252
|
+
// PUBLIC KEY
|
|
253
|
+
// turn polynomials into byte arrays
|
|
254
|
+
keys[0] = [];
|
|
255
|
+
let bytes = [];
|
|
256
|
+
for (let i = 0; i < paramsK; i++) {
|
|
257
|
+
bytes = polyToBytes(pk[i]);
|
|
258
|
+
for (let j = 0; j < bytes.length; j++) {
|
|
259
|
+
keys[0].push(bytes[j]);
|
|
260
|
+
}
|
|
261
|
+
}
|
|
262
|
+
// append public seed
|
|
263
|
+
for (let i = 0; i < publicSeed.length; i++) {
|
|
264
|
+
keys[0].push(publicSeed[i]);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
// PRIVATE KEY
|
|
268
|
+
// turn polynomials into byte arrays
|
|
269
|
+
keys[1] = [];
|
|
270
|
+
bytes = [];
|
|
271
|
+
for (let i = 0; i < paramsK; i++) {
|
|
272
|
+
bytes = polyToBytes(s[i]);
|
|
273
|
+
for (let j = 0; j < bytes.length; j++) {
|
|
274
|
+
keys[1].push(bytes[j]);
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
return keys;
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
|
|
283
|
+
// indcpaEncrypt is the encryption function of the CPA-secure
|
|
284
|
+
// public-key encryption scheme underlying Kyber.
|
|
285
|
+
function indcpaEncrypt(pk1, msg, coins) {
|
|
286
|
+
|
|
287
|
+
// DECODE PUBLIC KEY
|
|
288
|
+
let pk = new Array(paramsK);
|
|
289
|
+
let start;
|
|
290
|
+
let end;
|
|
291
|
+
for (let i = 0; i < paramsK; i++) {
|
|
292
|
+
start = (i * 384);
|
|
293
|
+
end = (i + 1) * 384;
|
|
294
|
+
pk[i] = polyFromBytes(pk1.slice(start, end));
|
|
295
|
+
}
|
|
296
|
+
let seed = pk1.slice(1536, 1568);
|
|
297
|
+
|
|
298
|
+
// generate transpose of public matrix A
|
|
299
|
+
let at = generateMatrixA(seed, true);
|
|
300
|
+
|
|
301
|
+
// sample random vector r
|
|
302
|
+
let r = new Array(paramsK);
|
|
303
|
+
let nonce = 0;
|
|
304
|
+
for (let i = 0; i < paramsK; i++) {
|
|
305
|
+
r[i] = sample(coins, nonce);
|
|
306
|
+
nonce = nonce + 1;
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// sample error vector e1
|
|
310
|
+
let e1 = new Array(paramsK);
|
|
311
|
+
for (let i = 0; i < paramsK; i++) {
|
|
312
|
+
e1[i] = sample(coins, nonce);
|
|
313
|
+
nonce = nonce + 1;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// sample e2
|
|
317
|
+
let e2 = sample(coins, nonce);
|
|
318
|
+
|
|
319
|
+
// perform number theoretic transform on random vector r
|
|
320
|
+
for (let i = 0; i < paramsK; i++) {
|
|
321
|
+
r[i] = ntt(r[i]);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
// barrett reduction
|
|
325
|
+
for (let i = 0; i < paramsK; i++) {
|
|
326
|
+
r[i] = reduce(r[i]);
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ENCRYPT COMPUTATION
|
|
330
|
+
// A.r + e1 = u
|
|
331
|
+
// pk.r + e2 + m = v
|
|
332
|
+
|
|
333
|
+
// calculate A.r
|
|
334
|
+
let u = new Array(paramsK);
|
|
335
|
+
for (i = 0; i < paramsK; i++) {
|
|
336
|
+
u[i] = multiply(at[i], r);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
// perform inverse number theoretic transform on A.r
|
|
340
|
+
for (let i = 0; i < paramsK; i++) {
|
|
341
|
+
u[i] = nttInverse(u[i]);
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// calculate addition of e1
|
|
345
|
+
for (let i = 0; i < paramsK; i++) {
|
|
346
|
+
u[i] = add(u[i], e1[i]);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
// decode message m
|
|
350
|
+
let m = polyFromMsg(msg);
|
|
351
|
+
|
|
352
|
+
// calculate pk.r
|
|
353
|
+
let v = multiply(pk, r);
|
|
354
|
+
|
|
355
|
+
// perform inverse number theoretic transform on pk.r
|
|
356
|
+
v = nttInverse(v);
|
|
357
|
+
|
|
358
|
+
// calculate addition of e2
|
|
359
|
+
v = add(v, e2);
|
|
360
|
+
|
|
361
|
+
// calculate addition of m
|
|
362
|
+
v = add(v, m);
|
|
363
|
+
|
|
364
|
+
// barrett reduction
|
|
365
|
+
for (let i = 0; i < paramsK; i++) {
|
|
366
|
+
u[i] = reduce(u[i]);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
// barrett reduction
|
|
370
|
+
v = reduce(v);
|
|
371
|
+
|
|
372
|
+
// compress
|
|
373
|
+
let c1 = compress1(u);
|
|
374
|
+
let c2 = compress2(v);
|
|
375
|
+
|
|
376
|
+
// return c1 || c2
|
|
377
|
+
return c1.concat(c2);
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
// indcpaDecrypt is the decryption function of the CPA-secure
|
|
381
|
+
// public-key encryption scheme underlying Kyber.
|
|
382
|
+
function indcpaDecrypt(c, privateKey) {
|
|
383
|
+
|
|
384
|
+
// extract ciphertext
|
|
385
|
+
let u = decompress1(c.slice(0, 1408));
|
|
386
|
+
let v = decompress2(c.slice(1408, 1568));
|
|
387
|
+
|
|
388
|
+
let privateKeyPolyvec = polyvecFromBytes(privateKey);
|
|
389
|
+
|
|
390
|
+
for (let i = 0; i < paramsK; i++) {
|
|
391
|
+
u[i] = ntt(u[i]);
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
let mp = multiply(privateKeyPolyvec, u);
|
|
395
|
+
|
|
396
|
+
mp = nttInverse(mp);
|
|
397
|
+
|
|
398
|
+
mp = subtract(v, mp);
|
|
399
|
+
|
|
400
|
+
mp = reduce(mp);
|
|
401
|
+
|
|
402
|
+
return polyToMsg(mp);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
// polyvecFromBytes deserializes a vector of polynomials.
|
|
406
|
+
function polyvecFromBytes(a) {
|
|
407
|
+
let r = new Array(paramsK);
|
|
408
|
+
for (let i = 0; i < paramsK; i++) {
|
|
409
|
+
r[i] = new Array(384);
|
|
410
|
+
}
|
|
411
|
+
let start;
|
|
412
|
+
let end;
|
|
413
|
+
for (let i = 0; i < paramsK; i++) {
|
|
414
|
+
start = (i * 384);
|
|
415
|
+
end = (i + 1) * 384;
|
|
416
|
+
r[i] = polyFromBytes(a.slice(start, end));
|
|
417
|
+
}
|
|
418
|
+
return r;
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// polyToBytes serializes a polynomial into an array of bytes.
|
|
422
|
+
function polyToBytes(a) {
|
|
423
|
+
let t0, t1;
|
|
424
|
+
let r = new Array(384);
|
|
425
|
+
let a2 = subtract_q(a); // Returns: a - q if a >= q, else a (each coefficient of the polynomial)
|
|
426
|
+
// for 0-127
|
|
427
|
+
for (let i = 0; i < paramsN / 2; i++) {
|
|
428
|
+
// get two coefficient entries in the polynomial
|
|
429
|
+
t0 = uint16(a2[2 * i]);
|
|
430
|
+
t1 = uint16(a2[2 * i + 1]);
|
|
431
|
+
|
|
432
|
+
// convert the 2 coefficient into 3 bytes
|
|
433
|
+
r[3 * i + 0] = byte(t0 >> 0); // byte() does mod 256 of the input (output value 0-255)
|
|
434
|
+
r[3 * i + 1] = byte(t0 >> 8) | byte(t1 << 4);
|
|
435
|
+
r[3 * i + 2] = byte(t1 >> 4);
|
|
436
|
+
}
|
|
437
|
+
return r;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
// polyFromBytes de-serialises an array of bytes into a polynomial,
|
|
441
|
+
// and represents the inverse of polyToBytes.
|
|
442
|
+
function polyFromBytes(a) {
|
|
443
|
+
let r = new Array(384).fill(0);
|
|
444
|
+
for (let i = 0; i < paramsN / 2; i++) {
|
|
445
|
+
r[2 * i] = int16(((uint16(a[3 * i + 0]) >> 0) | (uint16(a[3 * i + 1]) << 8)) & 0xFFF);
|
|
446
|
+
r[2 * i + 1] = int16(((uint16(a[3 * i + 1]) >> 4) | (uint16(a[3 * i + 2]) << 4)) & 0xFFF);
|
|
447
|
+
}
|
|
448
|
+
return r;
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
// polyToMsg converts a polynomial to a 32-byte message
|
|
452
|
+
// and represents the inverse of polyFromMsg.
|
|
453
|
+
function polyToMsg(a) {
|
|
454
|
+
let msg = new Array(32);
|
|
455
|
+
let t;
|
|
456
|
+
let a2 = subtract_q(a);
|
|
457
|
+
for (let i = 0; i < paramsN / 8; i++) {
|
|
458
|
+
msg[i] = 0;
|
|
459
|
+
for (let j = 0; j < 8; j++) {
|
|
460
|
+
t = (((uint16(a2[8 * i + j]) << 1) + uint16(paramsQ / 2)) / uint16(paramsQ)) & 1;
|
|
461
|
+
msg[i] |= byte(t << j);
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
return msg;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
// polyFromMsg converts a 32-byte message to a polynomial.
|
|
468
|
+
function polyFromMsg(msg) {
|
|
469
|
+
let r = new Array(384).fill(0); // each element is int16 (0-65535)
|
|
470
|
+
let mask; // int16
|
|
471
|
+
for (let i = 0; i < paramsN / 8; i++) {
|
|
472
|
+
for (let j = 0; j < 8; j++) {
|
|
473
|
+
mask = -1 * int16((msg[i] >> j) & 1);
|
|
474
|
+
r[8 * i + j] = mask & int16((paramsQ + 1) / 2);
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return r;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
|
|
481
|
+
|
|
482
|
+
// generateMatrixA deterministically generates a matrix `A` (or the transpose of `A`)
|
|
483
|
+
// from a seed. Entries of the matrix are polynomials that look uniformly random.
|
|
484
|
+
// Performs rejection sampling on the output of an extendable-output function (XOF).
|
|
485
|
+
function generateMatrixA(seed, transposed) {
|
|
486
|
+
let a = new Array(paramsK);
|
|
487
|
+
let output = new Array(3 * 168);
|
|
488
|
+
const xof = new SHAKE(128);
|
|
489
|
+
let ctr = 0;
|
|
490
|
+
for (let i = 0; i < paramsK; i++) {
|
|
491
|
+
|
|
492
|
+
a[i] = new Array(paramsK);
|
|
493
|
+
let transpose = new Array(2);
|
|
494
|
+
|
|
495
|
+
for (let j = 0; j < paramsK; j++) {
|
|
496
|
+
|
|
497
|
+
// set if transposed matrix or not
|
|
498
|
+
transpose[0] = j;
|
|
499
|
+
transpose[1] = i;
|
|
500
|
+
if (transposed) {
|
|
501
|
+
transpose[0] = i;
|
|
502
|
+
transpose[1] = j;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// obtain xof of (seed+i+j) or (seed+j+i) depending on above code
|
|
506
|
+
// output is 672 bytes in length
|
|
507
|
+
xof.reset();
|
|
508
|
+
const buffer1 = Buffer.from(seed);
|
|
509
|
+
const buffer2 = Buffer.from(transpose);
|
|
510
|
+
xof.update(buffer1).update(buffer2);
|
|
511
|
+
let output = new Uint8Array(xof.digest({ buffer: Buffer.alloc(672)}));
|
|
512
|
+
|
|
513
|
+
// run rejection sampling on the output from above
|
|
514
|
+
let outputlen = 3 * 168; // 504
|
|
515
|
+
let result = new Array(2);
|
|
516
|
+
result = indcpaRejUniform(output.slice(0,504), outputlen, paramsN);
|
|
517
|
+
a[i][j] = result[0]; // the result here is an NTT-representation
|
|
518
|
+
ctr = result[1]; // keeps track of index of output array from sampling function
|
|
519
|
+
|
|
520
|
+
while (ctr < paramsN) { // if the polynomial hasnt been filled yet with mod q entries
|
|
521
|
+
|
|
522
|
+
let outputn = output.slice(504, 672); // take last 168 bytes of byte array from xof
|
|
523
|
+
|
|
524
|
+
let result1 = new Array(2);
|
|
525
|
+
result1 = indcpaRejUniform(outputn, 168, paramsN-ctr); // run sampling function again
|
|
526
|
+
let missing = result1[0]; // here is additional mod q polynomial coefficients
|
|
527
|
+
let ctrn = result1[1]; // how many coefficients were accepted and are in the output
|
|
528
|
+
// starting at last position of output array from first sampling function until 256 is reached
|
|
529
|
+
for (let k = ctr; k < paramsN; k++) {
|
|
530
|
+
a[i][j][k] = missing[k-ctr]; // fill rest of array with the additional coefficients until full
|
|
531
|
+
}
|
|
532
|
+
ctr = ctr + ctrn; // update index
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return a;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
// indcpaRejUniform runs rejection sampling on uniform random bytes
|
|
541
|
+
// to generate uniform random integers modulo `Q`.
|
|
542
|
+
function indcpaRejUniform(buf, bufl, len) {
|
|
543
|
+
let r = new Array(384).fill(0);
|
|
544
|
+
let val0, val1; // d1, d2 in kyber documentation
|
|
545
|
+
let pos = 0; // i
|
|
546
|
+
let ctr = 0; // j
|
|
547
|
+
|
|
548
|
+
while (ctr < len && pos + 3 <= bufl) {
|
|
549
|
+
|
|
550
|
+
// compute d1 and d2
|
|
551
|
+
val0 = (uint16((buf[pos]) >> 0) | (uint16(buf[pos + 1]) << 8)) & 0xFFF;
|
|
552
|
+
val1 = (uint16((buf[pos + 1]) >> 4) | (uint16(buf[pos + 2]) << 4)) & 0xFFF;
|
|
553
|
+
|
|
554
|
+
// increment input buffer index by 3
|
|
555
|
+
pos = pos + 3;
|
|
556
|
+
|
|
557
|
+
// if d1 is less than 3329
|
|
558
|
+
if (val0 < paramsQ) {
|
|
559
|
+
// assign to d1
|
|
560
|
+
r[ctr] = val0;
|
|
561
|
+
// increment position of output array
|
|
562
|
+
ctr = ctr + 1;
|
|
563
|
+
}
|
|
564
|
+
if (ctr < len && val1 < paramsQ) {
|
|
565
|
+
r[ctr] = val1;
|
|
566
|
+
ctr = ctr + 1;
|
|
567
|
+
}
|
|
568
|
+
|
|
569
|
+
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
let result = new Array(2);
|
|
573
|
+
result[0] = r; // returns polynomial NTT representation
|
|
574
|
+
result[1] = ctr; // ideally should return 256
|
|
575
|
+
return result;
|
|
576
|
+
}
|
|
577
|
+
|
|
578
|
+
// sample samples a polynomial deterministically from a seed
|
|
579
|
+
// and nonce, with the output polynomial being close to a centered
|
|
580
|
+
// binomial distribution with parameter paramsETA = 2.
|
|
581
|
+
function sample(seed, nonce) {
|
|
582
|
+
let l = paramsETA * paramsN / 4;
|
|
583
|
+
let p = prf(l, seed, nonce);
|
|
584
|
+
return byteopsCbd(p);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
// prf provides a pseudo-random function (PRF) which returns
|
|
588
|
+
// a byte array of length `l`, using the provided key and nonce
|
|
589
|
+
// to instantiate the PRF's underlying hash function.
|
|
590
|
+
function prf(l, key, nonce) {
|
|
591
|
+
let nonce_arr = new Array(1);
|
|
592
|
+
nonce_arr[0] = nonce;
|
|
593
|
+
const hash = new SHAKE(256);
|
|
594
|
+
hash.reset();
|
|
595
|
+
const buffer1 = Buffer.from(key);
|
|
596
|
+
const buffer2 = Buffer.from(nonce_arr);
|
|
597
|
+
hash.update(buffer1).update(buffer2);
|
|
598
|
+
let buf = hash.digest({ buffer: Buffer.alloc(l)}); // 128 long byte array
|
|
599
|
+
return buf;
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
// byteopsCbd computes a polynomial with coefficients distributed
|
|
603
|
+
// according to a centered binomial distribution with parameter paramsETA,
|
|
604
|
+
// given an array of uniformly random bytes.
|
|
605
|
+
function byteopsCbd(buf) {
|
|
606
|
+
let t, d;
|
|
607
|
+
let a, b;
|
|
608
|
+
let r = new Array(384).fill(0);
|
|
609
|
+
for (let i = 0; i < paramsN / 8; i++) {
|
|
610
|
+
t = (byteopsLoad32(buf.slice(4 * i, buf.length)) >>> 0);
|
|
611
|
+
d = ((t & 0x55555555) >>> 0);
|
|
612
|
+
d = (d + ((((t >> 1) >>> 0) & 0x55555555) >>> 0) >>> 0);
|
|
613
|
+
for (let j = 0; j < 8; j++) {
|
|
614
|
+
a = int16((((d >> (4 * j + 0)) >>> 0) & 0x3) >>> 0);
|
|
615
|
+
b = int16((((d >> (4 * j + paramsETA)) >>> 0) & 0x3) >>> 0);
|
|
616
|
+
r[8 * i + j] = a - b;
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
return r;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
// byteopsLoad32 returns a 32-bit unsigned integer loaded from byte x.
|
|
623
|
+
function byteopsLoad32(x) {
|
|
624
|
+
let r;
|
|
625
|
+
r = uint32(x[0]);
|
|
626
|
+
r = (((r | (uint32(x[1]) << 8)) >>> 0) >>> 0);
|
|
627
|
+
r = (((r | (uint32(x[2]) << 16)) >>> 0) >>> 0);
|
|
628
|
+
r = (((r | (uint32(x[3]) << 24)) >>> 0) >>> 0);
|
|
629
|
+
return uint32(r);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
// ntt performs an inplace number-theoretic transform (NTT) in `Rq`.
|
|
633
|
+
// The input is in standard order, the output is in bit-reversed order.
|
|
634
|
+
function ntt(r) {
|
|
635
|
+
let j = 0;
|
|
636
|
+
let k = 1;
|
|
637
|
+
let zeta;
|
|
638
|
+
let t;
|
|
639
|
+
// 128, 64, 32, 16, 8, 4, 2
|
|
640
|
+
for (let l = 128; l >= 2; l >>= 1) {
|
|
641
|
+
// 0,
|
|
642
|
+
for (let start = 0; start < 256; start = j + l) {
|
|
643
|
+
zeta = nttZetas[k];
|
|
644
|
+
k = k + 1;
|
|
645
|
+
// for each element in the subsections (128, 64, 32, 16, 8, 4, 2) starting at an offset
|
|
646
|
+
for (j = start; j < start + l; j++) {
|
|
647
|
+
// compute the modular multiplication of the zeta and each element in the subsection
|
|
648
|
+
t = nttFqMul(zeta, r[j + l]); // t is mod q
|
|
649
|
+
// overwrite each element in the subsection as the opposite subsection element minus t
|
|
650
|
+
r[j + l] = r[j] - t;
|
|
651
|
+
// add t back again to the opposite subsection
|
|
652
|
+
r[j] = r[j] + t;
|
|
653
|
+
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
}
|
|
657
|
+
return r;
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
// nttFqMul performs multiplication followed by Montgomery reduction
|
|
661
|
+
// and returns a 16-bit integer congruent to `a*b*R^{-1} mod Q`.
|
|
662
|
+
function nttFqMul(a, b) {
|
|
663
|
+
return byteopsMontgomeryReduce(a * b);
|
|
664
|
+
}
|
|
665
|
+
|
|
666
|
+
// reduce applies Barrett reduction to all coefficients of a polynomial.
|
|
667
|
+
function reduce(r) {
|
|
668
|
+
for (let i = 0; i < paramsN; i++) {
|
|
669
|
+
r[i] = barrett(r[i]);
|
|
670
|
+
}
|
|
671
|
+
return r;
|
|
672
|
+
}
|
|
673
|
+
|
|
674
|
+
// barrett computes a Barrett reduction; given
|
|
675
|
+
// a integer `a`, returns a integer congruent to
|
|
676
|
+
// `a mod Q` in {0,...,Q}.
|
|
677
|
+
function barrett(a) {
|
|
678
|
+
let v = ( (1<<24) + paramsQ / 2) / paramsQ;
|
|
679
|
+
let t = v * a >> 24;
|
|
680
|
+
t = t * paramsQ;
|
|
681
|
+
return a - t;
|
|
682
|
+
}
|
|
683
|
+
|
|
684
|
+
// byteopsMontgomeryReduce computes a Montgomery reduction; given
|
|
685
|
+
// a 32-bit integer `a`, returns `a * R^-1 mod Q` where `R=2^16`.
|
|
686
|
+
function byteopsMontgomeryReduce(a) {
|
|
687
|
+
let u = int16(int32(a) * paramsQinv);
|
|
688
|
+
let t = u * paramsQ;
|
|
689
|
+
t = a - t;
|
|
690
|
+
t >>= 16;
|
|
691
|
+
return int16(t);
|
|
692
|
+
}
|
|
693
|
+
|
|
694
|
+
// polyToMont performs the in-place conversion of all coefficients
|
|
695
|
+
// of a polynomial from the normal domain to the Montgomery domain.
|
|
696
|
+
function polyToMont(r) {
|
|
697
|
+
// let f = int16(((uint64(1) << 32) >>> 0) % uint64(paramsQ));
|
|
698
|
+
let f = 1353; // if paramsQ changes then this needs to be updated
|
|
699
|
+
for (let i = 0; i < paramsN; i++) {
|
|
700
|
+
r[i] = byteopsMontgomeryReduce(int32(r[i]) * int32(f));
|
|
701
|
+
}
|
|
702
|
+
return r;
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
// pointwise-multiplies elements of polynomial-vectors
|
|
706
|
+
// `a` and `b`, accumulates the results into `r`, and then multiplies by `2^-16`.
|
|
707
|
+
function multiply(a, b) {
|
|
708
|
+
let r = polyBaseMulMontgomery(a[0], b[0]);
|
|
709
|
+
let t;
|
|
710
|
+
for (let i = 1; i < paramsK; i++) {
|
|
711
|
+
t = polyBaseMulMontgomery(a[i], b[i]);
|
|
712
|
+
r = add(r, t);
|
|
713
|
+
}
|
|
714
|
+
return reduce(r);
|
|
715
|
+
}
|
|
716
|
+
|
|
717
|
+
// polyBaseMulMontgomery performs the multiplication of two polynomials
|
|
718
|
+
// in the number-theoretic transform (NTT) domain.
|
|
719
|
+
function polyBaseMulMontgomery(a, b) {
|
|
720
|
+
let rx, ry;
|
|
721
|
+
for (let i = 0; i < paramsN / 4; i++) {
|
|
722
|
+
rx = nttBaseMul(
|
|
723
|
+
a[4 * i + 0], a[4 * i + 1],
|
|
724
|
+
b[4 * i + 0], b[4 * i + 1],
|
|
725
|
+
nttZetas[64 + i]
|
|
726
|
+
);
|
|
727
|
+
ry = nttBaseMul(
|
|
728
|
+
a[4 * i + 2], a[4 * i + 3],
|
|
729
|
+
b[4 * i + 2], b[4 * i + 3],
|
|
730
|
+
-nttZetas[64 + i]
|
|
731
|
+
);
|
|
732
|
+
a[4 * i + 0] = rx[0];
|
|
733
|
+
a[4 * i + 1] = rx[1];
|
|
734
|
+
a[4 * i + 2] = ry[0];
|
|
735
|
+
a[4 * i + 3] = ry[1];
|
|
736
|
+
}
|
|
737
|
+
return a;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// nttBaseMul performs the multiplication of polynomials
|
|
741
|
+
// in `Zq[X]/(X^2-zeta)`. Used for multiplication of elements
|
|
742
|
+
// in `Rq` in the number-theoretic transformation domain.
|
|
743
|
+
function nttBaseMul(a0, a1, b0, b1, zeta) {
|
|
744
|
+
let r = new Array(2);
|
|
745
|
+
r[0] = nttFqMul(a1, b1);
|
|
746
|
+
r[0] = nttFqMul(r[0], zeta);
|
|
747
|
+
r[0] = r[0] + nttFqMul(a0, b0);
|
|
748
|
+
r[1] = nttFqMul(a0, b1);
|
|
749
|
+
r[1] = r[1] + nttFqMul(a1, b0);
|
|
750
|
+
return r;
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
// adds two polynomials.
|
|
754
|
+
function add(a, b) {
|
|
755
|
+
let c = new Array(384);
|
|
756
|
+
for (let i = 0; i < paramsN; i++) {
|
|
757
|
+
c[i] = a[i] + b[i];
|
|
758
|
+
}
|
|
759
|
+
return c;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
// subtracts two polynomials.
|
|
763
|
+
function subtract(a, b) {
|
|
764
|
+
for (let i = 0; i < paramsN; i++) {
|
|
765
|
+
a[i] = a[i] - b[i];
|
|
766
|
+
}
|
|
767
|
+
return a;
|
|
768
|
+
}
|
|
769
|
+
|
|
770
|
+
// nttInverse performs an inplace inverse number-theoretic transform (NTT)
|
|
771
|
+
// in `Rq` and multiplication by Montgomery factor 2^16.
|
|
772
|
+
// The input is in bit-reversed order, the output is in standard order.
|
|
773
|
+
function nttInverse(r) {
|
|
774
|
+
let j = 0;
|
|
775
|
+
let k = 0;
|
|
776
|
+
let zeta;
|
|
777
|
+
let t;
|
|
778
|
+
for (let l = 2; l <= 128; l <<= 1) {
|
|
779
|
+
for (let start = 0; start < 256; start = j + l) {
|
|
780
|
+
zeta = nttZetasInv[k];
|
|
781
|
+
k = k + 1;
|
|
782
|
+
for (j = start; j < start + l; j++) {
|
|
783
|
+
t = r[j];
|
|
784
|
+
r[j] = barrett(t + r[j + l]);
|
|
785
|
+
r[j + l] = t - r[j + l];
|
|
786
|
+
r[j + l] = nttFqMul(zeta, r[j + l]);
|
|
787
|
+
}
|
|
788
|
+
}
|
|
789
|
+
}
|
|
790
|
+
for (j = 0; j < 256; j++) {
|
|
791
|
+
r[j] = nttFqMul(r[j], nttZetasInv[127]);
|
|
792
|
+
}
|
|
793
|
+
return r;
|
|
794
|
+
}
|
|
795
|
+
|
|
796
|
+
// compress1 lossily compresses and serializes a vector of polynomials.
|
|
797
|
+
function compress1(u) {
|
|
798
|
+
let rr = 0;
|
|
799
|
+
let r = new Array(1408); // 4 * 352
|
|
800
|
+
let t = new Array(8);
|
|
801
|
+
for (let i = 0; i < paramsK; i++) {
|
|
802
|
+
for (let j = 0; j < paramsN/8; j++) {
|
|
803
|
+
for (let k = 0; k < 8; k++) {
|
|
804
|
+
t[k] = uint16((((uint32(u[i][8*j+k]) << 11 >>> 0) + uint32(paramsQ/2)) / uint32(paramsQ)) & 0x7ff >>> 0);
|
|
805
|
+
}
|
|
806
|
+
r[rr+0] = byte((t[0] >> 0));
|
|
807
|
+
r[rr+1] = byte((t[0] >> 8) | (t[1] << 3));
|
|
808
|
+
r[rr+2] = byte((t[1] >> 5) | (t[2] << 6));
|
|
809
|
+
r[rr+3] = byte((t[2] >> 2));
|
|
810
|
+
r[rr+4] = byte((t[2] >> 10) | (t[3] << 1));
|
|
811
|
+
r[rr+5] = byte((t[3] >> 7) | (t[4] << 4));
|
|
812
|
+
r[rr+6] = byte((t[4] >> 4) | (t[5] << 7));
|
|
813
|
+
r[rr+7] = byte((t[5] >> 1));
|
|
814
|
+
r[rr+8] = byte((t[5] >> 9) | (t[6] << 2));
|
|
815
|
+
r[rr+9] = byte((t[6] >> 6) | (t[7] << 5));
|
|
816
|
+
r[rr+10] = byte((t[7] >> 3));
|
|
817
|
+
rr = rr + 11;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
return r;
|
|
821
|
+
}
|
|
822
|
+
|
|
823
|
+
// compress2 lossily compresses and subsequently serializes a polynomial.
|
|
824
|
+
function compress2(v) {
|
|
825
|
+
let rr = 0;
|
|
826
|
+
let r = new Array(160);
|
|
827
|
+
let t = new Array(8);
|
|
828
|
+
for (let i = 0; i < paramsN/8; i++) {
|
|
829
|
+
for (let j = 0; j < 8; j++) {
|
|
830
|
+
t[j] = byte(((uint32(v[8*i+j])<<5 >>> 0)+uint32(paramsQ/2))/uint32(paramsQ)) & 31;
|
|
831
|
+
}
|
|
832
|
+
r[rr+0] = byte((t[0] >> 0) | (t[1] << 5));
|
|
833
|
+
r[rr+1] = byte((t[1] >> 3) | (t[2] << 2) | (t[3] << 7));
|
|
834
|
+
r[rr+2] = byte((t[3] >> 1) | (t[4] << 4));
|
|
835
|
+
r[rr+3] = byte((t[4] >> 4) | (t[5] << 1) | (t[6] << 6));
|
|
836
|
+
r[rr+4] = byte((t[6] >> 2) | (t[7] << 3));
|
|
837
|
+
rr = rr + 5;
|
|
838
|
+
}
|
|
839
|
+
return r;
|
|
840
|
+
}
|
|
841
|
+
|
|
842
|
+
// decompress1 de-serializes and decompresses a vector of polynomials and
|
|
843
|
+
// represents the approximate inverse of compress1. Since compression is lossy,
|
|
844
|
+
// the results of decompression may not match the original vector of polynomials.
|
|
845
|
+
function decompress1(a) {
|
|
846
|
+
let r = new Array(paramsK);
|
|
847
|
+
for (let i = 0; i < paramsK; i++) {
|
|
848
|
+
r[i] = new Array(384);
|
|
849
|
+
}
|
|
850
|
+
let aa = 0;
|
|
851
|
+
let t = new Array(8);
|
|
852
|
+
for (let i = 0; i < paramsK; i++) {
|
|
853
|
+
for (let j = 0; j < paramsN/8; j++) {
|
|
854
|
+
t[0] = (uint16(a[aa+0]) >> 0) | (uint16(a[aa+1]) << 8);
|
|
855
|
+
t[1] = (uint16(a[aa+1]) >> 3) | (uint16(a[aa+2]) << 5);
|
|
856
|
+
t[2] = (uint16(a[aa+2]) >> 6) | (uint16(a[aa+3]) << 2) | (uint16(a[aa+4]) << 10);
|
|
857
|
+
t[3] = (uint16(a[aa+4]) >> 1) | (uint16(a[aa+5]) << 7);
|
|
858
|
+
t[4] = (uint16(a[aa+5]) >> 4) | (uint16(a[aa+6]) << 4);
|
|
859
|
+
t[5] = (uint16(a[aa+6]) >> 7) | (uint16(a[aa+7]) << 1) | (uint16(a[aa+8]) << 9);
|
|
860
|
+
t[6] = (uint16(a[aa+8]) >> 2) | (uint16(a[aa+9]) << 6);
|
|
861
|
+
t[7] = (uint16(a[aa+9]) >> 5) | (uint16(a[aa+10]) << 3);
|
|
862
|
+
aa = aa + 11;
|
|
863
|
+
for (let k = 0; k < 8; k++) {
|
|
864
|
+
r[i][8*j+k] = (uint32(t[k] & 0x7FF) * paramsQ + 1024) >> 11;
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return r;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
// subtract_q applies the conditional subtraction of q to each coefficient of a polynomial.
|
|
872
|
+
// if a is 3329 then convert to 0
|
|
873
|
+
// Returns: a - q if a >= q, else a
|
|
874
|
+
function subtract_q(r) {
|
|
875
|
+
for (let i = 0; i < paramsN; i++) {
|
|
876
|
+
r[i] = r[i] - paramsQ; // should result in a negative integer
|
|
877
|
+
// push left most signed bit to right most position
|
|
878
|
+
// javascript does bitwise operations in signed 32 bit
|
|
879
|
+
// add q back again if left most bit was 0 (positive number)
|
|
880
|
+
r[i] = r[i] + ((r[i] >> 31) & paramsQ);
|
|
881
|
+
}
|
|
882
|
+
return r;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
// decompress2 de-serializes and subsequently decompresses a polynomial,
|
|
886
|
+
// representing the approximate inverse of compress2.
|
|
887
|
+
// Note that compression is lossy, and thus decompression will not match the
|
|
888
|
+
// original input.
|
|
889
|
+
function decompress2(a) {
|
|
890
|
+
let r = new Array(384);
|
|
891
|
+
let t = new Array(8);
|
|
892
|
+
let aa = 0;
|
|
893
|
+
for (let i = 0; i < paramsN/8; i++) {
|
|
894
|
+
t[0] = (a[aa+0] >> 0);
|
|
895
|
+
t[1] = (a[aa+0] >> 5) | (a[aa+1] << 3);
|
|
896
|
+
t[2] = (a[aa+1] >> 2);
|
|
897
|
+
t[3] = (a[aa+1] >> 7) | (a[aa+2] << 1);
|
|
898
|
+
t[4] = (a[aa+2] >> 4) | (a[aa+3] << 4);
|
|
899
|
+
t[5] = (a[aa+3] >> 1);
|
|
900
|
+
t[6] = (a[aa+3] >> 6) | (a[aa+4] << 2);
|
|
901
|
+
t[7] = (a[aa+4] >> 3);
|
|
902
|
+
aa = aa + 5;
|
|
903
|
+
for (let j = 0; j < 8; j++) {
|
|
904
|
+
r[8*i+j] = int16(((uint32(t[j]&31 >>> 0) * uint32(paramsQ)) + 16) >> 5);
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return r;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
function byte(n) {
|
|
911
|
+
n = n % 256;
|
|
912
|
+
return n;
|
|
913
|
+
}
|
|
914
|
+
|
|
915
|
+
/*
|
|
916
|
+
// not needed, just here for reference
|
|
917
|
+
function int8(n){
|
|
918
|
+
let end = -128;
|
|
919
|
+
let start = 127;
|
|
920
|
+
|
|
921
|
+
if( n >= end && n <= start){
|
|
922
|
+
return n;
|
|
923
|
+
}
|
|
924
|
+
if( n < end){
|
|
925
|
+
n = n+129;
|
|
926
|
+
n = n%256;
|
|
927
|
+
n = start + n;
|
|
928
|
+
return n;
|
|
929
|
+
}
|
|
930
|
+
if( n > start){
|
|
931
|
+
n = n-128;
|
|
932
|
+
n = n%256;
|
|
933
|
+
n = end + n;
|
|
934
|
+
return n;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
|
|
938
|
+
function uint8(n){
|
|
939
|
+
n = n%256;
|
|
940
|
+
return n;
|
|
941
|
+
}
|
|
942
|
+
*/
|
|
943
|
+
|
|
944
|
+
function int16(n) {
|
|
945
|
+
let end = -32768;
|
|
946
|
+
let start = 32767;
|
|
947
|
+
|
|
948
|
+
if (n >= end && n <= start) {
|
|
949
|
+
return n;
|
|
950
|
+
}
|
|
951
|
+
if (n < end) {
|
|
952
|
+
n = n + 32769;
|
|
953
|
+
n = n % 65536;
|
|
954
|
+
n = start + n;
|
|
955
|
+
return n;
|
|
956
|
+
}
|
|
957
|
+
if (n > start) {
|
|
958
|
+
n = n - 32768;
|
|
959
|
+
n = n % 65536;
|
|
960
|
+
n = end + n;
|
|
961
|
+
return n;
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
function uint16(n) {
|
|
966
|
+
n = n % 65536;
|
|
967
|
+
return n;
|
|
968
|
+
}
|
|
969
|
+
|
|
970
|
+
|
|
971
|
+
function int32(n) {
|
|
972
|
+
let end = -2147483648;
|
|
973
|
+
let start = 2147483647;
|
|
974
|
+
|
|
975
|
+
if (n >= end && n <= start) {
|
|
976
|
+
return n;
|
|
977
|
+
}
|
|
978
|
+
if (n < end) {
|
|
979
|
+
n = n + 2147483649;
|
|
980
|
+
n = n % 4294967296;
|
|
981
|
+
n = start + n;
|
|
982
|
+
return n;
|
|
983
|
+
}
|
|
984
|
+
if (n > start) {
|
|
985
|
+
n = n - 2147483648;
|
|
986
|
+
n = n % 4294967296;
|
|
987
|
+
n = end + n;
|
|
988
|
+
return n;
|
|
989
|
+
}
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// any bit operations to be done in uint32 must have >>> 0
|
|
993
|
+
// javascript calculates bitwise in SIGNED 32 bit so you need to convert
|
|
994
|
+
function uint32(n) {
|
|
995
|
+
n = n % 4294967296;
|
|
996
|
+
return n;
|
|
997
|
+
}
|
|
998
|
+
|
|
999
|
+
// compares two arrays and returns 1 if they are the same or 0 if not
|
|
1000
|
+
function ArrayCompare(a, b) {
|
|
1001
|
+
// check array lengths
|
|
1002
|
+
if (a.length != b.length) {
|
|
1003
|
+
return 0;
|
|
1004
|
+
}
|
|
1005
|
+
// check contents
|
|
1006
|
+
for (let i = 0; i < a.length; i++) {
|
|
1007
|
+
if (a[i] != b[i]) {
|
|
1008
|
+
return 0;
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
return 1;
|
|
1012
|
+
}
|
|
1013
|
+
function hexToDec(hexString) {
|
|
1014
|
+
return parseInt(hexString, 16);
|
|
1015
|
+
}
|
|
1016
|
+
// test run function
|
|
1017
|
+
Test1024 = function(){
|
|
1018
|
+
// read values from PQCkemKAT_3168.rsp
|
|
1019
|
+
// sk, ct, ss
|
|
1020
|
+
|
|
1021
|
+
let fs = require('fs');
|
|
1022
|
+
let textByLine = fs.readFileSync('./node_modules/crystals-kyber/PQCkemKAT_3168.rsp').toString().split("\n");
|
|
1023
|
+
|
|
1024
|
+
// console.log(textByLine.length); // seems to be an array of strings (lines)
|
|
1025
|
+
let sk100 = [];
|
|
1026
|
+
let ct100 = [];
|
|
1027
|
+
let ss100 = [];
|
|
1028
|
+
let counter = 0;
|
|
1029
|
+
while (counter < textByLine.length){
|
|
1030
|
+
if (textByLine[counter][0] == 'c' && textByLine[counter][1] == 't'){
|
|
1031
|
+
let tmp = [];
|
|
1032
|
+
for (j = 0; j < 1568; j++) {
|
|
1033
|
+
tmp[j] = hexToDec(textByLine[counter][2 * j + 5] + textByLine[counter][2 * j + 1 + 5]);
|
|
1034
|
+
}
|
|
1035
|
+
ct100.push(tmp);
|
|
1036
|
+
counter = counter + 1;
|
|
1037
|
+
continue;
|
|
1038
|
+
}
|
|
1039
|
+
else if(textByLine[counter][0] == 's' && textByLine[counter][1] == 's'){
|
|
1040
|
+
let tmp = [];
|
|
1041
|
+
for (j = 0; j < 32; j++) {
|
|
1042
|
+
tmp[j] = hexToDec(textByLine[counter][2 * j + 5] + textByLine[counter][2 * j + 1 + 5]);
|
|
1043
|
+
}
|
|
1044
|
+
ss100.push(tmp);
|
|
1045
|
+
counter = counter + 1;
|
|
1046
|
+
continue;
|
|
1047
|
+
}
|
|
1048
|
+
else if(textByLine[counter][0] == 's' && textByLine[counter][1] == 'k'){
|
|
1049
|
+
let tmp = [];
|
|
1050
|
+
for (j = 0; j < 3168; j++) {
|
|
1051
|
+
tmp[j] = hexToDec(textByLine[counter][2 * j + 5] + textByLine[counter][2 * j + 1 + 5]);
|
|
1052
|
+
}
|
|
1053
|
+
sk100.push(tmp);
|
|
1054
|
+
counter = counter + 1;
|
|
1055
|
+
continue;
|
|
1056
|
+
}
|
|
1057
|
+
else{
|
|
1058
|
+
counter = counter + 1;
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
let failures = 0;
|
|
1063
|
+
|
|
1064
|
+
// for each case (100 total)
|
|
1065
|
+
// test if ss equals Decrypt1024(c,sk)
|
|
1066
|
+
for (let i=0; i<100; i++){
|
|
1067
|
+
let ss2 = Decrypt1024(ct100[i],sk100[i]);
|
|
1068
|
+
|
|
1069
|
+
// success if both symmetric keys are the same
|
|
1070
|
+
if (ArrayCompare(ss100[i], ss2)){
|
|
1071
|
+
console.log("Test run [", i, "] success");
|
|
1072
|
+
}
|
|
1073
|
+
else{
|
|
1074
|
+
console.log("Test run [", i, "] fail");
|
|
1075
|
+
failures += 1;
|
|
1076
|
+
}
|
|
1077
|
+
}
|
|
1078
|
+
|
|
1079
|
+
if(failures==0){
|
|
1080
|
+
console.log(" ");
|
|
1081
|
+
console.log("All test runs successful.")
|
|
1082
|
+
}
|
|
1083
|
+
else{
|
|
1084
|
+
console.log(" ");
|
|
1085
|
+
console.log(failures, " test cases have failed.")
|
|
1086
|
+
}
|
|
1087
|
+
|
|
1088
|
+
// To generate a public and private key pair (pk, sk)
|
|
1089
|
+
let pk_sk = KeyGen1024();
|
|
1090
|
+
let pk = pk_sk[0];
|
|
1091
|
+
let sk = pk_sk[1];
|
|
1092
|
+
|
|
1093
|
+
// To generate a random 256 bit symmetric key (ss) and its encapsulation (c)
|
|
1094
|
+
let c_ss = Encrypt1024(pk);
|
|
1095
|
+
let c = c_ss[0];
|
|
1096
|
+
let ss1 = c_ss[1];
|
|
1097
|
+
|
|
1098
|
+
// To decapsulate and obtain the same symmetric key
|
|
1099
|
+
let ss2 = Decrypt1024(c, sk);
|
|
1100
|
+
|
|
1101
|
+
console.log();
|
|
1102
|
+
console.log("ss1",ss1);
|
|
1103
|
+
console.log("ss2",ss2);
|
|
1104
|
+
|
|
1105
|
+
// returns 1 if both symmetric keys are the same
|
|
1106
|
+
console.log(ArrayCompare(ss1, ss2));
|
|
1107
|
+
return
|
|
1108
|
+
}
|
|
1109
|
+
|
|
1110
|
+
// Export functions to index.js (entry point)
|
|
1111
|
+
exports.KeyGen1024 = KeyGen1024;
|
|
1112
|
+
exports.Encrypt1024 = Encrypt1024;
|
|
1113
|
+
exports.Decrypt1024 = Decrypt1024;
|
|
1114
|
+
exports.Test1024 = Test1024;
|