@noble/post-quantum 0.5.3 → 0.6.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/README.md +70 -39
- package/_crystals.d.ts +84 -0
- package/_crystals.d.ts.map +1 -1
- package/_crystals.js +64 -3
- package/_crystals.js.map +1 -1
- package/falcon.d.ts +84 -0
- package/falcon.d.ts.map +1 -0
- package/falcon.js +2378 -0
- package/falcon.js.map +1 -0
- package/hybrid.d.ts +181 -5
- package/hybrid.d.ts.map +1 -1
- package/hybrid.js +375 -53
- package/hybrid.js.map +1 -1
- package/ml-dsa.d.ts +22 -1
- package/ml-dsa.d.ts.map +1 -1
- package/ml-dsa.js +101 -51
- package/ml-dsa.js.map +1 -1
- package/ml-kem.d.ts +27 -3
- package/ml-kem.d.ts.map +1 -1
- package/ml-kem.js +154 -52
- package/ml-kem.js.map +1 -1
- package/package.json +12 -5
- package/slh-dsa.d.ts +116 -13
- package/slh-dsa.d.ts.map +1 -1
- package/slh-dsa.js +134 -35
- package/slh-dsa.js.map +1 -1
- package/src/_crystals.ts +101 -7
- package/src/falcon.ts +2470 -0
- package/src/hybrid.ts +406 -72
- package/src/ml-dsa.ts +144 -74
- package/src/ml-kem.ts +168 -54
- package/src/slh-dsa.ts +203 -44
- package/src/utils.ts +320 -15
- package/utils.d.ts +283 -4
- package/utils.d.ts.map +1 -1
- package/utils.js +245 -14
- package/utils.js.map +1 -1
package/src/_crystals.ts
CHANGED
|
@@ -8,28 +8,78 @@ import { shake128, shake256 } from '@noble/hashes/sha3.js';
|
|
|
8
8
|
import type { TypedArray } from '@noble/hashes/utils.js';
|
|
9
9
|
import { type BytesCoderLen, cleanBytes, type Coder, getMask } from './utils.ts';
|
|
10
10
|
|
|
11
|
+
/** Extendable-output reader used by the CRYSTALS implementations. */
|
|
11
12
|
export type XOF = (
|
|
12
13
|
seed: Uint8Array,
|
|
13
14
|
blockLen?: number
|
|
14
15
|
) => {
|
|
16
|
+
/**
|
|
17
|
+
* Read diagnostic counters for the current XOF session.
|
|
18
|
+
* @returns Current call and XOF block counters.
|
|
19
|
+
*/
|
|
15
20
|
stats: () => { calls: number; xofs: number };
|
|
21
|
+
/**
|
|
22
|
+
* Select one `(x, y)` coordinate pair and get a block reader for it.
|
|
23
|
+
* Only one coordinate stream is live at a time: a later `get(...)` call rebinds the shared
|
|
24
|
+
* SHAKE state and invalidates older readers.
|
|
25
|
+
* Each squeeze aliases one mutable internal output buffer, so callers must copy blocks they
|
|
26
|
+
* want to retain before the next read.
|
|
27
|
+
* @param x - First matrix coordinate.
|
|
28
|
+
* @param y - Second matrix coordinate.
|
|
29
|
+
* @returns Lazy block reader for that coordinate pair.
|
|
30
|
+
*/
|
|
16
31
|
get: (x: number, y: number) => () => Uint8Array; // return block aligned to blockLen and 3
|
|
32
|
+
/** Wipe any buffered state once the reader is no longer needed. */
|
|
17
33
|
clean: () => void;
|
|
18
34
|
};
|
|
19
35
|
|
|
20
36
|
/** CRYSTALS (ml-kem, ml-dsa) options */
|
|
37
|
+
/** Shared polynomial and NTT parameters for CRYSTALS algorithms. */
|
|
21
38
|
export type CrystalOpts<T extends TypedArray> = {
|
|
39
|
+
/**
|
|
40
|
+
* Allocate one zeroed polynomial/vector container.
|
|
41
|
+
* @param n - Number of coefficients to allocate.
|
|
42
|
+
* @returns Fresh typed container.
|
|
43
|
+
*/
|
|
22
44
|
newPoly: TypedCons<T>;
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
45
|
+
/** Polynomial size, typically `256`. */
|
|
46
|
+
N: number;
|
|
47
|
+
/** Prime modulus used for all coefficient arithmetic. */
|
|
48
|
+
Q: number;
|
|
49
|
+
/** Inverse transform normalization factor:
|
|
50
|
+
* `256**-1 mod q` for Dilithium, `128**-1 mod q` for Kyber.
|
|
51
|
+
*/
|
|
52
|
+
F: number;
|
|
53
|
+
/** Principal root of unity for the transform domain. */
|
|
26
54
|
ROOT_OF_UNITY: number;
|
|
27
|
-
|
|
55
|
+
/** Number of bits used for bit-reversal ordering. */
|
|
56
|
+
brvBits: number;
|
|
57
|
+
/** `true` for Kyber/ML-KEM mode, `false` for Dilithium/ML-DSA mode. */
|
|
28
58
|
isKyber: boolean;
|
|
29
59
|
};
|
|
30
60
|
|
|
61
|
+
/** Constructor function for typed polynomial containers. */
|
|
31
62
|
export type TypedCons<T extends TypedArray> = (n: number) => T;
|
|
32
63
|
|
|
64
|
+
/**
|
|
65
|
+
* Creates shared modular arithmetic, NTT, and packing helpers for CRYSTALS schemes.
|
|
66
|
+
* @param opts - Polynomial and transform parameters. See {@link CrystalOpts}.
|
|
67
|
+
* @returns CRYSTALS arithmetic and encoding helpers.
|
|
68
|
+
* @example
|
|
69
|
+
* Create shared modular arithmetic and NTT helpers for a CRYSTALS parameter set.
|
|
70
|
+
* ```ts
|
|
71
|
+
* const crystals = genCrystals({
|
|
72
|
+
* newPoly: (n) => new Uint16Array(n),
|
|
73
|
+
* N: 256,
|
|
74
|
+
* Q: 3329,
|
|
75
|
+
* F: 3303,
|
|
76
|
+
* ROOT_OF_UNITY: 17,
|
|
77
|
+
* brvBits: 7,
|
|
78
|
+
* isKyber: true,
|
|
79
|
+
* });
|
|
80
|
+
* const reduced = crystals.mod(-1);
|
|
81
|
+
* ```
|
|
82
|
+
*/
|
|
33
83
|
export const genCrystals = <T extends TypedArray>(
|
|
34
84
|
opts: CrystalOpts<T>
|
|
35
85
|
): {
|
|
@@ -37,23 +87,30 @@ export const genCrystals = <T extends TypedArray>(
|
|
|
37
87
|
smod: (a: number, modulo?: number) => number;
|
|
38
88
|
nttZetas: T;
|
|
39
89
|
NTT: {
|
|
90
|
+
/** Forward transform in place. Mutates and returns `r`. */
|
|
40
91
|
encode: (r: T) => T;
|
|
92
|
+
/** Inverse transform in place. Mutates and returns `r`. */
|
|
41
93
|
decode: (r: T) => T;
|
|
42
94
|
};
|
|
43
95
|
bitsCoder: (d: number, c: Coder<number, number>) => BytesCoderLen<T>;
|
|
44
96
|
} => {
|
|
45
97
|
// isKyber: true means Kyber, false means Dilithium
|
|
46
98
|
const { newPoly, N, Q, F, ROOT_OF_UNITY, brvBits, isKyber } = opts;
|
|
99
|
+
// Normalize JS `%` into the canonical Z_m representative `[0, modulo-1]` expected by
|
|
100
|
+
// FIPS 203 §2.3 / FIPS 204 §2.3 before downstream mod-q arithmetic.
|
|
47
101
|
const mod = (a: number, modulo = Q): number => {
|
|
48
102
|
const result = a % modulo | 0;
|
|
49
103
|
return (result >= 0 ? result | 0 : (modulo + result) | 0) | 0;
|
|
50
104
|
};
|
|
51
|
-
//
|
|
105
|
+
// FIPS 204 §7.4 uses the centered `mod ±` representative for low bits, keeping the
|
|
106
|
+
// positive midpoint when `modulo` is even.
|
|
107
|
+
// Center to `[-floor((modulo-1)/2), floor(modulo/2)]`.
|
|
52
108
|
const smod = (a: number, modulo = Q): number => {
|
|
53
109
|
const r = mod(a, modulo) | 0;
|
|
54
110
|
return (r > modulo >> 1 ? (r - modulo) | 0 : r) | 0;
|
|
55
111
|
};
|
|
56
|
-
//
|
|
112
|
+
// Kyber uses the FIPS 203 Appendix A `BitRev_7` table here via the first 128 entries, while
|
|
113
|
+
// Dilithium uses the FIPS 204 §7.5 / Appendix B `BitRev_8` zetas table over all 256 entries.
|
|
57
114
|
function getZettas() {
|
|
58
115
|
const out = newPoly(N);
|
|
59
116
|
for (let i = 0; i < N; i++) {
|
|
@@ -94,12 +151,15 @@ export const genCrystals = <T extends TypedArray>(
|
|
|
94
151
|
},
|
|
95
152
|
decode: (r: T): T => {
|
|
96
153
|
dit(r as any);
|
|
154
|
+
// The inverse-NTT normalization factor is family-specific: FIPS 203 Algorithm 10 line 14
|
|
155
|
+
// uses `128^-1 mod q` for Kyber, while FIPS 204 Algorithm 42 lines 21-23 use `256^-1 mod q`.
|
|
97
156
|
// kyber uses 128 here, because brv && stuff
|
|
98
157
|
for (let i = 0; i < r.length; i++) r[i] = mod(F * r[i]);
|
|
99
158
|
return r;
|
|
100
159
|
},
|
|
101
160
|
};
|
|
102
|
-
//
|
|
161
|
+
// Pack one little-endian `d`-bit word per coefficient, matching FIPS 203 ByteEncode /
|
|
162
|
+
// ByteDecode and the FIPS 204 BitsToBytes-based polynomial packing helpers.
|
|
103
163
|
const bitsCoder = (d: number, c: Coder<number, number>): BytesCoderLen<T> => {
|
|
104
164
|
const mask = getMask(d);
|
|
105
165
|
const bytesLen = d * (N / 8);
|
|
@@ -148,6 +208,8 @@ const createXofShake =
|
|
|
148
208
|
return {
|
|
149
209
|
stats: () => ({ calls, xofs }),
|
|
150
210
|
get: (x: number, y: number) => {
|
|
211
|
+
// Rebind to `seed || x || y` so callers can implement the spec's per-coordinate
|
|
212
|
+
// SHAKE inputs like `rho || j || i` and `rho || IntegerToBytes(counter, 2)`.
|
|
151
213
|
_seed[seedLen + 0] = x;
|
|
152
214
|
_seed[seedLen + 1] = y;
|
|
153
215
|
h.destroy();
|
|
@@ -165,5 +227,37 @@ const createXofShake =
|
|
|
165
227
|
};
|
|
166
228
|
};
|
|
167
229
|
|
|
230
|
+
/**
|
|
231
|
+
* SHAKE128-based extendable-output reader factory used by ML-KEM.
|
|
232
|
+
* `get(x, y)` selects one coordinate pair at a time; calling it again invalidates previously
|
|
233
|
+
* returned readers, and each squeeze reuses one mutable internal output buffer.
|
|
234
|
+
* @param seed - Seed bytes for the reader.
|
|
235
|
+
* @param blockLen - Optional output block length.
|
|
236
|
+
* @returns Stateful XOF reader.
|
|
237
|
+
* @example
|
|
238
|
+
* Build the ML-KEM SHAKE128 matrix expander and read one block.
|
|
239
|
+
* ```ts
|
|
240
|
+
* import { randomBytes } from '@noble/post-quantum/utils.js';
|
|
241
|
+
* import { XOF128 } from '@noble/post-quantum/_crystals.js';
|
|
242
|
+
* const reader = XOF128(randomBytes(32));
|
|
243
|
+
* const block = reader.get(0, 0)();
|
|
244
|
+
* ```
|
|
245
|
+
*/
|
|
168
246
|
export const XOF128: XOF = /* @__PURE__ */ createXofShake(shake128);
|
|
247
|
+
/**
|
|
248
|
+
* SHAKE256-based extendable-output reader factory used by ML-DSA.
|
|
249
|
+
* `get(x, y)` appends raw one-byte coordinates to the seed, invalidates previously returned
|
|
250
|
+
* readers, and reuses one mutable internal output buffer for each squeeze.
|
|
251
|
+
* @param seed - Seed bytes for the reader.
|
|
252
|
+
* @param blockLen - Optional output block length.
|
|
253
|
+
* @returns Stateful XOF reader.
|
|
254
|
+
* @example
|
|
255
|
+
* Build the ML-DSA SHAKE256 coefficient expander and read one block.
|
|
256
|
+
* ```ts
|
|
257
|
+
* import { randomBytes } from '@noble/post-quantum/utils.js';
|
|
258
|
+
* import { XOF256 } from '@noble/post-quantum/_crystals.js';
|
|
259
|
+
* const reader = XOF256(randomBytes(32));
|
|
260
|
+
* const block = reader.get(0, 0)();
|
|
261
|
+
* ```
|
|
262
|
+
*/
|
|
169
263
|
export const XOF256: XOF = /* @__PURE__ */ createXofShake(shake256);
|