kyber-mini 1.0.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.
Files changed (5) hide show
  1. package/README.md +29 -0
  2. package/build.ts +16 -0
  3. package/index.ts +327 -0
  4. package/package.json +27 -0
  5. package/utils.ts +202 -0
package/README.md ADDED
@@ -0,0 +1,29 @@
1
+ # kyber
2
+ A minimalistic, high-performance TypeScript/JavaScript implementation of the Kyber KEM and PKE algorithms (standard specified by [FIPS203](https://csrc.nist.gov/pubs/fips/203/final)).
3
+
4
+ ## Performance
5
+ Benchmarks indicate performance on par with noble/post-quantum in Node.js, and approximately 2x faster when running with Bun.
6
+
7
+ ## Usage
8
+ kyber KEM usage example using ML_KEM1024:
9
+ ```ts
10
+ import { kyber_kem1024 } from "kyber-mini";
11
+ const keys = kyber_kem1024.keyGen(); // generates a pair of private and public keys.
12
+ const encapsulatedKeys = kyber_kem1024.encapsulate(keys.publicKey); // creates a shared key and creates a ciphertext from which the recipient can recreate the shared key
13
+ const sharedKey = kyber_kem1024.decapsulate(encapsulatedKeys.ciphertext, keys.privateKey); // recreates the shared key from ciphercode
14
+ ```
15
+ kyber PKE usage example using ML_PKE1024:
16
+ ```ts
17
+ import { kyber_pke1024 } from "kyber-mini";
18
+ const keys = kyber_pke1024.keyGen(); // generates kyber_pke keys
19
+ const message = new Uint8Array(32);
20
+ // set a message
21
+ const ciphertext = kyber_pke1024.encrypt(message, keys.publicKey); // encrypts the message with a public key
22
+ const decryptedMessage = kyber_pke1024.decrypt(ciphertext, keys.privateKey); // decrypts the message with a private key
23
+ ```
24
+
25
+ ## Instalation
26
+ npm install kyber-mini
27
+
28
+ ## Audit
29
+ This library has not been audited by a third party.
package/build.ts ADDED
@@ -0,0 +1,16 @@
1
+ import { rename } from "node:fs/promises";
2
+ await Bun.build({
3
+ packages: "external",
4
+ entrypoints: ["index.ts"],
5
+ format: "cjs",
6
+ outdir: "dist",
7
+ minify: true,
8
+ });
9
+ await rename("dist/index.js", "dist/index.cjs");
10
+ await Bun.build({
11
+ packages: "external",
12
+ entrypoints: ["index.ts"],
13
+ format: "esm",
14
+ outdir: "dist",
15
+ minify: true,
16
+ });
package/index.ts ADDED
@@ -0,0 +1,327 @@
1
+ import { sha3_256, sha3_512, shake256 } from "@noble/hashes/sha3.js";
2
+ import {
3
+ addMod,
4
+ subMod,
5
+ NTT,
6
+ INTT,
7
+ mulNTT,
8
+ addPol,
9
+ byteDecodeTo,
10
+ byteEncode,
11
+ samplePolCBD,
12
+ pseudoRandom,
13
+ expandA,
14
+ compress,
15
+ decompress,
16
+ randomBytes,
17
+ round,
18
+ } from "./utils.ts";
19
+
20
+ const Q = 3329;
21
+ const N = 256;
22
+ const QHalf = (Q + 1) / 2;
23
+ type kyberParams = {
24
+ K: number;
25
+ ETA1: number;
26
+ ETA2: number;
27
+ DU: number;
28
+ DV: number;
29
+ };
30
+ const kyberParams = {
31
+ v512: {
32
+ K: 2,
33
+ ETA1: 3,
34
+ ETA2: 2,
35
+ DU: 10,
36
+ DV: 4,
37
+ },
38
+ v768: {
39
+ K: 3,
40
+ ETA1: 2,
41
+ ETA2: 2,
42
+ DU: 10,
43
+ DV: 4,
44
+ },
45
+ v1024: {
46
+ K: 4,
47
+ ETA1: 2,
48
+ ETA2: 2,
49
+ DU: 11,
50
+ DV: 5,
51
+ },
52
+ };
53
+
54
+ class kyberPKE {
55
+ private privateKeyLen;
56
+ private publicKeyLen;
57
+ private ciphertextLen;
58
+ private par;
59
+ constructor(par: kyberParams) {
60
+ this.privateKeyLen = (3 * par.K * N) >> 1;
61
+ this.publicKeyLen = ((3 * N * par.K) >> 1) + (1 << 5);
62
+ this.ciphertextLen = (par.DU * N * par.K + N * par.DV) >> 3;
63
+ this.par = par;
64
+ }
65
+ keyGen(seed: Uint8Array = randomBytes(32)): { privateKey: Uint8Array; publicKey: Uint8Array; } {
66
+ if (seed.length !== 32)
67
+ throw new Error(`Seed should be of length 32`);
68
+
69
+ const seedsHasher = sha3_512.create();
70
+ seedsHasher.update(seed);
71
+ seedsHasher.update(new Uint8Array([this.par.K]));
72
+ const seedsHash = seedsHasher.digest();
73
+ const seedB = seedsHash.subarray(32, 64);
74
+
75
+ const s = new Uint16Array(this.par.K * N);
76
+ let counter = 0;
77
+ for (let i = 0; i < this.par.K; i++) {
78
+ const sSub = s.subarray(i * N, (i + 1) * N)
79
+ sSub.set(samplePolCBD(pseudoRandom(seedB, counter++, this.par.ETA1), this.par.ETA1));
80
+ NTT(sSub);
81
+ }
82
+
83
+ const t = new Uint16Array(this.par.K * N);
84
+ const seedA = seedsHash.subarray(0, 32);
85
+ const aNTT = expandA(seedA, this.par.K);
86
+ for (let i = 0; i < this.par.K; i++) {
87
+ const ti = t.subarray(i * N, (i + 1) * N);
88
+ for (let j = 0; j < this.par.K; j++) {
89
+ const aNTTij = aNTT.subarray((i * this.par.K + j) * N, (i * this.par.K + j + 1) * N);
90
+ const sj = s.subarray(j * N, (j + 1) * N);
91
+ mulNTT(aNTTij, sj);
92
+ addPol(ti, aNTTij);
93
+ }
94
+ const ei = samplePolCBD(pseudoRandom(seedB, counter++, this.par.ETA1), this.par.ETA1);
95
+ NTT(ei);
96
+ addPol(ti, ei);
97
+ }
98
+ const privateKey = byteEncode(s, 12);
99
+
100
+ const publicKey = new Uint8Array(this.publicKeyLen);
101
+ publicKey.set(byteEncode(t, 12));
102
+ publicKey.set(seedA, 12 * this.par.K * N / 8);
103
+ return {
104
+ privateKey,
105
+ publicKey,
106
+ };
107
+ }
108
+ encrypt(plaintext: Uint8Array, publicKey: Uint8Array, seed: Uint8Array = randomBytes(32)): Uint8Array {
109
+ if (publicKey.length !== this.publicKeyLen)
110
+ throw new Error(`Public key should be of length ${this.publicKeyLen}`);
111
+ if (plaintext.length !== 32)
112
+ throw new Error(`Plaintext should be of length 32`);
113
+ if (seed.length !== 32)
114
+ throw new Error(`Seed should be of length 32`);
115
+
116
+ let salt = 0;
117
+ const rNTT = new Uint16Array(this.par.K * N);
118
+ for (let i = 0; i < this.par.K; i++) {
119
+ const rNTTi = rNTT.subarray(i * N, (i + 1) * N);
120
+ rNTTi.set(samplePolCBD(pseudoRandom(seed, salt++, this.par.ETA1), this.par.ETA1));
121
+ NTT(rNTTi);
122
+ }
123
+
124
+ const seedA = publicKey.subarray((12 * N * this.par.K) / 8);
125
+ const aNTT = expandA(seedA, this.par.K);
126
+ const u = new Uint16Array(this.par.K * N);
127
+ for (let i = 0; i < this.par.K; i++) {
128
+ const ui = u.subarray(i * N, (i + 1) * N);
129
+ for (let j = 0; j < this.par.K; j++) {
130
+ //note that this is a transposed a
131
+ const aNTTji = aNTT.subarray((j * this.par.K + i) * N, (j * this.par.K + i + 1) * N);
132
+ const rNTTj = rNTT.subarray(j * N, (j + 1) * N);
133
+ mulNTT(aNTTji, rNTTj);
134
+ addPol(ui, aNTTji);
135
+ }
136
+ INTT(ui);
137
+
138
+ const e1i = samplePolCBD(pseudoRandom(seed, salt++, this.par.ETA2), this.par.ETA2);
139
+ addPol(ui, e1i);
140
+ }
141
+
142
+ const ciphertext = new Uint8Array(this.ciphertextLen);
143
+ compress(u, this.par.DU);
144
+ ciphertext.set(byteEncode(u, this.par.DU));
145
+
146
+ let v: Uint16Array = new Uint16Array(N);
147
+ for (let i = 0; i < this.par.K; i++) {
148
+ const ti = new Uint16Array(N);
149
+ byteDecodeTo(
150
+ ti,
151
+ publicKey.subarray((i * 12 * N) / 8, ((i + 1) * 12 * N) / 8),
152
+ 12,
153
+ );
154
+ const rNTTi = rNTT.subarray(i * N, (i + 1) * N);
155
+ mulNTT(ti, rNTTi);
156
+ addPol(v, ti);
157
+ }
158
+ INTT(v);
159
+ const e2 = samplePolCBD(pseudoRandom(seed, salt, this.par.ETA2), this.par.ETA2);
160
+ addPol(v, e2);
161
+
162
+ for (let i = 0, byte = 0, number = 0; i < N; i++) {
163
+ const bit = 1 & (plaintext[byte] >> number++);
164
+ v[i] = addMod(v[i], QHalf * bit);
165
+ if (number === 8) {
166
+ byte++;
167
+ number = 0;
168
+ }
169
+ }
170
+ compress(v, this.par.DV);
171
+ ciphertext.set(byteEncode(v, this.par.DV), this.par.DU * N * this.par.K / 8);
172
+ return ciphertext;
173
+ }
174
+ decrypt(ciphertext: Uint8Array, privateKey: Uint8Array): Uint8Array {
175
+ if (privateKey.length !== this.privateKeyLen)
176
+ throw new Error(`Private key should be of length ${this.privateKeyLen}`);
177
+
178
+ if (ciphertext.length !== this.ciphertextLen)
179
+ throw new Error(`Ciphertext should be of length ${this.ciphertextLen}`);
180
+
181
+ const s = new Uint16Array(this.par.K * N);
182
+ byteDecodeTo(s, privateKey.subarray(0, this.par.K * 12 * N / 8), 12);
183
+
184
+ const u = new Uint16Array(this.par.K * N);
185
+ byteDecodeTo(
186
+ u,
187
+ ciphertext.subarray(0, this.par.K * this.par.DU * N / 8),
188
+ this.par.DU,
189
+ );
190
+ decompress(u, this.par.DU);
191
+
192
+ let tr: Uint16Array = new Uint16Array(N);
193
+ for (let i = 0; i < this.par.K; i++) {
194
+ const si = s.subarray(i * N, (i + 1) * N);
195
+ const ui = u.subarray(i * N, (i + 1) * N);
196
+ NTT(ui);
197
+ mulNTT(si, ui);
198
+ INTT(si);
199
+ addPol(tr, si);
200
+ }
201
+
202
+ const v = new Uint16Array(N);
203
+ byteDecodeTo(
204
+ v,
205
+ ciphertext.subarray(this.par.K * this.par.DU * N / 8),
206
+ this.par.DV,
207
+ );
208
+ decompress(v, this.par.DV);
209
+ const plaintext = new Uint8Array(N / 8);
210
+ for (let i = 0, byte = 0, number = 0; i < N; i++) {
211
+ const bit = round(subMod(v[i], tr[i]))
212
+ plaintext[byte] += bit << number++;
213
+ if (number === 8) {
214
+ byte++;
215
+ number = 0;
216
+ }
217
+ }
218
+ return plaintext;
219
+ }
220
+ };
221
+ class kyberKEM {
222
+ publicKeyLen;
223
+ privateKeyLen;
224
+ ciphertextLen;
225
+ par;
226
+ PKE;
227
+ constructor(par: kyberParams, PKE: kyberPKE) {
228
+ this.publicKeyLen = ((3 * N * par.K) >> 1) + (1 << 5);
229
+ this.privateKeyLen = ((3 * par.K * N) >> 1) + this.publicKeyLen + (1 << 6);
230
+ this.ciphertextLen = (par.DU * N * par.K + N * par.DV) >> 3;
231
+ this.par = par;
232
+ this.PKE = PKE;
233
+ }
234
+ keyGen(seed: Uint8Array = randomBytes(64)): { publicKey: Uint8Array, privateKey: Uint8Array } {
235
+ if (seed.length !== 64)
236
+ throw new Error(`Seed should be of length 64`);
237
+ const seedA = seed.subarray(0, 32);
238
+ const seedB = seed.subarray(32, 64);
239
+
240
+ const keys = this.PKE.keyGen(seedA);
241
+
242
+ const hasher = sha3_256.create();
243
+ hasher.update(keys.publicKey);
244
+ const hash = hasher.digest();
245
+
246
+ const privateKey = new Uint8Array(this.privateKeyLen);
247
+ privateKey.set(keys.privateKey);
248
+ privateKey.set(keys.publicKey, keys.privateKey.length);
249
+ privateKey.set(hash, keys.privateKey.length + keys.publicKey.length);
250
+ privateKey.set(seedB, keys.privateKey.length + keys.publicKey.length + 32);
251
+
252
+ return {
253
+ publicKey: keys.publicKey,
254
+ privateKey,
255
+ };
256
+ }
257
+ encapsulate(publicKey: Uint8Array, seed: Uint8Array = randomBytes(32)): { sharedKey: Uint8Array, ciphertext: Uint8Array } {
258
+ if (publicKey.length !== this.publicKeyLen)
259
+ throw new Error(`Public key should be of length ${this.publicKeyLen}`);
260
+ if (seed.length !== 32)
261
+ throw new Error(`Seed should be of length 32`);
262
+
263
+ const ek = publicKey.subarray(0, 384 * this.par.K);
264
+ const eke = new Uint16Array(N * this.par.K);
265
+ byteDecodeTo(eke, ek, 12);
266
+ if (!eke.every((value) => value < Q))
267
+ throw new Error(`Invalid private key - values are not in range [0, 3329)`);
268
+
269
+ const hasherH = sha3_512.create();
270
+ hasherH.update(seed);
271
+
272
+ const hasherG = sha3_256.create();
273
+ hasherG.update(publicKey);
274
+ hasherH.update(hasherG.digest());
275
+
276
+ const hash = hasherH.digest();
277
+ const ciphertext = this.PKE.encrypt(seed, publicKey, hash.subarray(32));
278
+ return {
279
+ sharedKey: hash.subarray(0, 32),
280
+ ciphertext,
281
+ };
282
+ }
283
+ decapsulate(ciphertext: Uint8Array, privateKey: Uint8Array): Uint8Array {
284
+ if (ciphertext.length !== this.ciphertextLen)
285
+ throw new Error(`Ciphertext should be of length ${this.ciphertextLen}`);
286
+ if (privateKey.length !== this.privateKeyLen)
287
+ throw new Error(`Private key should be of length ${this.privateKeyLen}`);
288
+
289
+ const checkHasher = sha3_256.create();
290
+ const dKeyLen = 384 * this.par.K;
291
+ const eKeyLen = dKeyLen + 32;
292
+ const eKey = privateKey.subarray(dKeyLen, dKeyLen + eKeyLen);
293
+
294
+ checkHasher.update(eKey);
295
+ const checkHash = checkHasher.digest();
296
+ const hashInMessage = privateKey.subarray(dKeyLen + eKeyLen, dKeyLen + eKeyLen + 32);
297
+ if (!checkHash.every((index) => checkHash[index] === hashInMessage[index]))
298
+ throw new Error(`Decryption key does not math its hash encoded in private key`);
299
+
300
+ const dKey = privateKey.subarray(0, dKeyLen);
301
+ const cand = this.PKE.decrypt(ciphertext, dKey);
302
+
303
+ const hasherH = sha3_512.create();
304
+ hasherH.update(cand);
305
+ hasherH.update(checkHash);
306
+ const hash = hasherH.digest();
307
+
308
+ const resCiphertext = this.PKE.encrypt(cand, eKey, hash.subarray(32));
309
+
310
+ const rejZ = privateKey.subarray(dKeyLen + eKeyLen + 32);
311
+ const rejectionHasher = shake256.create({ dkLen: 32 });
312
+ rejectionHasher.update(rejZ);
313
+ rejectionHasher.update(ciphertext);
314
+ if (!ciphertext.every((index) => ciphertext[index] === resCiphertext[index]))
315
+ return rejectionHasher.digest();
316
+
317
+ return hash.subarray(0, 32);
318
+ }
319
+ };
320
+
321
+ export const kyber_pke512 = new kyberPKE(kyberParams.v512);
322
+ export const kyber_pke768 = new kyberPKE(kyberParams.v768);
323
+ export const kyber_pke1024 = new kyberPKE(kyberParams.v1024);
324
+
325
+ export const kyber_kem512 = new kyberKEM(kyberParams.v512, kyber_pke512);
326
+ export const kyber_kem768 = new kyberKEM(kyberParams.v768, kyber_pke768);
327
+ export const kyber_kem1024 = new kyberKEM(kyberParams.v1024, kyber_pke1024);
package/package.json ADDED
@@ -0,0 +1,27 @@
1
+ {
2
+ "name": "kyber-mini",
3
+ "version": "1.0.0",
4
+ "description": "A lightweight kyber PKE and KEM library",
5
+ "license": "MIT",
6
+ "author": "rag27",
7
+ "type": "module",
8
+ "main": "index.js",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./index.ts",
12
+ "import": "./dist/index.js",
13
+ "require": "./dist/index.cjs"
14
+ }
15
+ },
16
+ "devDependencies": {
17
+ "@types/bun": "^1.3.6",
18
+ "@types/node": "^25.0.8"
19
+ },
20
+ "dependencies": {
21
+ "@noble/hashes": "^2.0.1"
22
+ },
23
+ "repository": {
24
+ "type": "git",
25
+ "url": "git+https://codeberg.org/RAG27/kyber"
26
+ }
27
+ }
package/utils.ts ADDED
@@ -0,0 +1,202 @@
1
+ import { shake128, shake256 } from "@noble/hashes/sha3.js";
2
+ const Q = 3329;
3
+ const N = 256;
4
+ function mulMod(a: number, b: number) {
5
+ return (a * b) % Q;
6
+ }
7
+ export function addMod(a: number, b: number) {
8
+ return (a + b) % Q;
9
+ }
10
+ export function subMod(a: number, b: number) {
11
+ return (a - b + Q) % Q;
12
+ }
13
+ const zetas = new Uint16Array([
14
+ 1729, 2580, 3289, 2642, 630, 1897, 848, 1062, 1919, 193, 797, 2786, 3260, 569, 1746, 296,
15
+ 2447, 1339, 1476, 3046, 56, 2240, 1333, 1426, 2094, 535, 2882, 2393, 2879, 1974, 821, 289,
16
+ 331, 3253, 1756, 1197, 2304, 2277, 2055, 650, 1977, 2513, 632, 2865, 33, 1320, 1915, 2319,
17
+ 1435, 807, 452, 1438, 2868, 1534, 2402, 2647, 2617, 1481, 648, 2474, 3110, 1227, 910, 17,
18
+ 2761, 583, 2649, 1637, 723, 2288, 1100, 1409, 2662, 3281, 233, 756, 2156, 3015, 3050, 1703,
19
+ 1651, 2789, 1789, 1847, 952, 1461, 2687, 939, 2308, 2437, 2388, 733, 2337, 268, 641, 1584,
20
+ 2298, 2037, 3220, 375, 2549, 2090, 1645, 1063, 319, 2773, 757, 2099, 561, 2466, 2594, 2804,
21
+ 1092, 403, 1026, 1143, 2150, 2775, 886, 1722, 1212, 1874, 1029, 2110, 2935, 885, 2154,
22
+ ]);
23
+ const invZetas = new Uint16Array([
24
+ 1600, 40, 749, 2481, 1432, 2699, 687, 1583, 2760, 69, 543, 2532, 3136, 1410, 2267, 2508,
25
+ 1355, 450, 936, 447, 2794, 1235, 1903, 1996, 1089, 3273, 283, 1853, 1990, 882, 3033, 2419,
26
+ 2102, 219, 855, 2681, 1848, 712, 682, 927, 1795, 461, 1891, 2877, 2522, 1894, 1010, 1414,
27
+ 2009, 3296, 464, 2697, 816, 1352, 2679, 1274, 1052, 1025, 2132, 1573, 76, 2998, 3040, 1175,
28
+ 2444, 394, 1219, 2300, 1455, 2117, 1607, 2443, 554, 1179, 2186, 2303, 2926, 2237, 525, 735,
29
+ 863, 2768, 1230, 2572, 556, 3010, 2266, 1684, 1239, 780, 2954, 109, 1292, 1031, 1745, 2688,
30
+ 3061, 992, 2596, 941, 892, 1021, 2390, 642, 1868, 2377, 1482, 1540, 540, 1678, 1626, 279,
31
+ 314, 1173, 2573, 3096, 48, 667, 1920, 2229, 1041, 2606, 1692, 680, 2746, 568, 3312,
32
+ ]);
33
+ export function NTT(pol: Uint16Array) {
34
+ for (let endLength = N / 2, m = 0; endLength >= 2; endLength /= 2) {
35
+ for (let start = 0; start < N; start += 2 * endLength, m++) {
36
+ for (let index = start; index < start + endLength; index++) {
37
+ const t = mulMod(pol[index + endLength], zetas[m]);
38
+ pol[index + endLength] = subMod(pol[index], t);
39
+ pol[index] = addMod(pol[index], t);
40
+ }
41
+ }
42
+ }
43
+ }
44
+ const kyberInv2 = (((Q + 1) / 2)) % Q;
45
+ export function INTT(pol: Uint16Array) {
46
+ for (let beginLength = 2, m = N / 2 - 2; beginLength <= 128; beginLength *= 2) {
47
+ for (let start = 256 - 2 * beginLength; start >= 0; start -= 2 * beginLength, m--) {
48
+ for (let index = start; index < start + beginLength; index++) {
49
+ const pa = pol[index];
50
+ pol[index] = mulMod(addMod(pol[index], pol[index + beginLength]), kyberInv2);
51
+ pol[index + beginLength] = mulMod(subMod(pa, pol[index + beginLength]), mulMod(kyberInv2, invZetas[m]));
52
+ }
53
+ }
54
+ }
55
+ }
56
+ export function mulNTT(pol1: Uint16Array, pol2: Uint16Array) {
57
+ const max = (N / 2 - 2);
58
+ let m = 0;
59
+ for (let index = N - 2; index >= 0; index -= 2) {
60
+ let z;
61
+ if ((m & 1) === 0)
62
+ z = subMod(0, zetas[max - m / 2]);
63
+ else
64
+ z = zetas[max - (m - 1) / 2];
65
+ const save = pol1[index];
66
+ pol1[index] = addMod(
67
+ mulMod(pol1[index], pol2[index]),
68
+ mulMod(
69
+ z,
70
+ mulMod(pol1[index + 1], pol2[index + 1])
71
+ )
72
+ );
73
+ pol1[index + 1] = addMod(
74
+ mulMod(save, pol2[index + 1]),
75
+ mulMod(pol2[index], pol1[index + 1])
76
+ );
77
+ m++;
78
+ }
79
+ }
80
+ export function addPol(pol1: Uint16Array, pol2: Uint16Array) {
81
+ for (let index = 0; index < N; index++)
82
+ pol1[index] = addMod(pol1[index], pol2[index]);
83
+ }
84
+ function sampleNTT(seed: Uint8Array, i: number, j: number) {
85
+ const ret: Uint16Array = new Uint16Array(N);
86
+ const hasher = shake128.create({ dkLen: 840 });
87
+ hasher.update(seed);
88
+ hasher.update(new Uint8Array([j]));
89
+ hasher.update(new Uint8Array([i]));
90
+ const allC = hasher.digest();
91
+ for (let i = 0, limit = 0; i < N && limit < 280; limit++) {
92
+ const d1 = allC[limit * 3] + N * (allC[limit * 3 + 1] & ((1 << 4) - 1));
93
+ const d2 = ((allC[limit * 3 + 1] >> 4) | 0) + (allC[limit * 3 + 2] << 4);
94
+ if (d1 < Q)
95
+ ret[i++] = d1;
96
+ if (d2 < Q && i < N)
97
+ ret[i++] = d2;
98
+ }
99
+ return ret;
100
+ }
101
+ export function byteDecodeTo(to: Uint16Array, bytes: Uint8Array, d: number) {
102
+ for (let i = 0, byte = 0, left = 8; i < to.length; i++) {
103
+ to[i] = 0;
104
+ for (let current = d; current > 0;) {
105
+ const move = Math.min(current, left);
106
+ to[i] += ((bytes[byte] >> (8 - left)) & ((1 << move) - 1)) << (d - current);
107
+ current -= move;
108
+ left -= move;
109
+ if (left === 0) {
110
+ left = 8;
111
+ byte++;
112
+ }
113
+ }
114
+ }
115
+ }
116
+ export function byteEncode(array: Uint16Array, d: number) {
117
+ const ret = new Uint8Array(Math.ceil(array.length * d / 8));
118
+ for (let i = 0, byte = 0, left = 8; i < array.length; i++) {
119
+ let a = array[i];
120
+ for (let current = d; current > 0;) {
121
+ const move = Math.min(current, left);
122
+ ret[byte] += (a & ((1 << move) - 1)) << (8 - left);
123
+ current -= move;
124
+ a >>= move;
125
+ left -= move;
126
+ if (left === 0) {
127
+ left = 8;
128
+ byte++;
129
+ }
130
+ }
131
+ }
132
+ return ret;
133
+ }
134
+ export function samplePolCBD(seed: Uint8Array, eta: number) {
135
+ const pol: Uint16Array = new Uint16Array(N);
136
+ for (let i = 0, byte = 0, number = 0; i < N; i++) {
137
+ let x = 0;
138
+ for (let j = 0; j < eta; j++) {
139
+ x += 1 & (seed[byte] >> number++);
140
+ if (number === 8) {
141
+ byte++;
142
+ number = 0;
143
+ }
144
+ }
145
+ let y = 0;
146
+ for (let j = 0; j < eta; j++) {
147
+ y += 1 & (seed[byte] >> number++);
148
+ if (number === 8) {
149
+ byte++;
150
+ number = 0;
151
+ }
152
+ }
153
+ pol[i] = subMod(x, y);
154
+ }
155
+ return pol;
156
+ }
157
+ export function pseudoRandom(seed: Uint8Array, salt: number, eta: number): Uint8Array {
158
+ const hasher = shake256.create({ dkLen: eta << 6 });
159
+ hasher.update(seed)
160
+ hasher.update(new Uint8Array([salt]));
161
+ return hasher.digest();
162
+ }
163
+ export function expandA(seed: Uint8Array, K: number) {
164
+ const ret = new Uint16Array(K * K * N);
165
+ for (let i = 0; i < K; i++) {
166
+ for (let j = 0; j < K; j++) {
167
+ const pol = sampleNTT(seed, i, j);
168
+ ret.set(pol, (i * K + j) * N);
169
+ }
170
+ }
171
+ return ret;
172
+ }
173
+ export function compress(array: Uint16Array, d: number) {
174
+ const powd = 1 << d;
175
+ for (let i = 0; i < array.length; i++) {
176
+ const mod = (array[i] * powd) % Q;
177
+ if (mod >= Q - mod)
178
+ array[i] = (((array[i] * powd + Q - 1) / Q) | 0) & (powd - 1);
179
+ else
180
+ array[i] = (((array[i] * powd) / Q) | 0) & (powd - 1);
181
+ }
182
+ }
183
+ export function decompress(array: Uint16Array, d: number) {
184
+ const powd = 1 << d;
185
+ for (let i = 0; i < array.length; i++) {
186
+ const mod = array[i] * Q & (powd - 1);
187
+ if ((array[i] * Q) >= powd - mod)
188
+ array[i] = (((array[i] * Q + powd - 1) / powd) | 0);
189
+ else
190
+ array[i] = (((array[i] * Q) / powd) | 0);
191
+ }
192
+ }
193
+ export function randomBytes(n: number): Uint8Array {
194
+ const ret = new Uint8Array(n);
195
+ crypto.getRandomValues(ret);
196
+ return ret;
197
+ }
198
+ export function round(n: number) {
199
+ if (n <= ((Q / 4) | 0) || n >= subMod(0, (Q / 4) | 0))
200
+ return 0;
201
+ return 1;
202
+ }