@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.
@@ -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;