@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/falcon.js
ADDED
|
@@ -0,0 +1,2378 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Falcon pq-friendly signature algorithm.
|
|
3
|
+
* Will change in backwards-incompatible way once FIPS-206 gets finalized.
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
/*! noble-post-quantum - MIT License (c) 2024 Paul Miller (paulmillr.com) */
|
|
7
|
+
import { rngAesCtrDrbg256 } from '@noble/ciphers/aes.js';
|
|
8
|
+
import { chacha20 } from '@noble/ciphers/chacha.js';
|
|
9
|
+
import { FFTCore } from '@noble/curves/abstract/fft.js';
|
|
10
|
+
import { invert } from '@noble/curves/abstract/modular.js';
|
|
11
|
+
import { bytesToNumberLE, numberToHexUnpadded } from '@noble/curves/utils.js';
|
|
12
|
+
import { shake256 } from '@noble/hashes/sha3.js';
|
|
13
|
+
import { abytes, bytesToHex, createView, hexToBytes, randomBytes, swap32IfBE, u32, u8, } from '@noble/hashes/utils.js';
|
|
14
|
+
import { genCrystals } from "./_crystals.js";
|
|
15
|
+
import { cleanBytes, getMask, baswap64If, splitCoder, validateSigOpts, validateVerOpts, } from "./utils.js";
|
|
16
|
+
/*
|
|
17
|
+
FIPS-206 would likely improve the situation with spec.
|
|
18
|
+
|
|
19
|
+
Falcon (non-FIPS) spec is terrible. Two main issues: non-deterministic keys & floats.
|
|
20
|
+
|
|
21
|
+
## Summary
|
|
22
|
+
|
|
23
|
+
- NIST round3 KATs pass
|
|
24
|
+
- No interop with other JS libraries, because they are incorrect
|
|
25
|
+
- No recoverPublicKey: it requires s1, which is calculated from public+s2. Sig only has s2.
|
|
26
|
+
- Code has verify_recover, but it's unused
|
|
27
|
+
- Mediocre code quality, primarily because it follows implementation-specific (C lib) tidbits
|
|
28
|
+
- Samplers are fragile
|
|
29
|
+
|
|
30
|
+
## Non-deterministic keys
|
|
31
|
+
|
|
32
|
+
Falcon spec doesn't provide enough data to re-create keys from KAT vectors. Spec mentions:
|
|
33
|
+
> This process reduces the maximum sizes of coefficients of F and G
|
|
34
|
+
> by about 30 bits at each iteration
|
|
35
|
+
While actual implementation reduces them by 25 bits (scale_k), which is very important detail.
|
|
36
|
+
|
|
37
|
+
There are also various implementation checks not mentioned in spec, like
|
|
38
|
+
> let's skip this perfectly valid key, because it doesn't fit into our bigint implementation
|
|
39
|
+
|
|
40
|
+
Without these, it's hard to produce correct keys. This means that,
|
|
41
|
+
unless NIST specifies full process with all operations,
|
|
42
|
+
**all keys are implementation-specific**.
|
|
43
|
+
|
|
44
|
+
Which means, we cannot use any key derivation schemes here: same seed will return
|
|
45
|
+
different keys in different implementations.
|
|
46
|
+
|
|
47
|
+
This also complicates testing a lot. If a key succesfully signs a message and other implementations
|
|
48
|
+
confirm it, there is still zero assurance with regards to quality / entropy of the key.
|
|
49
|
+
One can create a valid key, which nevertheless doesn't have enough entropy.
|
|
50
|
+
|
|
51
|
+
## Floats
|
|
52
|
+
|
|
53
|
+
Partially fixed by "fixed point" primitive. Falcon basically impelements floats on top of u64.
|
|
54
|
+
|
|
55
|
+
It's more constant-time, but in JS there is no **fast** u64:
|
|
56
|
+
|
|
57
|
+
- Using bigint backend would drop const-timeness
|
|
58
|
+
- Using u32 {hi, low} tuples means unnecessary allocations / jit deopt,
|
|
59
|
+
and is still 4 times slower than Floats
|
|
60
|
+
|
|
61
|
+
Then, there are rounding issues. This is implementation specific.
|
|
62
|
+
This matters more for C, since 'double' is not neccesarily binary64.
|
|
63
|
+
In js, floats guaranteed to be IEEE-754 binary64.
|
|
64
|
+
|
|
65
|
+
In theory floats are nice, but since fixed point format is not specified in spec
|
|
66
|
+
(other that "it is binary64"), this is even more fragile, since it doesn't implement exact
|
|
67
|
+
full spec of binary64 (two zeros/subnomarls/nans/etc). Those parts should not be used inside falcon,
|
|
68
|
+
but may cause some differences.
|
|
69
|
+
|
|
70
|
+
Lack of specification is also hard to debug, brings precision loss (a+b+c !== a+c+b):
|
|
71
|
+
there are no serialized floats, all float arithmetic happens inside of an algorithm, so
|
|
72
|
+
we can produce same results (small differences rounded at the end).
|
|
73
|
+
|
|
74
|
+
For byte-to-byte result in falcon, one needs to copy implementation-specific details, unspecced.
|
|
75
|
+
|
|
76
|
+
## CSPRNG
|
|
77
|
+
|
|
78
|
+
NIST KATs randomness situation is bad:
|
|
79
|
+
|
|
80
|
+
1. aes-drbg generates seed
|
|
81
|
+
2. The seed passes CSPRNG into sign, which uses shake256 to produce another seed and nonce
|
|
82
|
+
3. Then a separate rejection sampling chacha20 CSPRNG is created, based on that seed.
|
|
83
|
+
|
|
84
|
+
## Detached vs non-detached
|
|
85
|
+
|
|
86
|
+
The API is different between detached / non-detached signatures,
|
|
87
|
+
however only non-detached (sm) is included in KAT, so we implement them
|
|
88
|
+
(crypto_sign_open instead of crypto_sign_verify).
|
|
89
|
+
*/
|
|
90
|
+
// Utils
|
|
91
|
+
// MSB first. Current Falcon uses are byte-aligned only, and outer wrappers must still enforce
|
|
92
|
+
// exact body lengths / canonical padding because this helper neither flushes nor rejects a final
|
|
93
|
+
// partial field on its own.
|
|
94
|
+
const bitsCoderMSB = (newPoly, N, d, c) => {
|
|
95
|
+
const mask = getMask(d);
|
|
96
|
+
const bytesLen = d * (N / 8);
|
|
97
|
+
return {
|
|
98
|
+
bytesLen,
|
|
99
|
+
encode: (poly) => {
|
|
100
|
+
if (poly.length !== N)
|
|
101
|
+
throw new Error(`wrong length: expected ${N}, got ${poly.length}`);
|
|
102
|
+
const r = new Uint8Array(bytesLen);
|
|
103
|
+
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
|
|
104
|
+
buf = (buf << d) | (c.encode(poly[i]) & mask);
|
|
105
|
+
bufLen += d;
|
|
106
|
+
for (; bufLen >= 8; bufLen -= 8)
|
|
107
|
+
r[pos++] = (buf >>> (bufLen - 8)) & 0xff;
|
|
108
|
+
}
|
|
109
|
+
return r;
|
|
110
|
+
},
|
|
111
|
+
decode: (bytes) => {
|
|
112
|
+
const r = newPoly(N);
|
|
113
|
+
for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
|
|
114
|
+
buf = (buf << 8) | bytes[i];
|
|
115
|
+
bufLen += 8;
|
|
116
|
+
for (; bufLen >= d; bufLen -= d)
|
|
117
|
+
r[pos++] = c.decode((buf >>> (bufLen - d)) & mask);
|
|
118
|
+
}
|
|
119
|
+
return r;
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
};
|
|
123
|
+
// Adds a single leading tag byte. Exact body validation is delegated to `restCoder.decode()`.
|
|
124
|
+
// encode() zeroizes the temporary encoded body after copying, so wrapped encoders must return
|
|
125
|
+
// owned scratch bytes rather than caller-owned buffers.
|
|
126
|
+
const headerCoder = (tag, restCoder) => {
|
|
127
|
+
return {
|
|
128
|
+
bytesLen: 1 + restCoder.bytesLen,
|
|
129
|
+
encode(value) {
|
|
130
|
+
const body = restCoder.encode(value);
|
|
131
|
+
const out = new Uint8Array(1 + body.length);
|
|
132
|
+
out[0] = tag;
|
|
133
|
+
out.set(body, 1);
|
|
134
|
+
cleanBytes(body);
|
|
135
|
+
return out;
|
|
136
|
+
},
|
|
137
|
+
decode(data) {
|
|
138
|
+
if (data[0] !== tag)
|
|
139
|
+
throw new Error(`wrong tag: expected ${tag}, got 0x${data[0]}`);
|
|
140
|
+
return restCoder.decode(data.subarray(1));
|
|
141
|
+
},
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
// Fun, but overengineered. Hoping FIPS would fix this.
|
|
145
|
+
// Falcon-specific Golomb-Rice compressed format:
|
|
146
|
+
// Vec<[1bit sign, 7 bit low, array(1 terminated).length==high <<7]>.
|
|
147
|
+
// decode() returns only coefficients, so callers must still enforce exact consumed length /
|
|
148
|
+
// canonical framing around the payload.
|
|
149
|
+
const compCoder = (n) => {
|
|
150
|
+
const LIMIT = 2047;
|
|
151
|
+
return {
|
|
152
|
+
encode(data) {
|
|
153
|
+
// Algorithm 17: Compress(s, slen) (Page 47)
|
|
154
|
+
// Require: A polynomial s = Σ sᵢxⁱ ∈ Z[x] of degree < n, a string bitlength slen
|
|
155
|
+
// Ensure: A compressed representation str of s of slen bits, or ⊥
|
|
156
|
+
// 1: str ← {} ▷ str is the empty string
|
|
157
|
+
// 2: for i from 0 to n-1 do ▷ At each step, str ← (str||strᵢ), where strᵢ encodes sᵢ
|
|
158
|
+
// 3: str ← (str||b), where b = 1 if sᵢ < 0, b = 0 otherwise ▷ Encode the sign of sᵢ
|
|
159
|
+
// 4: str ← (str||b₆b₅...b₀), where bⱼ = (|sᵢ| >> j) & 0x1
|
|
160
|
+
// ▷ Encode in binary the low bits of |sᵢ|
|
|
161
|
+
// 5: k ← |sᵢ| >> 7
|
|
162
|
+
// 6: str ← (str||0ᵏ1) ▷ Encode in unary the high bits of |sᵢ|
|
|
163
|
+
// 7: if |str| > slen then
|
|
164
|
+
// 8: str ← ⊥ ▷ Abort if str is too long
|
|
165
|
+
// 9: else
|
|
166
|
+
// 10: str ← (str||0^{slen-|str|}) ▷ Pad str to slen bits
|
|
167
|
+
// 11: return str
|
|
168
|
+
if (data.length !== n)
|
|
169
|
+
throw new Error('wrong length');
|
|
170
|
+
const res = [];
|
|
171
|
+
let buf = 0;
|
|
172
|
+
let bufLen = 0;
|
|
173
|
+
const writeBits = (n, v) => {
|
|
174
|
+
bufLen += n;
|
|
175
|
+
buf = (buf << n) | v;
|
|
176
|
+
// flush buffer if bigger than byte
|
|
177
|
+
for (; bufLen >= 8; buf &= getMask(bufLen)) {
|
|
178
|
+
bufLen -= 8;
|
|
179
|
+
res.push((buf >>> bufLen) & 0xff);
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
for (let i = 0; i < n; i++) {
|
|
183
|
+
let v = data[i];
|
|
184
|
+
if (!Number.isInteger(v) || v < -LIMIT || v > LIMIT)
|
|
185
|
+
throw new Error(`data[${i}]=${v} out of range`);
|
|
186
|
+
const sign = v < 0 ? 1 : 0;
|
|
187
|
+
v = Math.abs(v);
|
|
188
|
+
writeBits(1, sign);
|
|
189
|
+
writeBits(7, v & 0b0111_1111); // low
|
|
190
|
+
writeBits((v >>> 7) + 1, 1); // high (unary)
|
|
191
|
+
}
|
|
192
|
+
if (bufLen > 0)
|
|
193
|
+
res.push((buf << (8 - bufLen)) & 0xff);
|
|
194
|
+
return new Uint8Array(res);
|
|
195
|
+
},
|
|
196
|
+
decode(data) {
|
|
197
|
+
// Algorithm 18: Decompress(str, slen), (Page 48)
|
|
198
|
+
// Require: A bitstring str = (str[i])_{i=0,...,slen-1}, a bitlength slen
|
|
199
|
+
// Ensure: A polynomial s = Σ sᵢxⁱ ∈ Z[x], or ⊥
|
|
200
|
+
// 1: if |str| ≠ slen then ▷ Enforce fixed bitlength
|
|
201
|
+
// 2: return ⊥
|
|
202
|
+
// 3: for i from 0 to (n-1) do
|
|
203
|
+
// 4: s'ᵢ ← Σ_{j=0 to 6} 2⁶⁻ʲ · str[1 + j] ▷ We recover the lowest bits of |sᵢ|.
|
|
204
|
+
// 5: k ← 0
|
|
205
|
+
// 6: while str[8 + k] = 0 do ▷ We recover the highest bits of |sᵢ|.
|
|
206
|
+
// 7: k ← k + 1
|
|
207
|
+
// 8: sᵢ ← (-1)^{str[0]} · (s'ᵢ + 2⁷k) ▷ We recompute sᵢ.
|
|
208
|
+
// 9: if (sᵢ = 0) and (str[0] = 1) then ▷ Enforce unique encoding if sᵢ = 0
|
|
209
|
+
// 10: return ⊥
|
|
210
|
+
// 11: str ← str with first 9 + k bits removed ▷ We remove the bits of str that encode sᵢ.
|
|
211
|
+
// 12: if str contains any non-zero bits then ▷ Enforce trailing bits at 0
|
|
212
|
+
// 13: return ⊥
|
|
213
|
+
// 14: return s = Σ_{i=0}^{n-1} sᵢxⁱ
|
|
214
|
+
const res = new Int16Array(n);
|
|
215
|
+
let buf = 0;
|
|
216
|
+
let bufLen = 0;
|
|
217
|
+
let pos = 0;
|
|
218
|
+
const readBits = (n) => {
|
|
219
|
+
for (; bufLen < n && pos < data.length; bufLen += 8)
|
|
220
|
+
buf = (buf << 8) | data[pos++];
|
|
221
|
+
if (bufLen < n)
|
|
222
|
+
throw new Error(`end of buffer: len=${bufLen} buf=${buf} lastByte=${data[pos]}`);
|
|
223
|
+
bufLen -= n;
|
|
224
|
+
const val = buf >>> bufLen;
|
|
225
|
+
buf &= getMask(bufLen);
|
|
226
|
+
return val;
|
|
227
|
+
};
|
|
228
|
+
for (let resPos = 0; resPos < n; resPos++) {
|
|
229
|
+
const sign = readBits(1);
|
|
230
|
+
const low = readBits(7);
|
|
231
|
+
let high = 0;
|
|
232
|
+
for (; !readBits(1); high++)
|
|
233
|
+
;
|
|
234
|
+
const v = low | (high << 7);
|
|
235
|
+
if (sign && v === 0)
|
|
236
|
+
throw new Error('negative zero encoding');
|
|
237
|
+
if (v > LIMIT)
|
|
238
|
+
throw new Error(`limit: ${v} > ${LIMIT}`);
|
|
239
|
+
res[resPos] = sign ? -v : v;
|
|
240
|
+
}
|
|
241
|
+
if (buf)
|
|
242
|
+
throw new Error('non-empty accumulator');
|
|
243
|
+
return res;
|
|
244
|
+
},
|
|
245
|
+
};
|
|
246
|
+
};
|
|
247
|
+
// Falcon padded-signature helper. encode() assumes `data.length <= len`; decode() strips trailing
|
|
248
|
+
// zero padding and returns a subarray view, so it is not a generic byte-string codec.
|
|
249
|
+
const pad = (len) => ({
|
|
250
|
+
encode(data) {
|
|
251
|
+
const res = new Uint8Array(len);
|
|
252
|
+
res.set(data);
|
|
253
|
+
return res;
|
|
254
|
+
},
|
|
255
|
+
decode(data) {
|
|
256
|
+
let end = data.length;
|
|
257
|
+
while (end > 0 && data[end - 1] === 0)
|
|
258
|
+
end--;
|
|
259
|
+
return data.subarray(0, end);
|
|
260
|
+
},
|
|
261
|
+
});
|
|
262
|
+
// Zero complex-polynomial temporaries in place. Requires fully initialized `{ re, im }` entries.
|
|
263
|
+
const cleanCPoly = (...list) => {
|
|
264
|
+
for (const p of list) {
|
|
265
|
+
for (let i = 0; i < p.length; i++) {
|
|
266
|
+
p[i].re = 0;
|
|
267
|
+
p[i].im = 0;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
};
|
|
271
|
+
// Generic complex helper used by Falcon's FFT code. Current audited use relies on
|
|
272
|
+
// add/sub/mul/conj/scale/magSqSum/neg; inv() is intentionally unimplemented.
|
|
273
|
+
function getComplex(field) {
|
|
274
|
+
const F = field;
|
|
275
|
+
return {
|
|
276
|
+
lift: (x) => {
|
|
277
|
+
// Reuse existing complex objects verbatim; callers that need isolation must clone first.
|
|
278
|
+
if (x.re !== undefined && x.im !== undefined)
|
|
279
|
+
return x;
|
|
280
|
+
return { re: x, im: F.ZERO };
|
|
281
|
+
},
|
|
282
|
+
add: (a, b) => ({
|
|
283
|
+
re: F.add(a.re, b.re),
|
|
284
|
+
im: F.add(a.im, b.im),
|
|
285
|
+
}),
|
|
286
|
+
sub: (a, b) => ({
|
|
287
|
+
re: F.sub(a.re, b.re),
|
|
288
|
+
im: F.sub(a.im, b.im),
|
|
289
|
+
}),
|
|
290
|
+
mul: (a, b) => ({
|
|
291
|
+
re: F.sub(F.mul(a.re, b.re), F.mul(a.im, b.im)),
|
|
292
|
+
im: F.add(F.mul(a.re, b.im), F.mul(a.im, b.re)),
|
|
293
|
+
}),
|
|
294
|
+
div: (a, b) => {
|
|
295
|
+
const denom = F.add(F.mul(b.re, b.re), F.mul(b.im, b.im));
|
|
296
|
+
return {
|
|
297
|
+
re: F.div(F.add(F.mul(a.re, b.re), F.mul(a.im, b.im)), denom),
|
|
298
|
+
im: F.div(F.sub(F.mul(a.im, b.re), F.mul(a.re, b.im)), denom),
|
|
299
|
+
};
|
|
300
|
+
},
|
|
301
|
+
neg: (a) => ({ re: F.neg(a.re), im: F.neg(a.im) }),
|
|
302
|
+
conj: (a) => ({ re: a.re, im: F.neg(a.im) }),
|
|
303
|
+
scale: (a, x) => ({
|
|
304
|
+
re: F.mul(a.re, x),
|
|
305
|
+
im: F.mul(a.im, x),
|
|
306
|
+
}),
|
|
307
|
+
// a.re * a.re + a.im * a.im + b.re * b.re + b.im * b.im;
|
|
308
|
+
magSqSum: (a, b) => F.add(F.add(F.add(F.mul(a.re, a.re), F.mul(a.im, a.im)), F.mul(b.re, b.re)), F.mul(b.im, b.im)),
|
|
309
|
+
eql: (a, b) => F.eql(a.re, b.re) && F.eql(a.im, b.im),
|
|
310
|
+
clone: (a) => ({ re: a.re, im: a.im }),
|
|
311
|
+
inv: () => {
|
|
312
|
+
throw new Error('not implemented');
|
|
313
|
+
},
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
// Falcon real-polynomial FFT layout: [...re, ...im]. Requires an even-length flat array and
|
|
317
|
+
// copies into fresh JS objects / arrays instead of creating views.
|
|
318
|
+
const ComplexArr = {
|
|
319
|
+
decode(lst) {
|
|
320
|
+
const N = lst.length;
|
|
321
|
+
const hn = N >> 1;
|
|
322
|
+
const len = lst.length;
|
|
323
|
+
if (len === 0)
|
|
324
|
+
return [];
|
|
325
|
+
if (len % 2 !== 0)
|
|
326
|
+
throw new Error('Array length must be even to pair real and imaginary parts.');
|
|
327
|
+
const res = [];
|
|
328
|
+
for (let i = 0; i < hn; i++) {
|
|
329
|
+
res.push({ re: lst[i], im: lst[i + hn] });
|
|
330
|
+
}
|
|
331
|
+
return res;
|
|
332
|
+
},
|
|
333
|
+
encode(lst) {
|
|
334
|
+
const re = [];
|
|
335
|
+
const im = [];
|
|
336
|
+
for (const i of lst) {
|
|
337
|
+
re.push(i.re);
|
|
338
|
+
im.push(i.im);
|
|
339
|
+
}
|
|
340
|
+
return [...re, ...im];
|
|
341
|
+
},
|
|
342
|
+
};
|
|
343
|
+
// Precomputed root-table layout [re[0], im[0], re[1], im[1], ...]. Used for `COMPLEX_ROOTS`,
|
|
344
|
+
// not Falcon's packed polynomial FFT layout; encode() is currently unused.
|
|
345
|
+
// decode() / encode() copy between the flat root table
|
|
346
|
+
// and detached `{ re, im }` objects; they never create aliasing views.
|
|
347
|
+
const ComplexArrInterleaved = {
|
|
348
|
+
decode(lst) {
|
|
349
|
+
const len = lst.length;
|
|
350
|
+
if (len === 0)
|
|
351
|
+
return [];
|
|
352
|
+
if (len % 2 !== 0)
|
|
353
|
+
throw new Error('Array length must be even to pair real and imaginary parts.');
|
|
354
|
+
const res = [];
|
|
355
|
+
// Iterate through the list, taking two elements at a time
|
|
356
|
+
for (let i = 0; i < len; i += 2) {
|
|
357
|
+
res.push({ re: lst[i], im: lst[i + 1] });
|
|
358
|
+
}
|
|
359
|
+
return res;
|
|
360
|
+
},
|
|
361
|
+
encode(lst) {
|
|
362
|
+
const res = [];
|
|
363
|
+
for (const complexNum of lst) {
|
|
364
|
+
res.push(complexNum.re);
|
|
365
|
+
res.push(complexNum.im);
|
|
366
|
+
}
|
|
367
|
+
return res;
|
|
368
|
+
},
|
|
369
|
+
};
|
|
370
|
+
// Alias a Float64Array as bytes for the root-table hash pin; not a portable serialization.
|
|
371
|
+
const u8f = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
372
|
+
// Alias bytes as Float64Array lanes. Falcon's exact binary64 tables are stored as little-endian
|
|
373
|
+
// payload bytes, so BE runtimes must decode lane-by-lane instead of aliasing host-endian floats.
|
|
374
|
+
// Copy/truncate to whole 8-byte lanes first
|
|
375
|
+
// so BE byte swaps cannot mutate caller-owned bytes
|
|
376
|
+
// or read a partial float.
|
|
377
|
+
const f64a = (arr) => new Float64Array(baswap64If(Uint8Array.from(arr.subarray(0, Math.floor(arr.byteLength / 8) * 8))).buffer);
|
|
378
|
+
// Exact big-endian binary64 hex helper for constants. Only decode() is currently used; malformed
|
|
379
|
+
// inputs fail through lower-level hex / DataView checks instead of an explicit wrapper guard.
|
|
380
|
+
const Float = {
|
|
381
|
+
encode(n) {
|
|
382
|
+
const bytes = new Uint8Array(8);
|
|
383
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
384
|
+
view.setFloat64(0, n, false);
|
|
385
|
+
return bytesToHex(bytes);
|
|
386
|
+
},
|
|
387
|
+
decode(s) {
|
|
388
|
+
const bytes = hexToBytes(s);
|
|
389
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
390
|
+
return view.getFloat64(0, false);
|
|
391
|
+
},
|
|
392
|
+
};
|
|
393
|
+
// Decode a 64-bit bigint bit pattern into the exact binary64 value.
|
|
394
|
+
const f64b = (n) => Float.decode(numberToHexUnpadded(n));
|
|
395
|
+
// Constants
|
|
396
|
+
const EMPTY_CHACHA20_BLOCK = /** @__PURE__ */ new Uint8Array(64);
|
|
397
|
+
// Falcon's randomized hashing salt r is always 320 bits / 40 bytes, and the same width
|
|
398
|
+
// also drives the detached and attached signature wire formats.
|
|
399
|
+
const NONCELEN = 40;
|
|
400
|
+
// Falcon's public modulus q = 12289 is also the NTT parameter chosen in round 3.
|
|
401
|
+
const Q = 12289; // 12 * 1024 + 1
|
|
402
|
+
// Falcon's midpoint floor(q/2); the only live use is the mirrored G-reconstruction reduction below.
|
|
403
|
+
const Qhalf = Q >> 1;
|
|
404
|
+
const QBig = BigInt(Q);
|
|
405
|
+
//const R = 4091; // 2^16 mod q
|
|
406
|
+
// This 16-bit Montgomery kernel uses R = 2^16, so mul(x, R2) converts x into Montgomery form.
|
|
407
|
+
const R2 = 10952; // 2^32 mod q
|
|
408
|
+
// falcon.pdf page 55 says "1/q mod 2^16",
|
|
409
|
+
// but the reduction formula and the round-3 Falcon code both require -1/q.
|
|
410
|
+
const Q0I = 12287; // -1/q mod 2^16
|
|
411
|
+
const F_INV_Q = 1.0 / Q;
|
|
412
|
+
const F_MINUS_INV_Q = -F_INV_Q;
|
|
413
|
+
// Round-3 bigint keygen keeps these tables coupled: MAX_BL_SMALL and MAX_BL_LARGE are measured
|
|
414
|
+
// 31-bit word bounds, and BITLENGTH is the measured avg/stddev heuristic. Edits must recheck the
|
|
415
|
+
// next-depth relation and the current 31 * wordCount headroom used by reduce().
|
|
416
|
+
const MAX_BL_SMALL = [1, 1, 2, 2, 4, 7, 14, 27, 53, 106, 209];
|
|
417
|
+
// Unreduced F/G word bounds for reduce(); the same round-3 source also couples this table to the
|
|
418
|
+
// top-10-word floating approximation there,
|
|
419
|
+
// so it must stay in sync with MAX_BL_SMALL and BITLENGTH.
|
|
420
|
+
const MAX_BL_LARGE = [2, 2, 5, 7, 12, 21, 40, 78, 157, 308];
|
|
421
|
+
// Exact binary64 encoding of Falcon's Gram-Schmidt keygen bound (1.17^2) * q = 16822.4121.
|
|
422
|
+
const BNORM_MAX = f64b(BigInt('4670353323383631276'));
|
|
423
|
+
// Measured round-3 bigint-keygen heuristic, not a normative Falcon parameter table or proof bound.
|
|
424
|
+
const BITLENGTH = [
|
|
425
|
+
{ avg: 4, std: 0 },
|
|
426
|
+
{ avg: 11, std: 1 },
|
|
427
|
+
{ avg: 24, std: 1 },
|
|
428
|
+
{ avg: 50, std: 1 },
|
|
429
|
+
{ avg: 102, std: 1 },
|
|
430
|
+
{ avg: 202, std: 2 },
|
|
431
|
+
{ avg: 401, std: 4 },
|
|
432
|
+
{ avg: 794, std: 5 },
|
|
433
|
+
{ avg: 1577, std: 8 },
|
|
434
|
+
{ avg: 3138, std: 13 },
|
|
435
|
+
{ avg: 6308, std: 25 },
|
|
436
|
+
];
|
|
437
|
+
// First entry is P(x = 0); the remaining entries are conditional tail thresholds scaled by 2^63.
|
|
438
|
+
// Smaller Falcon dimensions reuse the N = 1024, q = 12289 table by summing 2^(10-logn) draws.
|
|
439
|
+
// The trailing 0 sentinel guarantees gaussSingle()
|
|
440
|
+
// always selects a tail bucket when x = 0 is missed.
|
|
441
|
+
const gauss_1024_12289 = [
|
|
442
|
+
1283868770400643928n,
|
|
443
|
+
6416574995475331444n,
|
|
444
|
+
4078260278032692663n,
|
|
445
|
+
2353523259288686585n,
|
|
446
|
+
1227179971273316331n,
|
|
447
|
+
575931623374121527n,
|
|
448
|
+
242543240509105209n,
|
|
449
|
+
91437049221049666n,
|
|
450
|
+
30799446349977173n,
|
|
451
|
+
9255276791179340n,
|
|
452
|
+
2478152334826140n,
|
|
453
|
+
590642893610164n,
|
|
454
|
+
125206034929641n,
|
|
455
|
+
23590435911403n,
|
|
456
|
+
3948334035941n,
|
|
457
|
+
586753615614n,
|
|
458
|
+
77391054539n,
|
|
459
|
+
9056793210n,
|
|
460
|
+
940121950n,
|
|
461
|
+
86539696n,
|
|
462
|
+
7062824n,
|
|
463
|
+
510971n,
|
|
464
|
+
32764n,
|
|
465
|
+
1862n,
|
|
466
|
+
94n,
|
|
467
|
+
4n,
|
|
468
|
+
0n,
|
|
469
|
+
];
|
|
470
|
+
// Exact binary64 1/sigma payloads from round-3 fpr.h. Nearby decimal spellings round 1 ULP low in
|
|
471
|
+
// JS, so keep these as decoded bit patterns and recheck the raw payloads after edits.
|
|
472
|
+
const INV_SIGMA = [
|
|
473
|
+
0.0, // unused
|
|
474
|
+
f64b(BigInt('4574611497772390042')),
|
|
475
|
+
f64b(BigInt('4574501679055810265')),
|
|
476
|
+
f64b(BigInt('4574396282908341804')),
|
|
477
|
+
f64b(BigInt('4574245855758572086')),
|
|
478
|
+
f64b(BigInt('4574103865040221165')),
|
|
479
|
+
f64b(BigInt('4573969550563515544')),
|
|
480
|
+
f64b(BigInt('4573842244705920822')),
|
|
481
|
+
f64b(BigInt('4573721358406441454')),
|
|
482
|
+
f64b(BigInt('4573606369665796042')),
|
|
483
|
+
f64b(BigInt('4573496814039276259')),
|
|
484
|
+
];
|
|
485
|
+
// Exact binary64 sigma_min constants from round-3 fpr.h indexed by logn; despite one PQClean
|
|
486
|
+
// summary comment, these are sigma_min itself, not 1/sigma_min, which is why this table stays
|
|
487
|
+
// separate from INV_SIGMA.
|
|
488
|
+
const SIGMA_MIN = [
|
|
489
|
+
0.0, // unused
|
|
490
|
+
f64b(BigInt('4607707126469777035')),
|
|
491
|
+
f64b(BigInt('4607777455861499430')),
|
|
492
|
+
f64b(BigInt('4607846828256951418')),
|
|
493
|
+
f64b(BigInt('4607949175006100261')),
|
|
494
|
+
f64b(BigInt('4608049571757433526')),
|
|
495
|
+
f64b(BigInt('4608148125896792003')),
|
|
496
|
+
f64b(BigInt('4608244935301382692')),
|
|
497
|
+
f64b(BigInt('4608340089478362016')),
|
|
498
|
+
f64b(BigInt('4608433670533905013')),
|
|
499
|
+
f64b(BigInt('4608525754002622308')),
|
|
500
|
+
];
|
|
501
|
+
// Falcon Table 3.1 RCDT values for chi, split into 24-bit limbs; storage is [high, mid, low],
|
|
502
|
+
// so gaussian0() intentionally compares them against v0, v1, v2 in reverse order. The final
|
|
503
|
+
// RCDT[18] = 0 row is omitted because the algorithm iterates only over i = 0..17.
|
|
504
|
+
const GAUSS0 = new Uint32Array([
|
|
505
|
+
10745844, 3068844, 3741698, 5559083, 1580863, 8248194, 2260429, 13669192, 2736639, 708981,
|
|
506
|
+
4421575, 10046180, 169348, 7122675, 4136815, 30538, 13063405, 7650655, 4132, 14505003, 7826148,
|
|
507
|
+
417, 16768101, 11363290, 31, 8444042, 8086568, 1, 12844466, 265321, 0, 1232676, 13644283, 0,
|
|
508
|
+
38047, 9111839, 0, 870, 6138264, 0, 14, 12545723, 0, 0, 3104126, 0, 0, 28824, 0, 0, 198, 0, 0, 1,
|
|
509
|
+
]);
|
|
510
|
+
// Inclusive floor(beta^2) signature-acceptance bounds indexed by logn; the Falcon PDF publishes
|
|
511
|
+
// only Falcon-512 and Falcon-1024 directly, while the smaller rows are mirrored from the round-3
|
|
512
|
+
// NIST submission package.
|
|
513
|
+
const L2BOUND = [
|
|
514
|
+
0, // unused
|
|
515
|
+
101498,
|
|
516
|
+
208714,
|
|
517
|
+
428865,
|
|
518
|
+
892039,
|
|
519
|
+
1852696,
|
|
520
|
+
3842630,
|
|
521
|
+
7959734,
|
|
522
|
+
16468416,
|
|
523
|
+
34034726,
|
|
524
|
+
70265242,
|
|
525
|
+
];
|
|
526
|
+
// 32kb in hex.
|
|
527
|
+
// Could be 4x smaller by using 2 bytes per root. However, that would mean using sin/cos.
|
|
528
|
+
// Different JS engines give different sin / cos result, which means the result would be unreliable.
|
|
529
|
+
// See "COMPLEX ROOT GENERATION FOR FALCON" in tests.
|
|
530
|
+
// Those exact roots are taken from the round-3 Falcon submission, preserving its original
|
|
531
|
+
// bit-reversed order here and remapping it only later for FFTCore.
|
|
532
|
+
const COMPLEX_ROOTS = /** @__PURE__ */ (() => {
|
|
533
|
+
const roots = f64a(hexToBytes('000000000000000000000000000000000000000000000080000000000000f03fcd3b7f669ea0e63fcd3b7f66' +
|
|
534
|
+
'9ea0e63fcd3b7f669ea0e6bfcd3b7f669ea0e63f468d32cf6b90ed3f63a9aea6e27dd83f63a9aea6e27dd8bf' +
|
|
535
|
+
'468d32cf6b90ed3f63a9aea6e27dd83f468d32cf6b90ed3f468d32cf6b90edbf63a9aea6e27dd83fb05cf7cf' +
|
|
536
|
+
'9762ef3f0ba6693cb8f8c83f0ba6693cb8f8c8bfb05cf7cf9762ef3fc868ae393bc7e13fa3a10e29669bea3f' +
|
|
537
|
+
'a3a10e29669beabfc868ae393bc7e13fa3a10e29669bea3fc868ae393bc7e13fc868ae393bc7e1bfa3a10e29' +
|
|
538
|
+
'669bea3f0ba6693cb8f8c83fb05cf7cf9762ef3fb05cf7cf9762efbf0ba6693cb8f8c83f2625d1a38dd8ef3f' +
|
|
539
|
+
'2cb429bca617b93f2cb429bca617b9bf2625d1a38dd8ef3fd61d0925f34ce43f4117156b80bce83f4117156b' +
|
|
540
|
+
'80bce8bfd61d0925f34ce43fb1bd80f1b238ec3f3bf606385d2bde3f3bf606385d2bdebfb1bd80f1b238ec3f' +
|
|
541
|
+
'069fd52e0694d23fda2dc656419fee3fda2dc656419feebf069fd52e0694d23fda2dc656419fee3f069fd52e' +
|
|
542
|
+
'0694d23f069fd52e0694d2bfda2dc656419fee3f3bf606385d2bde3fb1bd80f1b238ec3fb1bd80f1b238ecbf' +
|
|
543
|
+
'3bf606385d2bde3f4117156b80bce83fd61d0925f34ce43fd61d0925f34ce4bf4117156b80bce83f2cb429bc' +
|
|
544
|
+
'a617b93f2625d1a38dd8ef3f2625d1a38dd8efbf2cb429bca617b93f7e6d79e321f6ef3f14d80df1651fa93f' +
|
|
545
|
+
'14d80df1651fa9bf7e6d79e321f6ef3fa0ec8c34697de53fafaf6a22dfb5e73fafaf6a22dfb5e7bfa0ec8c34' +
|
|
546
|
+
'697de53f73c73cf47aedec3fc05ce109105ddb3fc05ce109105ddbbf73c73cf47aedec3fdd1fab759a8fd53f' +
|
|
547
|
+
'e586f6042121ee3fe586f6042121eebfdd1fab759a8fd53fd73092fb7e0aef3f1b5f217bf919cf3f1b5f217b' +
|
|
548
|
+
'f919cfbfd73092fb7e0aef3feeff22998773e03f3e6e19458372eb3f3e6e19458372ebbfeeff22998773e03f' +
|
|
549
|
+
'4187f347e0b3e93f3570e1fcf70fe33f3570e1fcf70fe3bf4187f347e0b3e93f3a618e6e10c8c23f17a5087f' +
|
|
550
|
+
'55a7ef3f17a5087f55a7efbf3a618e6e10c8c23f17a5087f55a7ef3f3a618e6e10c8c23f3a618e6e10c8c2bf' +
|
|
551
|
+
'17a5087f55a7ef3f3570e1fcf70fe33f4187f347e0b3e93f4187f347e0b3e9bf3570e1fcf70fe33f3e6e1945' +
|
|
552
|
+
'8372eb3feeff22998773e03feeff22998773e0bf3e6e19458372eb3f1b5f217bf919cf3fd73092fb7e0aef3f' +
|
|
553
|
+
'd73092fb7e0aefbf1b5f217bf919cf3fe586f6042121ee3fdd1fab759a8fd53fdd1fab759a8fd5bfe586f604' +
|
|
554
|
+
'2121ee3fc05ce109105ddb3f73c73cf47aedec3f73c73cf47aedecbfc05ce109105ddb3fafaf6a22dfb5e73f' +
|
|
555
|
+
'a0ec8c34697de53fa0ec8c34697de5bfafaf6a22dfb5e73f14d80df1651fa93f7e6d79e321f6ef3f7e6d79e3' +
|
|
556
|
+
'21f6efbf14d80df1651fa93f0dcd846088fdef3f7e66a3f75521993f7e66a3f7552199bf0dcd846088fdef3f' +
|
|
557
|
+
'df2c1d55b710e63f96ffef37082de73f96ffef37082de7bfdf2c1d55b710e63f3ac94dd13441ed3f8aeda843' +
|
|
558
|
+
'79efd93f8aeda84379efd9bf3ac94dd13441ed3f9f45fa308508d73f3cc2ccb613dbed3f3cc2ccb613dbedbf' +
|
|
559
|
+
'9f45fa308508d73f89e564acf338ef3f634f7e6a820bcc3f634f7e6a820bccbf89e564acf338ef3f234b1b54' +
|
|
560
|
+
'b31ee13f000215580a09eb3f000215580a09ebbf234b1b54b31ee13f822746a0a729ea3fdf12dd4c056de23f' +
|
|
561
|
+
'df12dd4c056de2bf822746a0a729ea3fc63f8b4414e2c53fa94b71fa6487ef3fa94b71fa6487efbfc63f8b44' +
|
|
562
|
+
'14e2c53fd39fe17064c2ef3f0e73a9564e56bf3f0e73a9564e56bfbfd39fe17064c2ef3fb9502029faafe33f' +
|
|
563
|
+
'fb639249223ae93ffb639249223ae9bfb9502029faafe33f2a956facc0d7eb3fba9af8dba48bdf3fba9af8db' +
|
|
564
|
+
'a48bdfbf2a956facc0d7eb3f77f6b162d211d13f634968e740d7ee3f634968e740d7eebf77f6b162d211d13f' +
|
|
565
|
+
'12e148ec8862ee3f016617945c13d43f016617945c13d4bf12e148ec8862ee3f5ec431996ec6dc3ff5113421' +
|
|
566
|
+
'4b95ec3ff51134214b95ecbf5ec431996ec6dc3f6e97ff0b0e3be83fe9e5e3bbcae6e43fe9e5e3bbcae6e4bf' +
|
|
567
|
+
'6e97ff0b0e3be83ff619ce9220d5b23f3a8801adcde9ef3f3a8801adcde9efbff619ce9220d5b23f3a8801ad' +
|
|
568
|
+
'cde9ef3ff619ce9220d5b23ff619ce9220d5b2bf3a8801adcde9ef3fe9e5e3bbcae6e43f6e97ff0b0e3be83f' +
|
|
569
|
+
'6e97ff0b0e3be8bfe9e5e3bbcae6e43ff51134214b95ec3f5ec431996ec6dc3f5ec431996ec6dcbff5113421' +
|
|
570
|
+
'4b95ec3f016617945c13d43f12e148ec8862ee3f12e148ec8862eebf016617945c13d43f634968e740d7ee3f' +
|
|
571
|
+
'77f6b162d211d13f77f6b162d211d1bf634968e740d7ee3fba9af8dba48bdf3f2a956facc0d7eb3f2a956fac' +
|
|
572
|
+
'c0d7ebbfba9af8dba48bdf3ffb639249223ae93fb9502029faafe33fb9502029faafe3bffb639249223ae93f' +
|
|
573
|
+
'0e73a9564e56bf3fd39fe17064c2ef3fd39fe17064c2efbf0e73a9564e56bf3fa94b71fa6487ef3fc63f8b44' +
|
|
574
|
+
'14e2c53fc63f8b4414e2c5bfa94b71fa6487ef3fdf12dd4c056de23f822746a0a729ea3f822746a0a729eabf' +
|
|
575
|
+
'df12dd4c056de23f000215580a09eb3f234b1b54b31ee13f234b1b54b31ee1bf000215580a09eb3f634f7e6a' +
|
|
576
|
+
'820bcc3f89e564acf338ef3f89e564acf338efbf634f7e6a820bcc3f3cc2ccb613dbed3f9f45fa308508d73f' +
|
|
577
|
+
'9f45fa308508d7bf3cc2ccb613dbed3f8aeda84379efd93f3ac94dd13441ed3f3ac94dd13441edbf8aeda843' +
|
|
578
|
+
'79efd93f96ffef37082de73fdf2c1d55b710e63fdf2c1d55b710e6bf96ffef37082de73f7e66a3f75521993f' +
|
|
579
|
+
'0dcd846088fdef3f0dcd846088fdefbf7e66a3f75521993fdb929b1662ffef3f84c7defcd121893f84c7defc' +
|
|
580
|
+
'd12189bfdb929b1662ffef3f3d78f0251959e63fafa8ea5444e7e63fafa8ea5444e7e6bf3d78f0251959e63f' +
|
|
581
|
+
'8be6c9736169ed3fd793bc632a37d93fd793bc632a37d9bf8be6c9736169ed3fe7cc1d31a9c3d73f9ba03862' +
|
|
582
|
+
'52b6ed3f9ba0386252b6edbfe7cc1d31a9c3d73f2d2f0b3b604eef3f5104b025a082ca3f5104b025a082cabf' +
|
|
583
|
+
'2d2f0b3b604eef3f49dbde634d73e13f11d5219ebcd2ea3f11d5219ebcd2eabf49dbde634d73e13fe2fa021b' +
|
|
584
|
+
'0963ea3f59eb3399791ae23f59eb3399791ae2bfe2fa021b0963ea3f31bf50ded96dc73f7720a1a39975ef3f' +
|
|
585
|
+
'7720a1a39975efbf31bf50ded96dc73f7ba66dfd15ceef3fd5c29ec78537bc3fd5c29ec78537bcbf7ba66dfd' +
|
|
586
|
+
'15ceef3fd4564553d9fee33f0d94efa3ccfbe83f0d94efa3ccfbe8bfd4564553d9fee33f49557226c408ec3f' +
|
|
587
|
+
'd678ef5219dcde3fd678ef5219dcdebf49557226c408ec3f3edb4c3f44d3d13f740bdfc8d8bbee3f740bdfc8' +
|
|
588
|
+
'd8bbeebf3edb4c3f44d3d13f0dd14cab7b81ee3f5281e1c21054d33f5281e1c21054d3bf0dd14cab7b81ee3f' +
|
|
589
|
+
'89e3865b7779dd3f9b7388348b67ec3f9b7388348b67ecbf89e3865b7779dd3fbf2eba0f407ce83f39099b9b' +
|
|
590
|
+
'449ae43f39099b9b449ae4bfbf2eba0f407ce83f19a49a0ad0f6b53f095bbdfccae1ef3f095bbdfccae1efbf' +
|
|
591
|
+
'19a49a0ad0f6b53fad718e6595f0ef3fe020f8796e65af3fe020f8796e65afbfad718e6595f0ef3f9655a392' +
|
|
592
|
+
'8232e53f711757e3ecf8e73f711757e3ecf8e7bf9655a3928232e53f5cfcfcf3f0c1ec3fe71e01d84912dc3f' +
|
|
593
|
+
'e71e01d84912dcbf5cfcfcf3f0c1ec3f6ae77842e2d1d43f7ec12b4b6a42ee3f7ec12b4b6a42eebf6ae77842' +
|
|
594
|
+
'e2d1d43fc273e4a378f1ee3faefd370eb84fd03faefd370eb84fd0bfc273e4a378f1ee3fb73e4c87fc1ce03f' +
|
|
595
|
+
'd2903567aaa5eb3fd2903567aaa5ebbfb73e4c87fc1ce03f42d7c7f47e77e93ff35906b15860e33ff35906b1' +
|
|
596
|
+
'5860e3bf42d7c7f47e77e93f77f5dacef039c13f41d7957179b5ef3f41d7957179b5efbf77f5dacef039c13f' +
|
|
597
|
+
'9b09c924f997ef3f5a3e29b17655c43f5a3e29b17655c4bf9b09c924f997ef3feaf3fa25dbbee23f94af29ef' +
|
|
598
|
+
'43efe93f94af29ef43efe9bfeaf3fa25dbbee23f1257f53e4d3eeb3f8f895d4d70c9e03f8f895d4d70c9e0bf' +
|
|
599
|
+
'1257f53e4d3eeb3f114345e54f93cd3fda3a76f75222ef3fda3a76f75222efbf114345e54f93cd3f2bbe2d62' +
|
|
600
|
+
'aefeed3fc6273fdd7d4cd63fc6273fdd7d4cd6bf2bbe2d62aefeed3fca3f6d2bc8a6da3fdc353e74e717ed3f' +
|
|
601
|
+
'dc353e74e717edbfca3f6d2bc8a6da3f6172035fe771e73f8c0165be7bc7e53f8c0165be7bc7e5bf6172035f' +
|
|
602
|
+
'e771e73fcd55947565d8a23f5df7feef72faef3f5df7feef72faefbfcd55947565d8a23f5df7feef72faef3f' +
|
|
603
|
+
'cd55947565d8a23fcd55947565d8a2bf5df7feef72faef3f8c0165be7bc7e53f6172035fe771e73f6172035f' +
|
|
604
|
+
'e771e7bf8c0165be7bc7e53fdc353e74e717ed3fca3f6d2bc8a6da3fca3f6d2bc8a6dabfdc353e74e717ed3f' +
|
|
605
|
+
'c6273fdd7d4cd63f2bbe2d62aefeed3f2bbe2d62aefeedbfc6273fdd7d4cd63fda3a76f75222ef3f114345e5' +
|
|
606
|
+
'4f93cd3f114345e54f93cdbfda3a76f75222ef3f8f895d4d70c9e03f1257f53e4d3eeb3f1257f53e4d3eebbf' +
|
|
607
|
+
'8f895d4d70c9e03f94af29ef43efe93feaf3fa25dbbee23feaf3fa25dbbee2bf94af29ef43efe93f5a3e29b1' +
|
|
608
|
+
'7655c43f9b09c924f997ef3f9b09c924f997efbf5a3e29b17655c43f41d7957179b5ef3f77f5dacef039c13f' +
|
|
609
|
+
'77f5dacef039c1bf41d7957179b5ef3ff35906b15860e33f42d7c7f47e77e93f42d7c7f47e77e9bff35906b1' +
|
|
610
|
+
'5860e33fd2903567aaa5eb3fb73e4c87fc1ce03fb73e4c87fc1ce0bfd2903567aaa5eb3faefd370eb84fd03f' +
|
|
611
|
+
'c273e4a378f1ee3fc273e4a378f1eebfaefd370eb84fd03f7ec12b4b6a42ee3f6ae77842e2d1d43f6ae77842' +
|
|
612
|
+
'e2d1d4bf7ec12b4b6a42ee3fe71e01d84912dc3f5cfcfcf3f0c1ec3f5cfcfcf3f0c1ecbfe71e01d84912dc3f' +
|
|
613
|
+
'711757e3ecf8e73f9655a3928232e53f9655a3928232e5bf711757e3ecf8e73fe020f8796e65af3fad718e65' +
|
|
614
|
+
'95f0ef3fad718e6595f0efbfe020f8796e65af3f095bbdfccae1ef3f19a49a0ad0f6b53f19a49a0ad0f6b5bf' +
|
|
615
|
+
'095bbdfccae1ef3f39099b9b449ae43fbf2eba0f407ce83fbf2eba0f407ce8bf39099b9b449ae43f9b738834' +
|
|
616
|
+
'8b67ec3f89e3865b7779dd3f89e3865b7779ddbf9b7388348b67ec3f5281e1c21054d33f0dd14cab7b81ee3f' +
|
|
617
|
+
'0dd14cab7b81eebf5281e1c21054d33f740bdfc8d8bbee3f3edb4c3f44d3d13f3edb4c3f44d3d1bf740bdfc8' +
|
|
618
|
+
'd8bbee3fd678ef5219dcde3f49557226c408ec3f49557226c408ecbfd678ef5219dcde3f0d94efa3ccfbe83f' +
|
|
619
|
+
'd4564553d9fee33fd4564553d9fee3bf0d94efa3ccfbe83fd5c29ec78537bc3f7ba66dfd15ceef3f7ba66dfd' +
|
|
620
|
+
'15ceefbfd5c29ec78537bc3f7720a1a39975ef3f31bf50ded96dc73f31bf50ded96dc7bf7720a1a39975ef3f' +
|
|
621
|
+
'59eb3399791ae23fe2fa021b0963ea3fe2fa021b0963eabf59eb3399791ae23f11d5219ebcd2ea3f49dbde63' +
|
|
622
|
+
'4d73e13f49dbde634d73e1bf11d5219ebcd2ea3f5104b025a082ca3f2d2f0b3b604eef3f2d2f0b3b604eefbf' +
|
|
623
|
+
'5104b025a082ca3f9ba0386252b6ed3fe7cc1d31a9c3d73fe7cc1d31a9c3d7bf9ba0386252b6ed3fd793bc63' +
|
|
624
|
+
'2a37d93f8be6c9736169ed3f8be6c9736169edbfd793bc632a37d93fafa8ea5444e7e63f3d78f0251959e63f' +
|
|
625
|
+
'3d78f0251959e6bfafa8ea5444e7e63f84c7defcd121893fdb929b1662ffef3fdb929b1662ffefbf84c7defc' +
|
|
626
|
+
'd121893f928a8e85d8ffef3f710067fef021793f710067fef02179bf928a8e85d8ffef3f10af9184f77ce63f' +
|
|
627
|
+
'7582c1730dc4e63f7582c1730dc4e6bf10af9184f77ce63ff9ecb8020b7ded3fb0a4c82ea5dad83fb0a4c82e' +
|
|
628
|
+
'a5dad8bff9ecb8020b7ded3fc4aa4eb0e320d83f888966a983a3ed3f888966a983a3edbfc4aa4eb0e320d83f' +
|
|
629
|
+
'849e78b1a258ef3f6643dcf2cbbdc93f6643dcf2cbbdc9bf849e78b1a258ef3fb8b9f2095a9de13fd4c01659' +
|
|
630
|
+
'32b7ea3fd4c0165932b7eabfb8b9f2095a9de13f9de69f52587fea3f1b86bc8bf0f0e13f1b86bc8bf0f0e1bf' +
|
|
631
|
+
'9de69f52587fea3fc6649ce86633c83fb7bbf57d3f6cef3fb7bbf57d3f6cefbfc6649ce86633c83f840b2214' +
|
|
632
|
+
'79d3ef3f035c4924b7a7ba3f035c4924b7a7babf840b221479d3ef3fb16b8e17ff25e43fcc98163345dce83f' +
|
|
633
|
+
'cc98163345dce8bfb16b8e17ff25e43fb071a93fde20ec3f1451f8eae083de3f1451f8eae083debfb071a93f' +
|
|
634
|
+
'de20ec3f71bbc3abbb33d23f8ea8e7e8b2adee3f8ea8e7e8b2adeebf71bbc3abbb33d23ff2f71d368490ee3f' +
|
|
635
|
+
'8703ecda22f4d23f8703ecda22f4d2bff2f71d368490ee3f58cc81148fd2dd3f07692b014250ec3f07692b01' +
|
|
636
|
+
'4250ecbf58cc81148fd2dd3faad44d9a7e9ce83f4773981bb573e43f4773981bb573e4bfaad44d9a7e9ce83f' +
|
|
637
|
+
'215b5d6a5887b73f56f4f19f53ddef3f56f4f19f53ddefbf215b5d6a5887b73f5c578d0f83f3ef3fe3d7c012' +
|
|
638
|
+
'8d42ac3fe3d7c0128d42acbf5c578d0f83f3ef3f375197381058e53fb23dc36c83d7e73fb23dc36c83d7e7bf' +
|
|
639
|
+
'375197381058e53ff6328b89d9d7ec3f01bd0423cfb7db3f01bd0423cfb7dbbff6328b89d9d7ec3f243caf80' +
|
|
640
|
+
'd830d53f25ce70e8ea31ee3f25ce70e8ea31eebf243caf80d830d53fec950b0c22feee3ff9eddf1adcdccf3f' +
|
|
641
|
+
'f9eddf1adcdccfbfec950b0c22feee3f1a22ae265648e03fe90475d2388ceb3fe90475d2388cebbf1a22ae26' +
|
|
642
|
+
'5648e03f220dd82ecf95e93f578e0c0d4038e33f578e0c0d4038e3bf220dd82ecf95e93fcf7becd41601c23f' +
|
|
643
|
+
'bbcf468e8eaeef3fbbcf468e8eaeefbfcf7becd41601c23fc8b2ad55ce9fef3f148dcdb0db8ec33f148dcdb0' +
|
|
644
|
+
'db8ec3bfc8b2ad55ce9fef3f17eae8e380e7e23fd580eaf5b1d1e93fd580eaf5b1d1e9bf17eae8e380e7e23f' +
|
|
645
|
+
'051492fe8958eb3fe1c51774909ee03fe1c51774909ee0bf051492fe8958eb3f1b1a101eca56ce3f5d20f753' +
|
|
646
|
+
'8f16ef3f5d20f7538f16efbf1b1a101eca56ce3fac8029ca0c10ee3f93a69e3727eed53f93a69e3727eed5bf' +
|
|
647
|
+
'ac8029ca0c10ee3f09407f6c0d02db3f92bdb2fed402ed3f92bdb2fed402edbf09407f6c0d02db3fe5554f57' +
|
|
648
|
+
'0094e73f50725d2a8da2e53f50725d2a8da2e5bfe5554f570094e73f43cd90d200fca53fdf81dbda71f8ef3f' +
|
|
649
|
+
'df81dbda71f8efbf43cd90d200fca53ff8d3f11d25fcef3f01cfd13137699f3f01cfd13137699fbff8d3f11d' +
|
|
650
|
+
'25fcef3f7470839534ece53f8dd2a88d944fe73f8dd2a88d944fe7bf7470839534ece53f9fefe020b22ced3f' +
|
|
651
|
+
'e5a1de27414bda3fe5a1de27414bdabf9fefe020b22ced3f177ec77d9daad63fda47def705eded3fda47def7' +
|
|
652
|
+
'05ededbf177ec77d9daad63f9d9a08c9c92def3f86b212b38ccfcc3f86b212b38ccfccbf9d9a08c9c92def3f' +
|
|
653
|
+
'7e8e2abb26f4e03fb4130047cd23eb3fb4130047cd23ebbf7e8e2abb26f4e03f37f9baea950cea3fa89c6227' +
|
|
654
|
+
'0796e23fa89c62270796e2bf37f9baea950cea3ff2c59785df1bc53fdb41aeffd58fef3fdb41aeffd58fefbf' +
|
|
655
|
+
'f2c59785df1bc53f8641e41716bcef3f1d83ba47a072c03f1d83ba47a072c0bf8641e41716bcef3f22ebdf85' +
|
|
656
|
+
'4188e33fd76d8ee4ef58e93fd76d8ee4ef58e9bf22ebdf854188e33fea8093c4d7beeb3f1012e74bf6e2df3f' +
|
|
657
|
+
'1012e74bf6e2dfbfea8093c4d7beeb3f90dbdbcfd9b0d03fbc9d5ae282e4ee3fbc9d5ae282e4eebf90dbdbcf' +
|
|
658
|
+
'd9b0d03ffc9f72049f52ee3f541057a5b872d43f541057a5b872d4bffc9f72049f52ee3f0b0097497f6cdc3f' +
|
|
659
|
+
'00b9a069c1abec3f00b9a069c1abecbf0b0097497f6cdc3fcc7ab5331b1ae83f9ba0599fc00ce53f9ba0599f' +
|
|
660
|
+
'c00ce5bfcc7ab5331b1ae83fb309d7340144b13fc473b6ec58edef3fc473b6ec58edefbfb309d7340144b13f' +
|
|
661
|
+
'40392eaff3e5ef3f962027791166b43f962027791166b4bf40392eaff3e5ef3f0400ec45a1c0e43fcc58e91a' +
|
|
662
|
+
'c55be83fcc58e91ac55be8bf0400ec45a1c0e43ff33c23528e7eec3f5bdbe9e81620dd3f5bdbe9e81620ddbf' +
|
|
663
|
+
'f33c23528e7eec3fb71404faceb3d33f44976adb2772ee3f44976adb2772eebfb71404faceb3d33f84bfc3d3' +
|
|
664
|
+
'b2c9ee3f775176d7a072d13f775176d7a072d1bf84bfc3d3b2c9ee3f67d03f960534df3fdd7753e164f0eb3f' +
|
|
665
|
+
'dd7753e164f0ebbf67d03f960534df3fa29dd46f161be93f4483c53882d7e33f4483c53882d7e3bfa29dd46f' +
|
|
666
|
+
'161be93fc99faecb0ec7bd3f21b7fe6c64c8ef3f21b7fe6c64c8efbfc99faecb0ec7bd3f6e3de629a67eef3f' +
|
|
667
|
+
'b24af60413a8c63fb24af60413a8c6bf6e3de629a67eef3f1fac98fbd543e23fc89a11c87846ea3fc89a11c8' +
|
|
668
|
+
'7846eabf1fac98fbd543e23f74143cb404eeea3feb6c33af1549e13feb6c33af1549e1bf74143cb404eeea3f' +
|
|
669
|
+
'22673def3247cb3fdd92ff85d043ef3fdd92ff85d043efbf22673def3247cb3f600241cbd7c8ed3ff618240f' +
|
|
670
|
+
'3466d73ff618240f3466d7bf600241cbd7c8ed3fffbd41617193d93fb13ee9526f55ed3fb13ee9526f55edbf' +
|
|
671
|
+
'ffbd41617193d93f7a6d17b3420ae73fe91b1ca30335e63fe91b1ca30335e6bf7a6d17b3420ae73ffd0ee3bb' +
|
|
672
|
+
'36d9923fa1514bb49cfeef3fa1514bb49cfeefbffd0ee3bb36d9923fa1514bb49cfeef3ffd0ee3bb36d9923f' +
|
|
673
|
+
'fd0ee3bb36d992bfa1514bb49cfeef3fe91b1ca30335e63f7a6d17b3420ae73f7a6d17b3420ae7bfe91b1ca3' +
|
|
674
|
+
'0335e63fb13ee9526f55ed3fffbd41617193d93fffbd41617193d9bfb13ee9526f55ed3ff618240f3466d73f' +
|
|
675
|
+
'600241cbd7c8ed3f600241cbd7c8edbff618240f3466d73fdd92ff85d043ef3f22673def3247cb3f22673def' +
|
|
676
|
+
'3247cbbfdd92ff85d043ef3feb6c33af1549e13f74143cb404eeea3f74143cb404eeeabfeb6c33af1549e13f' +
|
|
677
|
+
'c89a11c87846ea3f1fac98fbd543e23f1fac98fbd543e2bfc89a11c87846ea3fb24af60413a8c63f6e3de629' +
|
|
678
|
+
'a67eef3f6e3de629a67eefbfb24af60413a8c63f21b7fe6c64c8ef3fc99faecb0ec7bd3fc99faecb0ec7bdbf' +
|
|
679
|
+
'21b7fe6c64c8ef3f4483c53882d7e33fa29dd46f161be93fa29dd46f161be9bf4483c53882d7e33fdd7753e1' +
|
|
680
|
+
'64f0eb3f67d03f960534df3f67d03f960534dfbfdd7753e164f0eb3f775176d7a072d13f84bfc3d3b2c9ee3f' +
|
|
681
|
+
'84bfc3d3b2c9eebf775176d7a072d13f44976adb2772ee3fb71404faceb3d33fb71404faceb3d3bf44976adb' +
|
|
682
|
+
'2772ee3f5bdbe9e81620dd3ff33c23528e7eec3ff33c23528e7eecbf5bdbe9e81620dd3fcc58e91ac55be83f' +
|
|
683
|
+
'0400ec45a1c0e43f0400ec45a1c0e4bfcc58e91ac55be83f962027791166b43f40392eaff3e5ef3f40392eaf' +
|
|
684
|
+
'f3e5efbf962027791166b43fc473b6ec58edef3fb309d7340144b13fb309d7340144b1bfc473b6ec58edef3f' +
|
|
685
|
+
'9ba0599fc00ce53fcc7ab5331b1ae83fcc7ab5331b1ae8bf9ba0599fc00ce53f00b9a069c1abec3f0b009749' +
|
|
686
|
+
'7f6cdc3f0b0097497f6cdcbf00b9a069c1abec3f541057a5b872d43ffc9f72049f52ee3ffc9f72049f52eebf' +
|
|
687
|
+
'541057a5b872d43fbc9d5ae282e4ee3f90dbdbcfd9b0d03f90dbdbcfd9b0d0bfbc9d5ae282e4ee3f1012e74b' +
|
|
688
|
+
'f6e2df3fea8093c4d7beeb3fea8093c4d7beebbf1012e74bf6e2df3fd76d8ee4ef58e93f22ebdf854188e33f' +
|
|
689
|
+
'22ebdf854188e3bfd76d8ee4ef58e93f1d83ba47a072c03f8641e41716bcef3f8641e41716bcefbf1d83ba47' +
|
|
690
|
+
'a072c03fdb41aeffd58fef3ff2c59785df1bc53ff2c59785df1bc5bfdb41aeffd58fef3fa89c62270796e23f' +
|
|
691
|
+
'37f9baea950cea3f37f9baea950ceabfa89c62270796e23fb4130047cd23eb3f7e8e2abb26f4e03f7e8e2abb' +
|
|
692
|
+
'26f4e0bfb4130047cd23eb3f86b212b38ccfcc3f9d9a08c9c92def3f9d9a08c9c92defbf86b212b38ccfcc3f' +
|
|
693
|
+
'da47def705eded3f177ec77d9daad63f177ec77d9daad6bfda47def705eded3fe5a1de27414bda3f9fefe020' +
|
|
694
|
+
'b22ced3f9fefe020b22cedbfe5a1de27414bda3f8dd2a88d944fe73f7470839534ece53f7470839534ece5bf' +
|
|
695
|
+
'8dd2a88d944fe73f01cfd13137699f3ff8d3f11d25fcef3ff8d3f11d25fcefbf01cfd13137699f3fdf81dbda' +
|
|
696
|
+
'71f8ef3f43cd90d200fca53f43cd90d200fca5bfdf81dbda71f8ef3f50725d2a8da2e53fe5554f570094e73f' +
|
|
697
|
+
'e5554f570094e7bf50725d2a8da2e53f92bdb2fed402ed3f09407f6c0d02db3f09407f6c0d02dbbf92bdb2fe' +
|
|
698
|
+
'd402ed3f93a69e3727eed53fac8029ca0c10ee3fac8029ca0c10eebf93a69e3727eed53f5d20f7538f16ef3f' +
|
|
699
|
+
'1b1a101eca56ce3f1b1a101eca56cebf5d20f7538f16ef3fe1c51774909ee03f051492fe8958eb3f051492fe' +
|
|
700
|
+
'8958ebbfe1c51774909ee03fd580eaf5b1d1e93f17eae8e380e7e23f17eae8e380e7e2bfd580eaf5b1d1e93f' +
|
|
701
|
+
'148dcdb0db8ec33fc8b2ad55ce9fef3fc8b2ad55ce9fefbf148dcdb0db8ec33fbbcf468e8eaeef3fcf7becd4' +
|
|
702
|
+
'1601c23fcf7becd41601c2bfbbcf468e8eaeef3f578e0c0d4038e33f220dd82ecf95e93f220dd82ecf95e9bf' +
|
|
703
|
+
'578e0c0d4038e33fe90475d2388ceb3f1a22ae265648e03f1a22ae265648e0bfe90475d2388ceb3ff9eddf1a' +
|
|
704
|
+
'dcdccf3fec950b0c22feee3fec950b0c22feeebff9eddf1adcdccf3f25ce70e8ea31ee3f243caf80d830d53f' +
|
|
705
|
+
'243caf80d830d5bf25ce70e8ea31ee3f01bd0423cfb7db3ff6328b89d9d7ec3ff6328b89d9d7ecbf01bd0423' +
|
|
706
|
+
'cfb7db3fb23dc36c83d7e73f375197381058e53f375197381058e5bfb23dc36c83d7e73fe3d7c0128d42ac3f' +
|
|
707
|
+
'5c578d0f83f3ef3f5c578d0f83f3efbfe3d7c0128d42ac3f56f4f19f53ddef3f215b5d6a5887b73f215b5d6a' +
|
|
708
|
+
'5887b7bf56f4f19f53ddef3f4773981bb573e43faad44d9a7e9ce83faad44d9a7e9ce8bf4773981bb573e43f' +
|
|
709
|
+
'07692b014250ec3f58cc81148fd2dd3f58cc81148fd2ddbf07692b014250ec3f8703ecda22f4d23ff2f71d36' +
|
|
710
|
+
'8490ee3ff2f71d368490eebf8703ecda22f4d23f8ea8e7e8b2adee3f71bbc3abbb33d23f71bbc3abbb33d2bf' +
|
|
711
|
+
'8ea8e7e8b2adee3f1451f8eae083de3fb071a93fde20ec3fb071a93fde20ecbf1451f8eae083de3fcc981633' +
|
|
712
|
+
'45dce83fb16b8e17ff25e43fb16b8e17ff25e4bfcc98163345dce83f035c4924b7a7ba3f840b221479d3ef3f' +
|
|
713
|
+
'840b221479d3efbf035c4924b7a7ba3fb7bbf57d3f6cef3fc6649ce86633c83fc6649ce86633c8bfb7bbf57d' +
|
|
714
|
+
'3f6cef3f1b86bc8bf0f0e13f9de69f52587fea3f9de69f52587feabf1b86bc8bf0f0e13fd4c0165932b7ea3f' +
|
|
715
|
+
'b8b9f2095a9de13fb8b9f2095a9de1bfd4c0165932b7ea3f6643dcf2cbbdc93f849e78b1a258ef3f849e78b1' +
|
|
716
|
+
'a258efbf6643dcf2cbbdc93f888966a983a3ed3fc4aa4eb0e320d83fc4aa4eb0e320d8bf888966a983a3ed3f' +
|
|
717
|
+
'b0a4c82ea5dad83ff9ecb8020b7ded3ff9ecb8020b7dedbfb0a4c82ea5dad83f7582c1730dc4e63f10af9184' +
|
|
718
|
+
'f77ce63f10af9184f77ce6bf7582c1730dc4e63f710067fef021793f928a8e85d8ffef3f928a8e85d8ffefbf' +
|
|
719
|
+
'710067fef021793f021d6221f6ffef3fbaa4ccbef821693fbaa4ccbef82169bf021d6221f6ffef3f719ca1ea' +
|
|
720
|
+
'd18ee63f9ce22fed5cb2e63f9ce22fed5cb2e6bf719ca1ead18ee63f4fa44584c486ed3f44edd5864bacd83f' +
|
|
721
|
+
'44edd5864bacd8bf4fa44584c486ed3f3f90f3aa6a4fd83f463d8bdd009aed3f463d8bdd009aedbf3f90f3aa' +
|
|
722
|
+
'6a4fd83f5d6843eda65def3ffa2ab6e9495bc93ffa2ab6e9495bc9bf5d6843eda65def3fbf73131750b2e13f' +
|
|
723
|
+
'8eb92c7a54a9ea3f8eb92c7a54a9eabfbf73131750b2e13fd25a546e678dea3f7248dc641bdce13f7248dc64' +
|
|
724
|
+
'1bdce1bfd25a546e678dea3f0418c4271796c83fee3c88567567ef3fee3c88567567efbf0418c4271796c83f' +
|
|
725
|
+
'9e5ca72d0dd6ef3f5ca824ebb6dfb93f5ca824ebb6dfb9bf9e5ca72d0dd6ef3f80432a5b7f39e43f55461875' +
|
|
726
|
+
'6acce83f554618756acce8bf80432a5b7f39e43ff1e33149d12cec3f25d83c6da857de3f25d83c6da857debf' +
|
|
727
|
+
'f1e33149d12cec3fba545599e663d23f0058e69383a6ee3f0058e69383a6eebfba545599e663d23f306b0136' +
|
|
728
|
+
'ec97ee3f2045954e1ac4d23f2045954e1ac4d2bf306b0136ec97ee3fde41a966fffedd3f04c041318344ec3f' +
|
|
729
|
+
'04c041318344ecbfde41a966fffedd3f881dde1e87ace83fa2322b695a60e43fa2322b695a60e4bf881dde1e' +
|
|
730
|
+
'87ace83fa130c112874fb83f8c531475fadaef3f8c531475fadaefbfa130c112874fb83fd3beb154dcf4ef3f' +
|
|
731
|
+
'17835fbd01b1aa3f17835fbd01b1aabfd3beb154dcf4ef3f9f649751c36ae53f33d3e29cb8c6e73f33d3e29c' +
|
|
732
|
+
'b8c6e7bf9f649751c36ae53f60a09927b3e2ec3f9356fd14788adb3f9356fd14788adbbf60a09927b3e2ec3f' +
|
|
733
|
+
'b467f4124060d53f7a1939448f29ee3f7a1939448f29eebfb467f4124060d53f8c73cf145a04ef3f0238bd80' +
|
|
734
|
+
'747bcf3f0238bd80747bcfbf8c73cf145a04ef3fb7b831ecf35de03fe992e786667feb3fe992e786667febbf' +
|
|
735
|
+
'b7b831ecf35de03fb2062ba4dfa4e93f1fa649ec2124e33f1fa649ec2124e3bfb2062ba4dfa4e93f0934fd4d' +
|
|
736
|
+
'9964c23fdcfd0ccbfbaaef3fdcfd0ccbfbaaefbf0934fd4d9964c23f91177aac9ba3ef3fa71645f97b2bc33f' +
|
|
737
|
+
'a71645f97b2bc3bf91177aac9ba3ef3f1510444bc2fbe23fc275f010d1c2e93fc275f010d1c2e9bf1510444b' +
|
|
738
|
+
'c2fbe23f47bcfd148f65eb3f8cb032201189e03f8cb032201189e0bf47bcfd148f65eb3f48e32d466bb8ce3f' +
|
|
739
|
+
'5f8f89bc9010ef3f5f8f89bc9010efbf48e32d466bb8ce3fd966dc2fa018ee3fb6b39d8be7bed53fb6b39d8b' +
|
|
740
|
+
'e7bed5bfd966dc2fa018ee3f7219b31d972fdb3f7b46cee830f8ec3f7b46cee830f8ecbf7219b31d972fdb3f' +
|
|
741
|
+
'd297bf07f7a4e73fdf23f7d50190e53fdf23f7d50190e5bfd297bf07f7a4e73f864687a5ba8da73f64911bbb' +
|
|
742
|
+
'53f7ef3f64911bbb53f7efbf864687a5ba8da73f79a6e29ce0fcef3f1d3be54c4f459c3f1d3be54c4f459cbf' +
|
|
743
|
+
'79a6e29ce0fcef3f106ae5bd7cfee53f4299078e553ee73f4299078e553ee7bf106ae5bd7cfee53fdcfbcb7b' +
|
|
744
|
+
'fc36ed3fc00ab543651dda3fc00ab543651ddabfdcfbcb7bfc36ed3fb60c8a6398d9d63f818d6d0f16e4ed3f' +
|
|
745
|
+
'818d6d0f16e4edbfb60c8a6398d9d63ff0ae3a5a6833ef3fdd745d53906dcc3fdd745d53906dccbff0ae3a5a' +
|
|
746
|
+
'6833ef3f57a9d0487209e13ff5a24c2a7416eb3ff5a24c2a7416ebbf57a9d0487209e13f5ea7c0d2261bea3f' +
|
|
747
|
+
'ba3c4def8b81e23fba3c4def8b81e2bf5ea7c0d2261bea3fdecb5486007fc53f784bcb37a78bef3f784bcb37' +
|
|
748
|
+
'a78befbfdecb5486007fc53f888d0a0f47bfef3f5bb86fade80ec03f5bb86fade80ec0bf888d0a0f47bfef3f' +
|
|
749
|
+
'2930d6e3239ce33f6c4aace39049e93f6c4aace39049e9bf2930d6e3239ce33f27230dcb54cbeb3fded2245c' +
|
|
750
|
+
'57b7df3fded2245c57b7dfbf27230dcb54cbeb3fce49174e5be1d03f5186076aebddee3f5186076aebddeebf' +
|
|
751
|
+
'ce49174e5be1d03fd36704559d5aee3ff03689dc1043d43ff03689dc1043d4bfd36704559d5aee3f895386c3' +
|
|
752
|
+
'7f99dc3f49c4b9198fa0ec3f49c4b9198fa0ecbf895386c37f99dc3fff45f5139c2ae83f86a4cc25ccf9e43f' +
|
|
753
|
+
'86a4cc25ccf9e4bfff45f5139c2ae83f4d44ed74960cb23f0f4130259debef3f0f4130259debefbf4d44ed74' +
|
|
754
|
+
'960cb23f602d4885eae7ef3f99a2c5129f9db33f99a2c5129f9db3bf602d4885eae7ef3f7f9f586dbcd3e43f' +
|
|
755
|
+
'fa83af11714be83ffa83af11714be8bf7f9f586dbcd3e43f139c0287f589ec3f21cde1ae4bf3dc3f21cde1ae' +
|
|
756
|
+
'4bf3dcbf139c0287f589ec3f71c26ee99be3d33fa7535dc5616aee3fa7535dc5616aeebf71c26ee99be3d33f' +
|
|
757
|
+
'0990995e83d0ee3f7893c6ef3e42d13f7893c6ef3e42d1bf0990995e83d0ee3fa3cd56e6de5fdf3fc1541161' +
|
|
758
|
+
'1be4eb3fc15411611be4ebbfa3cd56e6de5fdf3f15a8c51fa42ae93f18c58149c4c3e33f18c58149c4c3e3bf' +
|
|
759
|
+
'15a8c51fa42ae93f3faae4fdb78ebe3ff69a7d3b6ec5ef3ff69a7d3b6ec5efbf3faae4fdb78ebe3f0cc6404a' +
|
|
760
|
+
'0f83ef3f0d831d831a45c63f0d831d831a45c6bf0cc6404a0f83ef3f1071bb4c7358e23fc63b594a1838ea3f' +
|
|
761
|
+
'c63b594a1838eabf1071bb4c7358e23fb6579fd88ffbea3f4f25eecfe933e13f4f25eecfe933e1bfb6579fd8' +
|
|
762
|
+
'8ffbea3fad5df13463a9cb3f65bc1bbc6b3eef3f65bc1bbc6b3eefbfad5df13463a9cb3f5a918af3fed1ed3f' +
|
|
763
|
+
'921026c96337d73f921026c96337d7bf5a918af3fed1ed3ff2f90d447dc1d93f2475181b5b4bed3f2475181b' +
|
|
764
|
+
'5b4bedbff2f90d447dc1d93fbf410e96ac1be73fff22ec4fe422e63fff22ec4fe422e6bfbf410e96ac1be73f' +
|
|
765
|
+
'26b2fa214dfd953f77cb70681cfeef3f77cb70681cfeefbf26b2fa214dfd953fd13bc54309ffef3fcb97b96a' +
|
|
766
|
+
'296a8f3fcb97b96a296a8fbfd13bc54309ffef3f5b537f431547e63f755bc999caf8e63f755bc999caf8e6bf' +
|
|
767
|
+
'5b537f431547e63f7f8a8872715fed3f8f94abb75565d93f8f94abb75565d9bf7f8a8872715fed3faedf13e6' +
|
|
768
|
+
'f594d73f9a7595439ebfed3f9a7595439ebfedbfaedf13e6f594d73fb4abbc062249ef3fabb9f3d5f1e4ca3f' +
|
|
769
|
+
'abb9f3d5f1e4cabfb4abbc062249ef3fbce2dbe4365ee13fefec45f368e0ea3fefec45f368e0eabfbce2dbe4' +
|
|
770
|
+
'365ee13f23f59010c954ea3fe2132c662d2fe23fe2132c662d2fe2bf23f59010c954ea3fffc4088dfd0ac73f' +
|
|
771
|
+
'2a321a9c297aef3f2a321a9c297aefbfffc4088dfd0ac73f5443910347cbef3fc17d303b53ffbc3fc17d303b' +
|
|
772
|
+
'53ffbcbf5443910347cbef3f8006beea33ebe33ffe5e5743790be93ffe5e5743790be9bf8006beea33ebe33f' +
|
|
773
|
+
'47b1a1259dfceb3ffef7bf061908df3ffef7bf061908dfbf47b1a1259dfceb3f43f2e8fbf7a2d13fb2f61a4b' +
|
|
774
|
+
'cfc2ee3fb2f61a4bcfc2eebf43f2e8fbf7a2d13f5a16a529db79ee3fabb653e3f583d33fabb653e3f583d3bf' +
|
|
775
|
+
'5a16a529db79ee3f9d60a82bd04cdd3fd7aa9e891573ec3fd7aa9e891573ecbf9d60a82bd04cdd3f95a19a1d' +
|
|
776
|
+
'0a6ce83ff122675179ade43ff122675179ade4bf95a19a1d0a6ce83f0a4d4d4a772eb53f86d8e92be9e3ef3f' +
|
|
777
|
+
'86d8e92be9e3efbf0a4d4d4a772eb53f9161820201efef3f6430464e617bb03f6430464e617bb0bf91618202' +
|
|
778
|
+
'01efef3fa69ad91ca81fe53ffa526e758b09e83ffa526e758b09e8bfa69ad91ca81fe53f99da000ae2b6ec3f' +
|
|
779
|
+
'293126476d3fdc3f293126476d3fdcbf99da000ae2b6ec3ff3821bd153a2d43f5ece81ff8d4aee3f5ece81ff' +
|
|
780
|
+
'8d4aeebff3821bd153a2d43f44a5504c07ebee3f1e66eb054e80d03f1e66eb054e80d0bf44a5504c07ebee3f' +
|
|
781
|
+
'e1822bc84007e03f0dc4b6a049b2eb3f0dc4b6a049b2ebbfe1822bc84007e03fe17fbd423f68e93f8d7f811b' +
|
|
782
|
+
'5374e33f8d7f811b5374e3bfe17fbd423f68e93f8667b2bc4dd6c03fb7ad668dd1b8ef3fb7ad668dd1b8efbf' +
|
|
783
|
+
'8667b2bc4dd6c03f08ac854ff193ef3f88fa797fb1b8c43f88fa797fb1b8c4bf08ac854ff193ef3f58eb7ae8' +
|
|
784
|
+
'76aae23fde4931f1f4fde93fde4931f1f4fde9bf58eb7ae876aae23ff37bf3a51531eb3fb6c44bb8d0dee03f' +
|
|
785
|
+
'b6c44bb8d0dee0bff37bf3a51531eb3feebd2c4d7731cd3fce0946fc1728ef3fce0946fc1728efbfeebd2c4d' +
|
|
786
|
+
'7731cd3f9ca59b6ae3f5ed3fcb63ad9c947bd63fcb63ad9c947bd6bf9ca59b6ae3f5ed3f1bf3dbd30c79da3f' +
|
|
787
|
+
'e1a4e5c65522ed3fe1a4e5c65522edbf1bf3dbd30c79da3f6447302cc560e73f5c343ee7ded9e53f5c343ee7' +
|
|
788
|
+
'ded9e5bf6447302cc560e73f7fc142db8546a13faefd25e455fbef3faefd25e455fbefbf7fc142db8546a13f' +
|
|
789
|
+
'14c008427cf9ef3f7961f86f396aa43f7961f86f396aa4bf14c008427cf9ef3f48744f260bb5e53f5bb3901b' +
|
|
790
|
+
'fb82e73f5bb3901bfb82e7bf48744f260bb5e53fb9d2592f670ded3f09dc5c1273d4da3f09dc5c1273d4dabf' +
|
|
791
|
+
'b9d2592f670ded3f02c2885c591dd63f540f28d96607ee3f540f28d96607eebf02c2885c591dd63f084728be' +
|
|
792
|
+
'7a1cef3f9a09013f16f5cd3f9a09013f16f5cdbf084728be7a1cef3fec858f8705b4e03f2579de09744beb3f' +
|
|
793
|
+
'2579de09744bebbfec858f8705b4e03f7224b4ed82e0e93fb89b4ed333d3e23fb89b4ed333d3e2bf7224b4ed' +
|
|
794
|
+
'82e0e93f9348db572ff2c33f29defb7ced9bef3f29defb7ced9befbf9348db572ff2c33f4dd581c60db2ef3f' +
|
|
795
|
+
'e724be40899dc13fe724be40899dc1bf4dd581c60db2ef3fe14dc152524ce33f947545f1ae86e93f947545f1' +
|
|
796
|
+
'ae86e9bfe14dc152524ce33f5e15d91ffa98eb3f96bded55ae32e03f96bded55ae32e0bf5e15d91ffa98eb3f' +
|
|
797
|
+
'd2fdb906181fd03fc0a31ce5d6f7ee3fc0a31ce5d6f7eebfd2fdb906181fd03f85ce75ec333aee3f487019dc' +
|
|
798
|
+
'6301d53f487019dc6301d5bf85ce75ec333aee3fd9c0ff1715e5db3fa0dec220eeccec3fa0dec220eeccecbf' +
|
|
799
|
+
'd9c0ff1715e5db3f8636b0873fe8e73ffc9d15f54f45e53ffc9d15f54f45e5bf8636b0873fe8e73fc98e80f9' +
|
|
800
|
+
'06d4ad3fed31e11416f2ef3fed31e11416f2efbfc98e80f906d4ad3f0733f72299dfef3f29b1793e1bbfb63f' +
|
|
801
|
+
'29b1793e1bbfb6bf0733f72299dfef3fff9160300387e43fa11b48e7668ce83fa11b48e7668ce8bfff916030' +
|
|
802
|
+
'0387e43f5af8fe59ef5bec3fd910fa5c0ca6dd3fd910fa5c0ca6ddbf5af8fe59ef5bec3fafba38b61f24d33f' +
|
|
803
|
+
'2560ad5b0989ee3f2560ad5b0989eebfafba38b61f24d33f11885b51cfb4ee3fbe27d7838503d23fbe27d783' +
|
|
804
|
+
'8503d2bf11885b51cfb4ee3f2056f29506b0de3f575e46dcd914ec3f575e46dcd914ecbf2056f29506b0de3f' +
|
|
805
|
+
'496c489b10ece83f8c103d667212e43f8c103d667212e4bf496c489b10ece83f4cf638eca66fbb3f8760d858' +
|
|
806
|
+
'd1d0ef3f8760d858d1d0efbf4cf638eca66fbb3fb77e4b43f670ef3f1ccbd2bba7d0c73f1ccbd2bba7d0c7bf' +
|
|
807
|
+
'b77e4b43f670ef3fd66075a1ba05e23ff5609dde3871ea3ff5609dde3871eabfd66075a1ba05e23fc8fa3ebd' +
|
|
808
|
+
'ffc4ea3fe5463a1f5988e13fe5463a1f5988e1bfc8fa3ebdffc4ea3fda31181b3e20ca3f072daf1f8b53ef3f' +
|
|
809
|
+
'072daf1f8b53efbfda31181b3e20ca3fb98ae62cf4aced3fe44173d34df2d73fe44173d34df2d7bfb98ae62c' +
|
|
810
|
+
'f4aced3fd17bef81ef08d93fff0d8c503f73ed3fff0d8c503f73edbfd17bef81ef08d93fcdaf4aefafd5e63f' +
|
|
811
|
+
'86b3523f0f6be63f86b3523f0f6be6bfcdaf4aefafd5e63f0397500e6bd9823f4f8c972ca7ffef3f4f8c972c' +
|
|
812
|
+
'a7ffefbf0397500e6bd9823f4f8c972ca7ffef3f0397500e6bd9823f0397500e6bd982bf4f8c972ca7ffef3f' +
|
|
813
|
+
'86b3523f0f6be63fcdaf4aefafd5e63fcdaf4aefafd5e6bf86b3523f0f6be63fff0d8c503f73ed3fd17bef81' +
|
|
814
|
+
'ef08d93fd17bef81ef08d9bfff0d8c503f73ed3fe44173d34df2d73fb98ae62cf4aced3fb98ae62cf4acedbf' +
|
|
815
|
+
'e44173d34df2d73f072daf1f8b53ef3fda31181b3e20ca3fda31181b3e20cabf072daf1f8b53ef3fe5463a1f' +
|
|
816
|
+
'5988e13fc8fa3ebdffc4ea3fc8fa3ebdffc4eabfe5463a1f5988e13ff5609dde3871ea3fd66075a1ba05e23f' +
|
|
817
|
+
'd66075a1ba05e2bff5609dde3871ea3f1ccbd2bba7d0c73fb77e4b43f670ef3fb77e4b43f670efbf1ccbd2bb' +
|
|
818
|
+
'a7d0c73f8760d858d1d0ef3f4cf638eca66fbb3f4cf638eca66fbbbf8760d858d1d0ef3f8c103d667212e43f' +
|
|
819
|
+
'496c489b10ece83f496c489b10ece8bf8c103d667212e43f575e46dcd914ec3f2056f29506b0de3f2056f295' +
|
|
820
|
+
'06b0debf575e46dcd914ec3fbe27d7838503d23f11885b51cfb4ee3f11885b51cfb4eebfbe27d7838503d23f' +
|
|
821
|
+
'2560ad5b0989ee3fafba38b61f24d33fafba38b61f24d3bf2560ad5b0989ee3fd910fa5c0ca6dd3f5af8fe59' +
|
|
822
|
+
'ef5bec3f5af8fe59ef5becbfd910fa5c0ca6dd3fa11b48e7668ce83fff9160300387e43fff9160300387e4bf' +
|
|
823
|
+
'a11b48e7668ce83f29b1793e1bbfb63f0733f72299dfef3f0733f72299dfefbf29b1793e1bbfb63fed31e114' +
|
|
824
|
+
'16f2ef3fc98e80f906d4ad3fc98e80f906d4adbfed31e11416f2ef3ffc9d15f54f45e53f8636b0873fe8e73f' +
|
|
825
|
+
'8636b0873fe8e7bffc9d15f54f45e53fa0dec220eeccec3fd9c0ff1715e5db3fd9c0ff1715e5dbbfa0dec220' +
|
|
826
|
+
'eeccec3f487019dc6301d53f85ce75ec333aee3f85ce75ec333aeebf487019dc6301d53fc0a31ce5d6f7ee3f' +
|
|
827
|
+
'd2fdb906181fd03fd2fdb906181fd0bfc0a31ce5d6f7ee3f96bded55ae32e03f5e15d91ffa98eb3f5e15d91f' +
|
|
828
|
+
'fa98ebbf96bded55ae32e03f947545f1ae86e93fe14dc152524ce33fe14dc152524ce3bf947545f1ae86e93f' +
|
|
829
|
+
'e724be40899dc13f4dd581c60db2ef3f4dd581c60db2efbfe724be40899dc13f29defb7ced9bef3f9348db57' +
|
|
830
|
+
'2ff2c33f9348db572ff2c3bf29defb7ced9bef3fb89b4ed333d3e23f7224b4ed82e0e93f7224b4ed82e0e9bf' +
|
|
831
|
+
'b89b4ed333d3e23f2579de09744beb3fec858f8705b4e03fec858f8705b4e0bf2579de09744beb3f9a09013f' +
|
|
832
|
+
'16f5cd3f084728be7a1cef3f084728be7a1cefbf9a09013f16f5cd3f540f28d96607ee3f02c2885c591dd63f' +
|
|
833
|
+
'02c2885c591dd6bf540f28d96607ee3f09dc5c1273d4da3fb9d2592f670ded3fb9d2592f670dedbf09dc5c12' +
|
|
834
|
+
'73d4da3f5bb3901bfb82e73f48744f260bb5e53f48744f260bb5e5bf5bb3901bfb82e73f7961f86f396aa43f' +
|
|
835
|
+
'14c008427cf9ef3f14c008427cf9efbf7961f86f396aa43faefd25e455fbef3f7fc142db8546a13f7fc142db' +
|
|
836
|
+
'8546a1bfaefd25e455fbef3f5c343ee7ded9e53f6447302cc560e73f6447302cc560e7bf5c343ee7ded9e53f' +
|
|
837
|
+
'e1a4e5c65522ed3f1bf3dbd30c79da3f1bf3dbd30c79dabfe1a4e5c65522ed3fcb63ad9c947bd63f9ca59b6a' +
|
|
838
|
+
'e3f5ed3f9ca59b6ae3f5edbfcb63ad9c947bd63fce0946fc1728ef3feebd2c4d7731cd3feebd2c4d7731cdbf' +
|
|
839
|
+
'ce0946fc1728ef3fb6c44bb8d0dee03ff37bf3a51531eb3ff37bf3a51531ebbfb6c44bb8d0dee03fde4931f1' +
|
|
840
|
+
'f4fde93f58eb7ae876aae23f58eb7ae876aae2bfde4931f1f4fde93f88fa797fb1b8c43f08ac854ff193ef3f' +
|
|
841
|
+
'08ac854ff193efbf88fa797fb1b8c43fb7ad668dd1b8ef3f8667b2bc4dd6c03f8667b2bc4dd6c0bfb7ad668d' +
|
|
842
|
+
'd1b8ef3f8d7f811b5374e33fe17fbd423f68e93fe17fbd423f68e9bf8d7f811b5374e33f0dc4b6a049b2eb3f' +
|
|
843
|
+
'e1822bc84007e03fe1822bc84007e0bf0dc4b6a049b2eb3f1e66eb054e80d03f44a5504c07ebee3f44a5504c' +
|
|
844
|
+
'07ebeebf1e66eb054e80d03f5ece81ff8d4aee3ff3821bd153a2d43ff3821bd153a2d4bf5ece81ff8d4aee3f' +
|
|
845
|
+
'293126476d3fdc3f99da000ae2b6ec3f99da000ae2b6ecbf293126476d3fdc3ffa526e758b09e83fa69ad91c' +
|
|
846
|
+
'a81fe53fa69ad91ca81fe5bffa526e758b09e83f6430464e617bb03f9161820201efef3f9161820201efefbf' +
|
|
847
|
+
'6430464e617bb03f86d8e92be9e3ef3f0a4d4d4a772eb53f0a4d4d4a772eb5bf86d8e92be9e3ef3ff1226751' +
|
|
848
|
+
'79ade43f95a19a1d0a6ce83f95a19a1d0a6ce8bff122675179ade43fd7aa9e891573ec3f9d60a82bd04cdd3f' +
|
|
849
|
+
'9d60a82bd04cddbfd7aa9e891573ec3fabb653e3f583d33f5a16a529db79ee3f5a16a529db79eebfabb653e3' +
|
|
850
|
+
'f583d33fb2f61a4bcfc2ee3f43f2e8fbf7a2d13f43f2e8fbf7a2d1bfb2f61a4bcfc2ee3ffef7bf061908df3f' +
|
|
851
|
+
'47b1a1259dfceb3f47b1a1259dfcebbffef7bf061908df3ffe5e5743790be93f8006beea33ebe33f8006beea' +
|
|
852
|
+
'33ebe3bffe5e5743790be93fc17d303b53ffbc3f5443910347cbef3f5443910347cbefbfc17d303b53ffbc3f' +
|
|
853
|
+
'2a321a9c297aef3fffc4088dfd0ac73fffc4088dfd0ac7bf2a321a9c297aef3fe2132c662d2fe23f23f59010' +
|
|
854
|
+
'c954ea3f23f59010c954eabfe2132c662d2fe23fefec45f368e0ea3fbce2dbe4365ee13fbce2dbe4365ee1bf' +
|
|
855
|
+
'efec45f368e0ea3fabb9f3d5f1e4ca3fb4abbc062249ef3fb4abbc062249efbfabb9f3d5f1e4ca3f9a759543' +
|
|
856
|
+
'9ebfed3faedf13e6f594d73faedf13e6f594d7bf9a7595439ebfed3f8f94abb75565d93f7f8a8872715fed3f' +
|
|
857
|
+
'7f8a8872715fedbf8f94abb75565d93f755bc999caf8e63f5b537f431547e63f5b537f431547e6bf755bc999' +
|
|
858
|
+
'caf8e63fcb97b96a296a8f3fd13bc54309ffef3fd13bc54309ffefbfcb97b96a296a8f3f77cb70681cfeef3f' +
|
|
859
|
+
'26b2fa214dfd953f26b2fa214dfd95bf77cb70681cfeef3fff22ec4fe422e63fbf410e96ac1be73fbf410e96' +
|
|
860
|
+
'ac1be7bfff22ec4fe422e63f2475181b5b4bed3ff2f90d447dc1d93ff2f90d447dc1d9bf2475181b5b4bed3f' +
|
|
861
|
+
'921026c96337d73f5a918af3fed1ed3f5a918af3fed1edbf921026c96337d73f65bc1bbc6b3eef3fad5df134' +
|
|
862
|
+
'63a9cb3fad5df13463a9cbbf65bc1bbc6b3eef3f4f25eecfe933e13fb6579fd88ffbea3fb6579fd88ffbeabf' +
|
|
863
|
+
'4f25eecfe933e13fc63b594a1838ea3f1071bb4c7358e23f1071bb4c7358e2bfc63b594a1838ea3f0d831d83' +
|
|
864
|
+
'1a45c63f0cc6404a0f83ef3f0cc6404a0f83efbf0d831d831a45c63ff69a7d3b6ec5ef3f3faae4fdb78ebe3f' +
|
|
865
|
+
'3faae4fdb78ebebff69a7d3b6ec5ef3f18c58149c4c3e33f15a8c51fa42ae93f15a8c51fa42ae9bf18c58149' +
|
|
866
|
+
'c4c3e33fc15411611be4eb3fa3cd56e6de5fdf3fa3cd56e6de5fdfbfc15411611be4eb3f7893c6ef3e42d13f' +
|
|
867
|
+
'0990995e83d0ee3f0990995e83d0eebf7893c6ef3e42d13fa7535dc5616aee3f71c26ee99be3d33f71c26ee9' +
|
|
868
|
+
'9be3d3bfa7535dc5616aee3f21cde1ae4bf3dc3f139c0287f589ec3f139c0287f589ecbf21cde1ae4bf3dc3f' +
|
|
869
|
+
'fa83af11714be83f7f9f586dbcd3e43f7f9f586dbcd3e4bffa83af11714be83f99a2c5129f9db33f602d4885' +
|
|
870
|
+
'eae7ef3f602d4885eae7efbf99a2c5129f9db33f0f4130259debef3f4d44ed74960cb23f4d44ed74960cb2bf' +
|
|
871
|
+
'0f4130259debef3f86a4cc25ccf9e43fff45f5139c2ae83fff45f5139c2ae8bf86a4cc25ccf9e43f49c4b919' +
|
|
872
|
+
'8fa0ec3f895386c37f99dc3f895386c37f99dcbf49c4b9198fa0ec3ff03689dc1043d43fd36704559d5aee3f' +
|
|
873
|
+
'd36704559d5aeebff03689dc1043d43f5186076aebddee3fce49174e5be1d03fce49174e5be1d0bf5186076a' +
|
|
874
|
+
'ebddee3fded2245c57b7df3f27230dcb54cbeb3f27230dcb54cbebbfded2245c57b7df3f6c4aace39049e93f' +
|
|
875
|
+
'2930d6e3239ce33f2930d6e3239ce3bf6c4aace39049e93f5bb86fade80ec03f888d0a0f47bfef3f888d0a0f' +
|
|
876
|
+
'47bfefbf5bb86fade80ec03f784bcb37a78bef3fdecb5486007fc53fdecb5486007fc5bf784bcb37a78bef3f' +
|
|
877
|
+
'ba3c4def8b81e23f5ea7c0d2261bea3f5ea7c0d2261beabfba3c4def8b81e23ff5a24c2a7416eb3f57a9d048' +
|
|
878
|
+
'7209e13f57a9d0487209e1bff5a24c2a7416eb3fdd745d53906dcc3ff0ae3a5a6833ef3ff0ae3a5a6833efbf' +
|
|
879
|
+
'dd745d53906dcc3f818d6d0f16e4ed3fb60c8a6398d9d63fb60c8a6398d9d6bf818d6d0f16e4ed3fc00ab543' +
|
|
880
|
+
'651dda3fdcfbcb7bfc36ed3fdcfbcb7bfc36edbfc00ab543651dda3f4299078e553ee73f106ae5bd7cfee53f' +
|
|
881
|
+
'106ae5bd7cfee5bf4299078e553ee73f1d3be54c4f459c3f79a6e29ce0fcef3f79a6e29ce0fcefbf1d3be54c' +
|
|
882
|
+
'4f459c3f64911bbb53f7ef3f864687a5ba8da73f864687a5ba8da7bf64911bbb53f7ef3fdf23f7d50190e53f' +
|
|
883
|
+
'd297bf07f7a4e73fd297bf07f7a4e7bfdf23f7d50190e53f7b46cee830f8ec3f7219b31d972fdb3f7219b31d' +
|
|
884
|
+
'972fdbbf7b46cee830f8ec3fb6b39d8be7bed53fd966dc2fa018ee3fd966dc2fa018eebfb6b39d8be7bed53f' +
|
|
885
|
+
'5f8f89bc9010ef3f48e32d466bb8ce3f48e32d466bb8cebf5f8f89bc9010ef3f8cb032201189e03f47bcfd14' +
|
|
886
|
+
'8f65eb3f47bcfd148f65ebbf8cb032201189e03fc275f010d1c2e93f1510444bc2fbe23f1510444bc2fbe2bf' +
|
|
887
|
+
'c275f010d1c2e93fa71645f97b2bc33f91177aac9ba3ef3f91177aac9ba3efbfa71645f97b2bc33fdcfd0ccb' +
|
|
888
|
+
'fbaaef3f0934fd4d9964c23f0934fd4d9964c2bfdcfd0ccbfbaaef3f1fa649ec2124e33fb2062ba4dfa4e93f' +
|
|
889
|
+
'b2062ba4dfa4e9bf1fa649ec2124e33fe992e786667feb3fb7b831ecf35de03fb7b831ecf35de0bfe992e786' +
|
|
890
|
+
'667feb3f0238bd80747bcf3f8c73cf145a04ef3f8c73cf145a04efbf0238bd80747bcf3f7a1939448f29ee3f' +
|
|
891
|
+
'b467f4124060d53fb467f4124060d5bf7a1939448f29ee3f9356fd14788adb3f60a09927b3e2ec3f60a09927' +
|
|
892
|
+
'b3e2ecbf9356fd14788adb3f33d3e29cb8c6e73f9f649751c36ae53f9f649751c36ae5bf33d3e29cb8c6e73f' +
|
|
893
|
+
'17835fbd01b1aa3fd3beb154dcf4ef3fd3beb154dcf4efbf17835fbd01b1aa3f8c531475fadaef3fa130c112' +
|
|
894
|
+
'874fb83fa130c112874fb8bf8c531475fadaef3fa2322b695a60e43f881dde1e87ace83f881dde1e87ace8bf' +
|
|
895
|
+
'a2322b695a60e43f04c041318344ec3fde41a966fffedd3fde41a966fffeddbf04c041318344ec3f2045954e' +
|
|
896
|
+
'1ac4d23f306b0136ec97ee3f306b0136ec97eebf2045954e1ac4d23f0058e69383a6ee3fba545599e663d23f' +
|
|
897
|
+
'ba545599e663d2bf0058e69383a6ee3f25d83c6da857de3ff1e33149d12cec3ff1e33149d12cecbf25d83c6d' +
|
|
898
|
+
'a857de3f554618756acce83f80432a5b7f39e43f80432a5b7f39e4bf554618756acce83f5ca824ebb6dfb93f' +
|
|
899
|
+
'9e5ca72d0dd6ef3f9e5ca72d0dd6efbf5ca824ebb6dfb93fee3c88567567ef3f0418c4271796c83f0418c427' +
|
|
900
|
+
'1796c8bfee3c88567567ef3f7248dc641bdce13fd25a546e678dea3fd25a546e678deabf7248dc641bdce13f' +
|
|
901
|
+
'8eb92c7a54a9ea3fbf73131750b2e13fbf73131750b2e1bf8eb92c7a54a9ea3ffa2ab6e9495bc93f5d6843ed' +
|
|
902
|
+
'a65def3f5d6843eda65defbffa2ab6e9495bc93f463d8bdd009aed3f3f90f3aa6a4fd83f3f90f3aa6a4fd8bf' +
|
|
903
|
+
'463d8bdd009aed3f44edd5864bacd83f4fa44584c486ed3f4fa44584c486edbf44edd5864bacd83f9ce22fed' +
|
|
904
|
+
'5cb2e63f719ca1ead18ee63f719ca1ead18ee6bf9ce22fed5cb2e63fbaa4ccbef821693f021d6221f6ffef3f' +
|
|
905
|
+
'021d6221f6ffefbfbaa4ccbef821693f'));
|
|
906
|
+
// Sanity check (shake256 because used already): catch byte-level corruption, endianness/layout
|
|
907
|
+
// mistakes, or accidental regeneration through engine-dependent sin/cos results.
|
|
908
|
+
const rootBytes = u8f(baswap64If(Float64Array.from(roots)));
|
|
909
|
+
if (bytesToHex(shake256(rootBytes)) !==
|
|
910
|
+
'f45a496cf56ccc6e3e3395a20209206d81d71a7905a661447bd5bc0e24e0af1e') {
|
|
911
|
+
throw new Error('COMPLEX_ROOTS mismatch');
|
|
912
|
+
}
|
|
913
|
+
return roots;
|
|
914
|
+
})();
|
|
915
|
+
// Falcon's q-field Montgomery kernel: mul() operates on Montgomery residues and returns one.
|
|
916
|
+
// inv() accepts a normal residue but returns its inverse in Montgomery form, so div(x, y) can stay
|
|
917
|
+
// `mul(x, inv(y))`; callers like toMontgomery() manage the representation boundaries.
|
|
918
|
+
const intField = {
|
|
919
|
+
mul(x, y) {
|
|
920
|
+
let z = Math.imul(x, y);
|
|
921
|
+
let w = Math.imul(Q, Math.imul(z, Q0I) & 0xffff);
|
|
922
|
+
z = ((z + w) >>> 16) - Q;
|
|
923
|
+
z += Q & (z >> 31);
|
|
924
|
+
return z >>> 0;
|
|
925
|
+
},
|
|
926
|
+
inv(y) {
|
|
927
|
+
// y^(q-2) mod q
|
|
928
|
+
if (y === 0)
|
|
929
|
+
throw new Error('divison by zero');
|
|
930
|
+
const e00 = this.mul(y, R2); // e0 = 1
|
|
931
|
+
const e01 = this.mul(e00, e00); // 2 * e0 = 2
|
|
932
|
+
const e02 = this.mul(e01, e00); // e1 + e0 = 3
|
|
933
|
+
const e03 = this.mul(e02, e01); // e3 = e2 + e1 = 5
|
|
934
|
+
const e04 = this.mul(e03, e03); // e4 = 2 * e3 = 10
|
|
935
|
+
const e05 = this.mul(e04, e04); // e5 = 2 * e4 = 20
|
|
936
|
+
const e06 = this.mul(e05, e05); // e6 = 2 * e5 = 40
|
|
937
|
+
const e07 = this.mul(e06, e06); // e7 = 2 * e6 = 80
|
|
938
|
+
const e08 = this.mul(e07, e07); // e8 = 2 * e7 = 160
|
|
939
|
+
const e09 = this.mul(e08, e02); // e9 = e8 + e2 = 163
|
|
940
|
+
const e10 = this.mul(e09, e08); // e10 = e9 + e8 = 323
|
|
941
|
+
const e11 = this.mul(e10, e10); // e11 = 2 * e10 = 646
|
|
942
|
+
const e12 = this.mul(e11, e11); // e12 = 2 * e11 = 1292
|
|
943
|
+
const e13 = this.mul(e12, e09); // e13 = e12 + e9 = 1455
|
|
944
|
+
const e14 = this.mul(e13, e13); // e14 = 2 * e13 = 2910
|
|
945
|
+
const e15 = this.mul(e14, e14); // e15 = 2 * e14 = 5820
|
|
946
|
+
const e16 = this.mul(e15, e10); // e16 = e15 + e10 = 6143
|
|
947
|
+
const e17 = this.mul(e16, e16); // e17 = 2 * e16 = 12286
|
|
948
|
+
const e18 = this.mul(e17, e00); // e18 = e17 + e0 = 12287
|
|
949
|
+
return e18;
|
|
950
|
+
},
|
|
951
|
+
div: (x, y) => intField.mul(x, intField.inv(y)),
|
|
952
|
+
};
|
|
953
|
+
function getIntPoly(logn) {
|
|
954
|
+
const n = 1 << logn;
|
|
955
|
+
const newPoly = (n) => new Uint16Array(n);
|
|
956
|
+
const F = Number(invert(BigInt(n), QBig));
|
|
957
|
+
const { mod, smod, NTT } = genCrystals({
|
|
958
|
+
N: n,
|
|
959
|
+
Q,
|
|
960
|
+
F: F,
|
|
961
|
+
ROOT_OF_UNITY: 7,
|
|
962
|
+
newPoly,
|
|
963
|
+
isKyber: false,
|
|
964
|
+
brvBits: 10,
|
|
965
|
+
});
|
|
966
|
+
// Keep Falcon source compatible with older TS parsers: avoid spelling newer
|
|
967
|
+
// `Uint16Array<ArrayBuffer>` syntax directly and cast the callee side at the boundary.
|
|
968
|
+
const ntt = (r) => NTT.encode(r);
|
|
969
|
+
const intt = (r) => NTT.decode(r);
|
|
970
|
+
// Falcon integer helpers mutate their first argument in place; div() also performs intt()
|
|
971
|
+
// before returning, so callers must treat these as owned-temporary transforms, not pure helpers.
|
|
972
|
+
// Centered representatives are in [-6144, 6144] for odd q = 12289,
|
|
973
|
+
// not a generic [-q/2, q/2] range.
|
|
974
|
+
const signedCoder = {
|
|
975
|
+
encode: (p) => Int16Array.from(p, (x) => smod(x)),
|
|
976
|
+
decode: (p) => Uint16Array.from(p, (x) => mod(x)),
|
|
977
|
+
};
|
|
978
|
+
const intPoly = {
|
|
979
|
+
create: newPoly,
|
|
980
|
+
smallSqnorm(f) {
|
|
981
|
+
let s = 0;
|
|
982
|
+
let ng = 0;
|
|
983
|
+
for (let u = 0; u < n; u++) {
|
|
984
|
+
const z = f[u];
|
|
985
|
+
s = (s + z * z) >>> 0;
|
|
986
|
+
ng |= s;
|
|
987
|
+
}
|
|
988
|
+
return (s | -(ng >>> 31)) >>> 0;
|
|
989
|
+
},
|
|
990
|
+
isShort(s1, s2) {
|
|
991
|
+
let s = 0 >>> 0;
|
|
992
|
+
let ng = 0 >>> 0;
|
|
993
|
+
for (let u = 0; u < n; u++) {
|
|
994
|
+
let z1 = (s1[u] << 16) >> 16;
|
|
995
|
+
s = (s + ((z1 * z1) >>> 0)) >>> 0;
|
|
996
|
+
ng |= s;
|
|
997
|
+
let z2 = (s2[u] << 16) >> 16;
|
|
998
|
+
s = (s + ((z2 * z2) >>> 0)) >>> 0;
|
|
999
|
+
ng |= s;
|
|
1000
|
+
}
|
|
1001
|
+
if (ng & 0x80000000)
|
|
1002
|
+
s = 0xffffffff;
|
|
1003
|
+
return s <= L2BOUND[logn];
|
|
1004
|
+
},
|
|
1005
|
+
sub(a, b) {
|
|
1006
|
+
for (let i = 0; i < n; i++)
|
|
1007
|
+
a[i] = mod(a[i] - b[i]);
|
|
1008
|
+
return a;
|
|
1009
|
+
},
|
|
1010
|
+
ntt,
|
|
1011
|
+
intt,
|
|
1012
|
+
toMontgomery(d) {
|
|
1013
|
+
for (let i = 0; i < n; i++)
|
|
1014
|
+
d[i] = intField.mul(d[i], R2);
|
|
1015
|
+
return d;
|
|
1016
|
+
},
|
|
1017
|
+
mul(f, d) {
|
|
1018
|
+
for (let i = 0; i < n; i++)
|
|
1019
|
+
f[i] = intField.mul(f[i], d[i]);
|
|
1020
|
+
return f;
|
|
1021
|
+
},
|
|
1022
|
+
div(f, d) {
|
|
1023
|
+
for (let i = 0; i < n; i++)
|
|
1024
|
+
f[i] = intField.div(f[i], d[i]);
|
|
1025
|
+
this.intt(f);
|
|
1026
|
+
return f;
|
|
1027
|
+
},
|
|
1028
|
+
};
|
|
1029
|
+
return { newPoly, intPoly, signedCoder };
|
|
1030
|
+
}
|
|
1031
|
+
// Falcon's JS binary64 complex field wrapper for FFT/sampler paths. Current uses are the
|
|
1032
|
+
// ordinary finite-number operations add/sub/neg/mul/conj/scale/magSqSum; the inherited wider API
|
|
1033
|
+
// exists because getComplex() exposes it, not because all methods are relied on by Falcon today.
|
|
1034
|
+
const fComplex = getComplex({
|
|
1035
|
+
ZERO: 0,
|
|
1036
|
+
ONE: 1,
|
|
1037
|
+
add: (x, y) => x + y,
|
|
1038
|
+
sub: (x, y) => x - y,
|
|
1039
|
+
mul: (x, y) => x * y,
|
|
1040
|
+
div: (x, y) => x / y,
|
|
1041
|
+
eql: (x, y) => x === y,
|
|
1042
|
+
inv: (x) => 1 / x,
|
|
1043
|
+
neg: (x) => -x,
|
|
1044
|
+
});
|
|
1045
|
+
// Detached object copy of the exact round-3 / PQClean fpr_gm_tab payload in its original order.
|
|
1046
|
+
const COMPLEX_ROOTS_O = ComplexArrInterleaved.decode(COMPLEX_ROOTS);
|
|
1047
|
+
// Re-map roots into the local forward FFTCore schedule
|
|
1048
|
+
// `{ dit: false, invertButterflies: true, brp: false }`.
|
|
1049
|
+
// Index 0 stays intentionally unused because FFTCore's forward group counter starts at 1.
|
|
1050
|
+
const FFTCoreRoots = {};
|
|
1051
|
+
// Inverse FFTCore reads roots as `N - grp`, so fill this table from the end and store `-conj(root)`
|
|
1052
|
+
// rather than plain conjugates
|
|
1053
|
+
// to match Falcon's split/iFFT sign convention under the local butterfly.
|
|
1054
|
+
const FFTCoreRootsConj = {};
|
|
1055
|
+
for (let logn = 0; logn < 10; logn++) {
|
|
1056
|
+
const out = new Array(1 << logn);
|
|
1057
|
+
const outC = new Array(1 << logn);
|
|
1058
|
+
for (let i = 0, g1 = 1, g2 = 1; i < logn; i++) {
|
|
1059
|
+
const ng = 1 << i;
|
|
1060
|
+
for (let k = 0; k < ng; k++)
|
|
1061
|
+
out[g1++] = COMPLEX_ROOTS_O[(ng << 1) + k];
|
|
1062
|
+
const ng2 = 1 << (logn - i);
|
|
1063
|
+
for (let k = 0; k < ng2 >> 1; k++)
|
|
1064
|
+
outC[out.length - g2++] = fComplex.neg(fComplex.conj(COMPLEX_ROOTS_O[ng2 + k]));
|
|
1065
|
+
}
|
|
1066
|
+
FFTCoreRoots[logn] = out;
|
|
1067
|
+
FFTCoreRootsConj[logn] = outC;
|
|
1068
|
+
}
|
|
1069
|
+
// Mixed float-poly helper surface: most methods allocate / return fresh values,
|
|
1070
|
+
// but FFT() and iFFT() mutate their CPoly input in place.
|
|
1071
|
+
// Flat Float64Array buffers use ComplexArr's [...re, ...im] layout.
|
|
1072
|
+
function getFloatPoly(logn) {
|
|
1073
|
+
const n = 1 << logn;
|
|
1074
|
+
const N_COMPLEX = n >> 1;
|
|
1075
|
+
const hn = Math.log2(N_COMPLEX);
|
|
1076
|
+
const fftOpts = { N: N_COMPLEX, invertButterflies: true, skipStages: 0, brp: false };
|
|
1077
|
+
const inv = 1.0 / N_COMPLEX;
|
|
1078
|
+
return {
|
|
1079
|
+
to: (f) => ComplexArr.decode(Array.from(f)),
|
|
1080
|
+
from: (f) => new Float64Array(ComplexArr.encode(f)),
|
|
1081
|
+
// Runtime callers also pass HashToPoint's Uint16Array output here;
|
|
1082
|
+
// the implementation only needs a numeric typed-array shape,
|
|
1083
|
+
// even though the local type is narrower.
|
|
1084
|
+
convSmall: (f) => ComplexArr.decode(Array.from(f)),
|
|
1085
|
+
add: (a, b) => a.map((i, j) => fComplex.add(i, b[j])),
|
|
1086
|
+
sub: (a, b) => a.map((i, j) => fComplex.sub(i, b[j])),
|
|
1087
|
+
neg: (a) => a.map((i) => fComplex.neg(i)),
|
|
1088
|
+
mul: (a, b) => a.map((i, j) => fComplex.mul(i, b[j])),
|
|
1089
|
+
conj: (a) => a.map((i) => fComplex.conj(i)),
|
|
1090
|
+
mulConst: (a, x) => a.map((i) => fComplex.scale(i, x)),
|
|
1091
|
+
scaleNorm: (a, b) => a.map((i, j) => fComplex.scale(i, b[j])),
|
|
1092
|
+
invNorm: (a, b) => new Float64Array(a.map((i, j) => 1.0 / fComplex.magSqSum(i, b[j]))),
|
|
1093
|
+
FFT: (f) => FFTCore(fComplex, { ...fftOpts, dit: false, roots: FFTCoreRoots[hn] })(f),
|
|
1094
|
+
iFFT(f) {
|
|
1095
|
+
FFTCore(fComplex, { ...fftOpts, dit: true, roots: FFTCoreRootsConj[hn] })(f);
|
|
1096
|
+
for (let i = 0; i < f.length; i++)
|
|
1097
|
+
f[i] = fComplex.scale(f[i], inv);
|
|
1098
|
+
return f;
|
|
1099
|
+
},
|
|
1100
|
+
};
|
|
1101
|
+
}
|
|
1102
|
+
function ApproxExp(x, ccs) {
|
|
1103
|
+
// Algorithm 13: ApproxExp(x, ccs), (Page 42)
|
|
1104
|
+
// Require: Floating-point values x ∈ [0, ln(2)] and ccs ∈ [0, 1]
|
|
1105
|
+
// Ensure: A floating approximation of ccs · exp(-x); berExp() applies the later 2^63 scaling.
|
|
1106
|
+
// 1: C = [0x00000004741183A3, ...]
|
|
1107
|
+
// 2: y ← C[0] ▷ y and z remain in {0, ..., 2⁶³ - 1} the whole algorithm.
|
|
1108
|
+
// 3: z ← ⌊2⁶³ · x⌋
|
|
1109
|
+
// 4: for i = 1, ..., 12 do
|
|
1110
|
+
// 5: y ← C[i] - (z · y) >> 63 ▷ (z · y) fits in 126 bits, but we only need the top 63 bits
|
|
1111
|
+
// 6: z ← ⌊2⁶³ · ccs⌋
|
|
1112
|
+
// 7: y ← (z · y) >> 63
|
|
1113
|
+
// 8: return y
|
|
1114
|
+
// FACCT / round-3 Falcon's leading 1.0 coefficient is implicit in `return ccs * (1.0 + z * y)`,
|
|
1115
|
+
// so the decimal list below stores the remaining 12 polynomial coefficients only.
|
|
1116
|
+
const ev = [
|
|
1117
|
+
0.99999999999999489297408672428, 0.500000000000019206858326015208,
|
|
1118
|
+
0.166666666666984014666397229121, 0.041666666666110491190622155955,
|
|
1119
|
+
0.008333333327800835146903501993, 0.001388888894063186997887560103,
|
|
1120
|
+
0.000198412739277311890541063977, 0.000024801566833585381209939524,
|
|
1121
|
+
0.000002755586350219122514855659, 0.000000275607356160477811864927,
|
|
1122
|
+
0.000000025299506379442070029551, 0.000000002073772366009083061987,
|
|
1123
|
+
];
|
|
1124
|
+
const y = -x;
|
|
1125
|
+
let z = ev[ev.length - 1];
|
|
1126
|
+
for (let i = ev.length - 2; i >= 0; i--)
|
|
1127
|
+
z = z * y + ev[i];
|
|
1128
|
+
return ccs * (1.0 + z * y);
|
|
1129
|
+
}
|
|
1130
|
+
function genFalcon(opts) {
|
|
1131
|
+
const { N } = opts;
|
|
1132
|
+
const logn = Math.log2(N);
|
|
1133
|
+
const id = (n) => n;
|
|
1134
|
+
const { newPoly, intPoly, signedCoder } = getIntPoly(logn);
|
|
1135
|
+
const floatPoly = getFloatPoly(logn);
|
|
1136
|
+
// Kinda like FFT Sampler: single function, but a lot of private deps and internal rng stake
|
|
1137
|
+
class NTRU {
|
|
1138
|
+
logn;
|
|
1139
|
+
shake;
|
|
1140
|
+
constructor(logn, seed) {
|
|
1141
|
+
this.logn = logn;
|
|
1142
|
+
this.shake = shake256.create().update(seed);
|
|
1143
|
+
}
|
|
1144
|
+
gaussSingle() {
|
|
1145
|
+
const g = 1 << (10 - this.logn);
|
|
1146
|
+
let val = 0;
|
|
1147
|
+
for (let i = 0; i < g; i++) {
|
|
1148
|
+
const r128 = bytesToNumberLE(this.shake.xof(16));
|
|
1149
|
+
const r1 = r128 & 0x7fffffffffffffffn;
|
|
1150
|
+
const r2 = (r128 >> 64n) & 0x7fffffffffffffffn;
|
|
1151
|
+
const sign = Number((r128 >> 63n) & 1n);
|
|
1152
|
+
let f = r1 < gauss_1024_12289[0] ? 1 : 0;
|
|
1153
|
+
let v = 0;
|
|
1154
|
+
for (let k = 1; k < gauss_1024_12289.length; k++) {
|
|
1155
|
+
const tBit = r2 >= gauss_1024_12289[k] ? 1 : 0;
|
|
1156
|
+
v |= k & -(tBit & (f ^ 1));
|
|
1157
|
+
f |= tBit;
|
|
1158
|
+
}
|
|
1159
|
+
val += sign === 1 ? -v : v;
|
|
1160
|
+
}
|
|
1161
|
+
return val;
|
|
1162
|
+
}
|
|
1163
|
+
polyGauss() {
|
|
1164
|
+
const n = 1 << this.logn;
|
|
1165
|
+
let mod2 = 0; // xor sum of previous elements
|
|
1166
|
+
const f = new Int8Array(n);
|
|
1167
|
+
for (let u = 0; u < n; u++) {
|
|
1168
|
+
let s;
|
|
1169
|
+
while (true) {
|
|
1170
|
+
s = this.gaussSingle();
|
|
1171
|
+
if (s < -127 || s > 127)
|
|
1172
|
+
continue;
|
|
1173
|
+
if (u === n - 1)
|
|
1174
|
+
if ((mod2 ^ (s & 1)) === 0)
|
|
1175
|
+
continue;
|
|
1176
|
+
break;
|
|
1177
|
+
}
|
|
1178
|
+
if (u < n - 1)
|
|
1179
|
+
mod2 ^= s & 1;
|
|
1180
|
+
f[u] = s;
|
|
1181
|
+
}
|
|
1182
|
+
return f;
|
|
1183
|
+
}
|
|
1184
|
+
galoisNorm(logn, a) {
|
|
1185
|
+
const n = 1 << logn;
|
|
1186
|
+
const d = new Array(n >> 1);
|
|
1187
|
+
for (let k = 0; k < n; k += 2) {
|
|
1188
|
+
let s = 0n;
|
|
1189
|
+
for (let i = 0; i <= k; i += 2)
|
|
1190
|
+
s += a[i] * a[k - i];
|
|
1191
|
+
for (let i = k + 2; i < n; i += 2)
|
|
1192
|
+
s -= a[i] * a[k + n - i];
|
|
1193
|
+
d[k >>> 1] = s;
|
|
1194
|
+
}
|
|
1195
|
+
for (let k = 0; k < n; k += 2) {
|
|
1196
|
+
let s = 0n;
|
|
1197
|
+
for (let i = 1; i < k; i += 2)
|
|
1198
|
+
s += a[i] * a[k - i];
|
|
1199
|
+
for (let i = k + 1; i < n; i += 2)
|
|
1200
|
+
s -= a[i] * a[k + n - i];
|
|
1201
|
+
d[k >>> 1] -= s;
|
|
1202
|
+
}
|
|
1203
|
+
return d;
|
|
1204
|
+
}
|
|
1205
|
+
mulConjD(logn, d, a, b) {
|
|
1206
|
+
const n = 1 << logn;
|
|
1207
|
+
for (let k = 0; k < n; k++) {
|
|
1208
|
+
let s = 0n;
|
|
1209
|
+
for (let i = 0; i <= k; i += 2)
|
|
1210
|
+
s += b[i >>> 1] * a[k - i];
|
|
1211
|
+
for (let i = k + 2 - (k & 1); i < n; i += 2)
|
|
1212
|
+
s -= b[i >>> 1] * a[k + n - i];
|
|
1213
|
+
if ((k & 1) === 0)
|
|
1214
|
+
d[k] = s;
|
|
1215
|
+
else
|
|
1216
|
+
d[k] = -s;
|
|
1217
|
+
}
|
|
1218
|
+
return d;
|
|
1219
|
+
}
|
|
1220
|
+
subMul(logn, a, b, c, e) {
|
|
1221
|
+
const n = 1 << logn;
|
|
1222
|
+
for (let k = 0; k < n; k++) {
|
|
1223
|
+
let s = 0n;
|
|
1224
|
+
for (let i = 0; i <= k; i++)
|
|
1225
|
+
s += b[i] * c[k - i];
|
|
1226
|
+
for (let i = k + 1; i < n; i++)
|
|
1227
|
+
s -= b[i] * c[k + n - i];
|
|
1228
|
+
a[k] -= s << e;
|
|
1229
|
+
}
|
|
1230
|
+
return a;
|
|
1231
|
+
}
|
|
1232
|
+
reduce(logn, f, g, F, G, logn_top) {
|
|
1233
|
+
// Algorithm 7: Reduce(f, g, F, G)
|
|
1234
|
+
// (Page 35)
|
|
1235
|
+
// Require: Polynomials f, g, F, G ∈ Z[x]/(φ)
|
|
1236
|
+
// Ensure: (F, G) is reduced with respect to (f, g)
|
|
1237
|
+
// 1: do
|
|
1238
|
+
// 2: k ← ⌊(Ff*+Gg*)/(ff*+gg*)⌋ ▷ (Ff*+Gg*)/(ff*+gg*) ∈ Q[x]/(φ) and k ∈ Z[x]/(φ)
|
|
1239
|
+
// 3: F ← F - kf
|
|
1240
|
+
// 4: G ← G - kg
|
|
1241
|
+
// 5: while k ≠ 0
|
|
1242
|
+
// ▷ Multiple iterations may be needed, e.g. if k is computed in small precision.
|
|
1243
|
+
const n = 1 << logn;
|
|
1244
|
+
const depth = logn_top - logn;
|
|
1245
|
+
const floatPoly = getFloatPoly(logn);
|
|
1246
|
+
const slen = MAX_BL_SMALL[depth];
|
|
1247
|
+
const llen = MAX_BL_LARGE[depth];
|
|
1248
|
+
let maxFGBits = BigInt(31 * llen);
|
|
1249
|
+
let FGlen = BigInt(llen);
|
|
1250
|
+
const scalefg = BigInt(31 * (slen - 10));
|
|
1251
|
+
const fgMaxBits = BITLENGTH[depth].avg + 6 * BITLENGTH[depth].std;
|
|
1252
|
+
const fgMinBits = BITLENGTH[depth].avg - 6 * BITLENGTH[depth].std;
|
|
1253
|
+
let scaleK = BigInt(Math.round(31 * llen - fgMinBits));
|
|
1254
|
+
let fx = new Float64Array(n);
|
|
1255
|
+
let gx = new Float64Array(n);
|
|
1256
|
+
for (let i = 0; i < n; i++) {
|
|
1257
|
+
fx[i] = Number(f[i] >> scalefg);
|
|
1258
|
+
gx[i] = Number(g[i] >> scalefg);
|
|
1259
|
+
}
|
|
1260
|
+
const rt3 = floatPoly.conj(floatPoly.FFT(floatPoly.to(fx)));
|
|
1261
|
+
const rt4 = floatPoly.conj(floatPoly.FFT(floatPoly.to(gx)));
|
|
1262
|
+
const rt5 = floatPoly.invNorm(rt3, rt4);
|
|
1263
|
+
const Fx = new Float64Array(n);
|
|
1264
|
+
const Gx = new Float64Array(n);
|
|
1265
|
+
const k = new Array(n);
|
|
1266
|
+
while (true) {
|
|
1267
|
+
let scaleFG = 31n * (FGlen - 10n);
|
|
1268
|
+
for (let i = 0; i < n; i++) {
|
|
1269
|
+
Fx[i] = Number(F[i] >> scaleFG);
|
|
1270
|
+
Gx[i] = Number(G[i] >> scaleFG);
|
|
1271
|
+
}
|
|
1272
|
+
const rt2 = floatPoly.mul(floatPoly.FFT(floatPoly.to(Gx)), rt4);
|
|
1273
|
+
const rt1 = floatPoly.mul(floatPoly.FFT(floatPoly.to(Fx)), rt3);
|
|
1274
|
+
// convert to float64array
|
|
1275
|
+
const rt2f = floatPoly.from(floatPoly.iFFT(floatPoly.scaleNorm(floatPoly.add(rt2, rt1), rt5)));
|
|
1276
|
+
const pdc = 2 ** Number(scaleFG - scalefg - scaleK);
|
|
1277
|
+
for (let i = 0; i < n; i++) {
|
|
1278
|
+
const BOUND = 2147483647.0;
|
|
1279
|
+
const val = rt2f[i] * pdc;
|
|
1280
|
+
if (val <= -BOUND || val >= BOUND)
|
|
1281
|
+
return false;
|
|
1282
|
+
k[i] = BigInt(Math.round(val));
|
|
1283
|
+
}
|
|
1284
|
+
F = this.subMul(logn, F, f, k, scaleK); // 3: F ← F - kf
|
|
1285
|
+
G = this.subMul(logn, G, g, k, scaleK); // 4: G ← G - kg
|
|
1286
|
+
const maxfgNew = scaleK + BigInt(Math.round(fgMaxBits)) + 10n;
|
|
1287
|
+
if (maxfgNew < maxFGBits)
|
|
1288
|
+
maxFGBits = maxfgNew;
|
|
1289
|
+
if (FGlen > 1n && FGlen * 31n >= maxFGBits + 31n)
|
|
1290
|
+
FGlen--;
|
|
1291
|
+
if (scaleK <= 0n)
|
|
1292
|
+
break;
|
|
1293
|
+
scaleK -= 25n;
|
|
1294
|
+
if (scaleK < 0n)
|
|
1295
|
+
scaleK = 0n;
|
|
1296
|
+
}
|
|
1297
|
+
return true;
|
|
1298
|
+
}
|
|
1299
|
+
// This is recursive thing that goes from logn to 0
|
|
1300
|
+
solveBranch(logn, f, g, F, G, logn_top) {
|
|
1301
|
+
// Algorithm 6: NTRUSolve_{n,q}(f, g), (Page 35)
|
|
1302
|
+
// Require: f, g ∈ Z[x]/(xⁿ + 1) with n a power of two
|
|
1303
|
+
// Ensure: Polynomials F, G such that (3.15) is verified
|
|
1304
|
+
// 1: if n = 1 then
|
|
1305
|
+
// 2: Compute u, v ∈ Z such that uf - vg = gcd(f, g) ▷ Using the extended GCD
|
|
1306
|
+
// 3: if gcd(f, g) ≠ 1 then
|
|
1307
|
+
// 4: abort and return ⊥
|
|
1308
|
+
// 5: (F, G) ← (vq, uq)
|
|
1309
|
+
// 6: return (F, G)
|
|
1310
|
+
// 7: else
|
|
1311
|
+
// 8: f' ← N(f) ▷ f', g', F', G' ∈ Z[x]/(x^{n/2} + 1)
|
|
1312
|
+
// 9: g' ← N(g) ▷ N as defined in either (3.25) or (3.26)
|
|
1313
|
+
// 10: (F', G') ← NTRUSolve_{n/2,q}(f', g') ▷ Recursive call
|
|
1314
|
+
// 11: F ← F'(x²)g(-x) ▷ F, G ∈ Z[x]/(xⁿ + 1)
|
|
1315
|
+
// 12: G ← G'(x²)f(-x)
|
|
1316
|
+
// 13: Reduce(f, g, F, G) ▷ (F, G) is reduced with respect to (f, g)
|
|
1317
|
+
// 14: return (F, G)
|
|
1318
|
+
if (logn === 0) {
|
|
1319
|
+
// // 1: if n = 1 then
|
|
1320
|
+
const xf = f[0];
|
|
1321
|
+
const xg = g[0];
|
|
1322
|
+
// We can rely on 'invert' to throw if they are not coprime.
|
|
1323
|
+
if (xf <= 0n || xg <= 0n)
|
|
1324
|
+
return false;
|
|
1325
|
+
try {
|
|
1326
|
+
const u1 = invert(xf, xg); // if gcd(f, g) ≠ 1 then
|
|
1327
|
+
const v1 = (1n - u1 * xf) / xg;
|
|
1328
|
+
F[0] = -v1 * QBig; // 5: (F, G) ← (vq, uq)
|
|
1329
|
+
G[0] = u1 * QBig;
|
|
1330
|
+
return true;
|
|
1331
|
+
}
|
|
1332
|
+
catch (e) {
|
|
1333
|
+
return false;
|
|
1334
|
+
}
|
|
1335
|
+
}
|
|
1336
|
+
if (logn_top === undefined)
|
|
1337
|
+
logn_top = logn;
|
|
1338
|
+
const n = 1 << logn;
|
|
1339
|
+
const hn = n >>> 1;
|
|
1340
|
+
if (!f || f.length < n || !g || g.length < n)
|
|
1341
|
+
return false;
|
|
1342
|
+
const fp = this.galoisNorm(logn, f); // 8: f' ← N(f)
|
|
1343
|
+
const gp = this.galoisNorm(logn, g); // 9: g' ← N(g)
|
|
1344
|
+
const Fp = new Array(hn); // 10: (F', G') ← NTRUSolve_{n/2,q}(f', g')
|
|
1345
|
+
const Gp = new Array(hn);
|
|
1346
|
+
// 10: (F', G') ← NTRUSolve_{n/2,q}(f', g')
|
|
1347
|
+
// ▷ Recursive call
|
|
1348
|
+
if (!this.solveBranch(logn - 1, fp, gp, Fp, Gp, logn_top))
|
|
1349
|
+
return false;
|
|
1350
|
+
F = this.mulConjD(logn, F, g, Fp); // 11: F ← F'(x²)g(-x)
|
|
1351
|
+
G = this.mulConjD(logn, G, f, Gp); // 12: G ← G'(x²)f(-x)
|
|
1352
|
+
// 13: Reduce(f, g, F, G)
|
|
1353
|
+
// ▷ (F, G) is reduced with respect to (f, g)
|
|
1354
|
+
return this.reduce(logn, f, g, F, G, logn_top);
|
|
1355
|
+
}
|
|
1356
|
+
solve(f, g) {
|
|
1357
|
+
// Algorithm 5: NTRUGen(φ, q)
|
|
1358
|
+
// (Page 34)
|
|
1359
|
+
// Require: A monic polynomial φ ∈ Z[x] of degree n, a modulus q
|
|
1360
|
+
// Ensure: Polynomials f, g, F, G
|
|
1361
|
+
// 1: σ{f,g} ← 1.17√q/2n ▷ σ{f,g} is chosen so that E[||(f, g)||] = 1.17√q
|
|
1362
|
+
// 2: for i from 0 to n-1 do
|
|
1363
|
+
// 3: fᵢ ← DZ,σ{f,g},0 ▷ See also (3.29)
|
|
1364
|
+
// 4: gᵢ ← DZ,σ{f,g},0
|
|
1365
|
+
// 5: f ← Σᵢ fᵢxⁱ ▷ f ∈ Z[x]/(φ)
|
|
1366
|
+
// 6: g ← Σᵢ gᵢxⁱ ▷ g ∈ Z[x]/(φ)
|
|
1367
|
+
// 7: if NTT(f) contains 0 as a coefficient then ▷ Check that f is invertible mod q
|
|
1368
|
+
// 8: restart
|
|
1369
|
+
// 9: γ ← max{||(g, -f)||, ||( (qf*)/(ff*+gg*), (qg*)/(ff*+gg*) )||}
|
|
1370
|
+
// ▷ Using (3.9) with (3.8) or (3.10)
|
|
1371
|
+
// 10: if γ > 1.17√q then ▷ Check that γ = ||B||_GS is short
|
|
1372
|
+
// 11: restart
|
|
1373
|
+
// 12: F, G ← NTRUSolve_{n,q}(f, g) ▷ Computing F, G such that fG - gF = q mod φ
|
|
1374
|
+
// 13: if (F, G) = ⊥ then
|
|
1375
|
+
// 14: restart
|
|
1376
|
+
// 15: return f, g, F, G
|
|
1377
|
+
const n = 1 << logn;
|
|
1378
|
+
const bf = Array.from(f).map(BigInt);
|
|
1379
|
+
const bg = Array.from(g).map(BigInt);
|
|
1380
|
+
const bF = new Array(n);
|
|
1381
|
+
const bG = new Array(n);
|
|
1382
|
+
// 12: F, G ← NTRUSolve_{n,q}(f, g)
|
|
1383
|
+
// ▷ Computing F, G such that fG - gF = q mod φ
|
|
1384
|
+
if (!this.solveBranch(logn, bf, bg, bF, bG))
|
|
1385
|
+
return false;
|
|
1386
|
+
const F = new Int8Array(n);
|
|
1387
|
+
const G = new Int8Array(n);
|
|
1388
|
+
for (let i = 0; i < n; i++) {
|
|
1389
|
+
const x = bF[i];
|
|
1390
|
+
const y = bG[i];
|
|
1391
|
+
if (x < -127 || x > +127 || y < -127 || y > +127)
|
|
1392
|
+
return false;
|
|
1393
|
+
F[i] = Number(x);
|
|
1394
|
+
G[i] = Number(y);
|
|
1395
|
+
}
|
|
1396
|
+
return [F, G];
|
|
1397
|
+
}
|
|
1398
|
+
generate() {
|
|
1399
|
+
// Algorithm 4: Keygen(φ, q)
|
|
1400
|
+
// (Page 33)
|
|
1401
|
+
// Require: A monic polynomial φ ∈ Z[x], a modulus q
|
|
1402
|
+
// Ensure: A secret key sk, a public key pk
|
|
1403
|
+
// 1: f, g, F, G ← NTRUGen(φ, q) ▷ Solving the NTRU equation
|
|
1404
|
+
// 2: B ← [ g -f ; G -F ]
|
|
1405
|
+
// 3: B̂ ← FFT(B) ▷ Compute the FFT for each of the 4 components {g, -f, G, -F}
|
|
1406
|
+
// 4: G ← B̂ × B̂*
|
|
1407
|
+
// 5: T ← ffLDL*(G) ▷ Computing the LDL* tree
|
|
1408
|
+
// 6: for each leaf leaf of T do
|
|
1409
|
+
// 7: leaf.value ← σ/√leaf.value ▷ Normalization step
|
|
1410
|
+
// 8: sk ← (B̂, T)
|
|
1411
|
+
// 9: h ← gf⁻¹ mod q
|
|
1412
|
+
// 10: pk ← h
|
|
1413
|
+
// 11: return sk, pk
|
|
1414
|
+
let max = 1_000_000;
|
|
1415
|
+
let curr = 0;
|
|
1416
|
+
while (true) {
|
|
1417
|
+
if (curr++ === max)
|
|
1418
|
+
throw new Error("can't generate key");
|
|
1419
|
+
const f = this.polyGauss();
|
|
1420
|
+
const g = this.polyGauss();
|
|
1421
|
+
let lim = 1 << (opts.fgBits - 1);
|
|
1422
|
+
for (let u = 0; u < N; u++) {
|
|
1423
|
+
if (f[u] >= lim || f[u] <= -lim || g[u] >= lim || g[u] <= -lim) {
|
|
1424
|
+
lim = -1;
|
|
1425
|
+
break;
|
|
1426
|
+
}
|
|
1427
|
+
}
|
|
1428
|
+
if (lim < 0)
|
|
1429
|
+
continue;
|
|
1430
|
+
const normf = intPoly.smallSqnorm(f);
|
|
1431
|
+
const normg = intPoly.smallSqnorm(g);
|
|
1432
|
+
const norm = (normf + normg) | -((normf | normg) >>> 31);
|
|
1433
|
+
// Cheap integer prefilter for the same 1.17^2*q Gram-Schmidt bound;
|
|
1434
|
+
// ceil(BNORM_MAX) = 16823.
|
|
1435
|
+
if (norm >= 16823)
|
|
1436
|
+
continue;
|
|
1437
|
+
let rt1 = floatPoly.FFT(floatPoly.convSmall(f));
|
|
1438
|
+
let rt2 = floatPoly.FFT(floatPoly.convSmall(g));
|
|
1439
|
+
const rt3 = floatPoly.invNorm(rt1, rt2);
|
|
1440
|
+
rt1 = floatPoly.iFFT(floatPoly.scaleNorm(floatPoly.mulConst(floatPoly.conj(rt1), Q), rt3));
|
|
1441
|
+
rt2 = floatPoly.iFFT(floatPoly.scaleNorm(floatPoly.mulConst(floatPoly.conj(rt2), Q), rt3));
|
|
1442
|
+
// Separate reals and then imaginary to enforce numerical stability
|
|
1443
|
+
let bnorm = 0;
|
|
1444
|
+
for (let u = 0; u < rt1.length; u++) {
|
|
1445
|
+
bnorm += rt1[u].re * rt1[u].re;
|
|
1446
|
+
bnorm += rt2[u].re * rt2[u].re;
|
|
1447
|
+
}
|
|
1448
|
+
for (let u = 0; u < rt1.length; u++) {
|
|
1449
|
+
bnorm += rt1[u].im * rt1[u].im;
|
|
1450
|
+
bnorm += rt2[u].im * rt2[u].im;
|
|
1451
|
+
}
|
|
1452
|
+
if (!(bnorm < BNORM_MAX))
|
|
1453
|
+
continue;
|
|
1454
|
+
let pub;
|
|
1455
|
+
try {
|
|
1456
|
+
pub = computePublic(f, g);
|
|
1457
|
+
}
|
|
1458
|
+
catch (_) {
|
|
1459
|
+
continue;
|
|
1460
|
+
}
|
|
1461
|
+
const solved = this.solve(f, g);
|
|
1462
|
+
if (solved === false)
|
|
1463
|
+
continue;
|
|
1464
|
+
return [f, g, solved[0], solved[1], pub]; // f g F G h
|
|
1465
|
+
}
|
|
1466
|
+
}
|
|
1467
|
+
}
|
|
1468
|
+
// same as ml-dsa id, but MSB bits :(
|
|
1469
|
+
const modqCoder = () => {
|
|
1470
|
+
const coder = bitsCoderMSB(newPoly, N, 14, {
|
|
1471
|
+
encode: id,
|
|
1472
|
+
decode: id,
|
|
1473
|
+
});
|
|
1474
|
+
return {
|
|
1475
|
+
bytesLen: coder.bytesLen,
|
|
1476
|
+
encode(poly) {
|
|
1477
|
+
// Keep these raw checks in sync with Q:
|
|
1478
|
+
// Falcon public-key coefficients must stay in [0, q - 1].
|
|
1479
|
+
for (let i = 0; i < poly.length; i++)
|
|
1480
|
+
if (poly[i] >= 12289)
|
|
1481
|
+
throw new Error('public key coeff out of range');
|
|
1482
|
+
return coder.encode(poly);
|
|
1483
|
+
},
|
|
1484
|
+
decode(bytes) {
|
|
1485
|
+
// Round-3 Falcon requires exact body length here;
|
|
1486
|
+
// otherwise truncated keys decode as zero-padded
|
|
1487
|
+
// and overlong keys silently ignore the tail in this generic bit decoder.
|
|
1488
|
+
if (bytes.length !== coder.bytesLen)
|
|
1489
|
+
throw new Error('wrong public key length');
|
|
1490
|
+
const poly = coder.decode(bytes);
|
|
1491
|
+
// Keep these raw checks in sync with Q:
|
|
1492
|
+
// Falcon public-key coefficients must stay in [0, q - 1].
|
|
1493
|
+
for (let i = 0; i < poly.length; i++)
|
|
1494
|
+
if (poly[i] >= 12289)
|
|
1495
|
+
throw new Error('public key coeff out of range');
|
|
1496
|
+
const normalized = coder.encode(poly);
|
|
1497
|
+
if (normalized.length !== bytes.length)
|
|
1498
|
+
throw new Error('wrong public key length');
|
|
1499
|
+
for (let i = 0; i < bytes.length; i++)
|
|
1500
|
+
if (bytes[i] !== normalized[i])
|
|
1501
|
+
throw new Error('wrong public key encoding');
|
|
1502
|
+
return poly;
|
|
1503
|
+
},
|
|
1504
|
+
};
|
|
1505
|
+
};
|
|
1506
|
+
const trimI8Coder = (bits) => {
|
|
1507
|
+
const shift = 32 - bits;
|
|
1508
|
+
const coder = bitsCoderMSB((len) => new Int8Array(len), N, bits, {
|
|
1509
|
+
encode: (v) => v & ((1 << bits) - 1),
|
|
1510
|
+
decode: (w) => ((w & getMask(bits)) << shift) >> shift,
|
|
1511
|
+
});
|
|
1512
|
+
return {
|
|
1513
|
+
bytesLen: coder.bytesLen,
|
|
1514
|
+
encode(poly) {
|
|
1515
|
+
// Secret-key trim encodings keep a symmetric signed range and reserve the most-negative
|
|
1516
|
+
// value as a non-canonical sentinel,
|
|
1517
|
+
// so encode() and decode() intentionally use different bounds.
|
|
1518
|
+
const max = (1 << (bits - 1)) - 1;
|
|
1519
|
+
const min = -max;
|
|
1520
|
+
for (let i = 0; i < poly.length; i++)
|
|
1521
|
+
if (poly[i] < min || poly[i] > max)
|
|
1522
|
+
throw new Error('private key coeff out of range');
|
|
1523
|
+
return coder.encode(poly);
|
|
1524
|
+
},
|
|
1525
|
+
decode(bytes) {
|
|
1526
|
+
const poly = coder.decode(bytes);
|
|
1527
|
+
const min = -(1 << (bits - 1));
|
|
1528
|
+
for (let i = 0; i < poly.length; i++)
|
|
1529
|
+
if (poly[i] === min)
|
|
1530
|
+
throw new Error('forbidden private key coeff');
|
|
1531
|
+
return poly;
|
|
1532
|
+
},
|
|
1533
|
+
};
|
|
1534
|
+
};
|
|
1535
|
+
const fgCoder = trimI8Coder(opts.fgBits);
|
|
1536
|
+
const FGCoder = trimI8Coder(opts.FGBits);
|
|
1537
|
+
// Current utils.splitCoder requires a label first;
|
|
1538
|
+
// without it Falcon key/sig encodings drift and KATs fail.
|
|
1539
|
+
// 0x50 + logn || f || g || F
|
|
1540
|
+
const secretKeyCoder = headerCoder(0x50 + logn, splitCoder('falcon.secretKey', fgCoder, fgCoder, FGCoder));
|
|
1541
|
+
const publicKeyCoder = headerCoder(0x00 + logn, modqCoder());
|
|
1542
|
+
const decodePaddedSig = (s2) => {
|
|
1543
|
+
// The fixed padded form accepts only a canonical compressed payload
|
|
1544
|
+
// followed by an all-zero tail.
|
|
1545
|
+
const normalized = compCoder(N).encode(compCoder(N).decode(s2));
|
|
1546
|
+
for (let i = normalized.length; i < s2.length; i++)
|
|
1547
|
+
if (s2[i] !== 0)
|
|
1548
|
+
throw new Error('non-zero padding');
|
|
1549
|
+
return normalized;
|
|
1550
|
+
};
|
|
1551
|
+
const decodeUnpaddedSig = (s2) => {
|
|
1552
|
+
// Unpadded attached and detached signatures require the compressed payload to use its exact
|
|
1553
|
+
// canonical bitlength. Appending a zero tail and adjusting the outer container length must
|
|
1554
|
+
// still be rejected.
|
|
1555
|
+
const normalized = compCoder(N).encode(compCoder(N).decode(s2));
|
|
1556
|
+
if (normalized.length !== s2.length)
|
|
1557
|
+
throw new Error('wrong signature length');
|
|
1558
|
+
return s2;
|
|
1559
|
+
};
|
|
1560
|
+
const decodeSig = opts.padded ? decodePaddedSig : decodeUnpaddedSig;
|
|
1561
|
+
// Unpadded: [ 2B sig_len ] [ 40B nonce ] [ message ] [ 1B header ] [ compressed_sig ]
|
|
1562
|
+
// Padded [ 1B header ] [ 40B nonce ] [ compressed_sig ] [ padding ] | [ message ]
|
|
1563
|
+
const SignatureCoderBasic = (logn) => {
|
|
1564
|
+
const TYPE_BYTE = 0x20 + logn;
|
|
1565
|
+
return {
|
|
1566
|
+
encode({ msg, nonce, s2 }) {
|
|
1567
|
+
let compressed = s2;
|
|
1568
|
+
const payloadLen = 1 + compressed.length;
|
|
1569
|
+
const totalLen = 2 + NONCELEN + msg.length + payloadLen;
|
|
1570
|
+
const out = new Uint8Array(totalLen);
|
|
1571
|
+
let i = 0;
|
|
1572
|
+
out[i++] = (payloadLen >> 8) & 0xff;
|
|
1573
|
+
out[i++] = payloadLen & 0xff;
|
|
1574
|
+
out.set(nonce, i);
|
|
1575
|
+
i += NONCELEN;
|
|
1576
|
+
out.set(msg, i);
|
|
1577
|
+
i += msg.length;
|
|
1578
|
+
out[i++] = TYPE_BYTE;
|
|
1579
|
+
out.set(compressed, i);
|
|
1580
|
+
return out;
|
|
1581
|
+
},
|
|
1582
|
+
decode(data) {
|
|
1583
|
+
if (!data || data.length < NONCELEN + 3)
|
|
1584
|
+
throw new Error('signature coder: wrong length');
|
|
1585
|
+
const len = (data[0] << 8) | data[1];
|
|
1586
|
+
const s2Len = len - 1;
|
|
1587
|
+
const msgLen = data.length - NONCELEN - 3 - s2Len;
|
|
1588
|
+
if (msgLen < 0)
|
|
1589
|
+
throw new Error('signature coder: wrong msg length');
|
|
1590
|
+
const typeByte = data[2 + NONCELEN + msgLen];
|
|
1591
|
+
if (typeByte !== TYPE_BYTE)
|
|
1592
|
+
throw new Error('signature coder: wrong type byte');
|
|
1593
|
+
const nonce = data.subarray(2, 2 + NONCELEN);
|
|
1594
|
+
const msg = data.subarray(2 + NONCELEN, 2 + NONCELEN + msgLen);
|
|
1595
|
+
const s2 = decodeUnpaddedSig(data.subarray(2 + NONCELEN + msgLen + 1));
|
|
1596
|
+
if (s2.length !== s2Len)
|
|
1597
|
+
throw new Error('signature coder: wrong s2 length');
|
|
1598
|
+
return { msg, nonce, s2 };
|
|
1599
|
+
},
|
|
1600
|
+
};
|
|
1601
|
+
};
|
|
1602
|
+
const SignatureCoderPadded = (logn) => {
|
|
1603
|
+
const sigLen = opts.paddedLen;
|
|
1604
|
+
return {
|
|
1605
|
+
encode({ msg, nonce, s2 }) {
|
|
1606
|
+
return headerCoder(0x30 + logn, splitCoder('falcon.signature', NONCELEN, sigLen, msg.length)).encode([nonce, pad(sigLen).encode(s2), msg]);
|
|
1607
|
+
},
|
|
1608
|
+
decode(data) {
|
|
1609
|
+
const msgLen = data.length - NONCELEN - sigLen - 1;
|
|
1610
|
+
const [nonce, s2, msg] = headerCoder(0x30 + logn, splitCoder('falcon.signature', NONCELEN, sigLen, msgLen)).decode(data);
|
|
1611
|
+
return { nonce, s2: decodeSig(s2), msg };
|
|
1612
|
+
},
|
|
1613
|
+
};
|
|
1614
|
+
};
|
|
1615
|
+
// [ 1B header ] [ 40B nonce ] [ compressed_sig ]
|
|
1616
|
+
const SignatureCoderDetached = (logn) => {
|
|
1617
|
+
const sigLen = opts.padded ? opts.sigLen - 1 - NONCELEN : opts.detachedLen;
|
|
1618
|
+
const getSigLen = (s2) => (opts.padded ? sigLen : s2.length);
|
|
1619
|
+
return {
|
|
1620
|
+
encode({ nonce, s2 }) {
|
|
1621
|
+
return headerCoder(0x30 + logn, splitCoder('falcon.detachedSignature', NONCELEN, getSigLen(s2))).encode([nonce, opts.padded ? pad(sigLen).encode(s2) : s2]);
|
|
1622
|
+
},
|
|
1623
|
+
decode(data) {
|
|
1624
|
+
const [nonce, raw] = headerCoder(0x30 + logn, splitCoder('falcon.detachedSignature', NONCELEN, data.length - NONCELEN - 1)).decode(data);
|
|
1625
|
+
const s2 = decodeSig(raw);
|
|
1626
|
+
return { nonce, s2 };
|
|
1627
|
+
},
|
|
1628
|
+
};
|
|
1629
|
+
};
|
|
1630
|
+
const SignatureCoder = (opts.padded ? SignatureCoderPadded : SignatureCoderBasic)(logn);
|
|
1631
|
+
// Round-3 Falcon rejects non-invertible f before division;
|
|
1632
|
+
// otherwise malformed secret keys leak a raw arithmetic error.
|
|
1633
|
+
// Returns NTT(f) after the nonzero-lane check;
|
|
1634
|
+
// callers still apply f^{-1} via coefficient-wise division.
|
|
1635
|
+
const invertF = (f) => {
|
|
1636
|
+
const tt = intPoly.ntt(signedCoder.decode(f));
|
|
1637
|
+
for (let u = 0; u < N; u++)
|
|
1638
|
+
if (tt[u] === 0)
|
|
1639
|
+
throw new Error('invalid secretKey: non-invertible f');
|
|
1640
|
+
return tt;
|
|
1641
|
+
};
|
|
1642
|
+
function computePublic(f, g) {
|
|
1643
|
+
const tt = invertF(f);
|
|
1644
|
+
const h = intPoly.ntt(signedCoder.decode(g));
|
|
1645
|
+
// intPoly.div() returns to coefficient form via intt(), so public keys are encoded from the
|
|
1646
|
+
// canonical polynomial h = g/f and verifyRaw() re-enters the NTT domain later.
|
|
1647
|
+
const res = intPoly.div(h, tt); // h = g/f
|
|
1648
|
+
cleanBytes(tt);
|
|
1649
|
+
return res;
|
|
1650
|
+
}
|
|
1651
|
+
// Reconstruct the omitted secret-key limb G as g*F/f mod q, then mirror round-3 Falcon's centered
|
|
1652
|
+
// reduction and small-coefficient check before using the completed basis for signing.
|
|
1653
|
+
function completePrivate(f, g, F) {
|
|
1654
|
+
let t1 = intPoly.toMontgomery(intPoly.ntt(signedCoder.decode(g)));
|
|
1655
|
+
const t2 = intPoly.ntt(signedCoder.decode(F));
|
|
1656
|
+
const tt = invertF(f);
|
|
1657
|
+
t1 = intPoly.div(intPoly.mul(t1, t2), tt);
|
|
1658
|
+
const G = new Int8Array(N);
|
|
1659
|
+
for (let u = 0; u < N; u++) {
|
|
1660
|
+
let w = t1[u];
|
|
1661
|
+
// This mirrors round-3 Falcon's secret-key G reconstruction, not a generic centered reduction
|
|
1662
|
+
// helper:
|
|
1663
|
+
// the threshold is floor(q/2), and w = Qhalf maps to -Qhalf - 1 here on purpose.
|
|
1664
|
+
w -= Q & ~-((w - Qhalf) >>> 31);
|
|
1665
|
+
const gi = w | 0;
|
|
1666
|
+
if (gi < -127 || gi > 127) {
|
|
1667
|
+
cleanBytes(t1, t2, tt, G);
|
|
1668
|
+
throw new Error('Coefficient out of bounds');
|
|
1669
|
+
}
|
|
1670
|
+
G[u] = gi;
|
|
1671
|
+
}
|
|
1672
|
+
cleanBytes(t1, t2, tt);
|
|
1673
|
+
return G;
|
|
1674
|
+
}
|
|
1675
|
+
function HashToPoint(nonce, msg) {
|
|
1676
|
+
// Algorithm 3: HashToPoint(str, q, n)
|
|
1677
|
+
// (Page 31)
|
|
1678
|
+
// Require: A string str, a modulus q ≤ 2¹⁶, a degree n ∈ N*
|
|
1679
|
+
// Ensure: An polynomial c = Σᵢ cᵢxⁱ in Zq[x]
|
|
1680
|
+
// 1: k ← ⌈2¹⁶/q⌉
|
|
1681
|
+
// 2: ctx ← SHAKE-256-Init()
|
|
1682
|
+
// 3: SHAKE-256-Inject(ctx, str)
|
|
1683
|
+
// 4: i ← 0
|
|
1684
|
+
// 5: while i < n do
|
|
1685
|
+
// 6: t ← SHAKE-256-Extract(ctx, 16)
|
|
1686
|
+
// 7: if t < kq then
|
|
1687
|
+
// 8: cᵢ ← t mod q
|
|
1688
|
+
// 9: i ← i + 1
|
|
1689
|
+
// 10: return c
|
|
1690
|
+
const h = shake256.create().update(nonce).update(msg); // 3: SHAKE-256-Inject(ctx, str)
|
|
1691
|
+
const c = new Uint16Array(N);
|
|
1692
|
+
// Round-3 Falcon keeps 16-bit draws only in 0..61444, i.e. below 61445 = 5*q, the largest
|
|
1693
|
+
// 16-bit multiple of q below 2^16; a literal ceil(2^16/q)*q would accept every sample.
|
|
1694
|
+
const kQ = 5 * Q;
|
|
1695
|
+
for (let i = 0; i < N;) {
|
|
1696
|
+
const tmp = h.xof(2); // 6: t ← SHAKE-256-Extract(ctx, 16)
|
|
1697
|
+
let w = (tmp[0] << 8) | tmp[1];
|
|
1698
|
+
if (w < kQ)
|
|
1699
|
+
c[i++] = w % Q; // 8: cᵢ ← t mod q
|
|
1700
|
+
}
|
|
1701
|
+
return c;
|
|
1702
|
+
}
|
|
1703
|
+
// This is basically one sampling routine,
|
|
1704
|
+
// but it carries a lot of internal state and gets complex quickly.
|
|
1705
|
+
class FFSampler {
|
|
1706
|
+
logn;
|
|
1707
|
+
// Shake
|
|
1708
|
+
shake;
|
|
1709
|
+
shakeBuf;
|
|
1710
|
+
ctrView;
|
|
1711
|
+
// ChaCha
|
|
1712
|
+
ctr = 0n;
|
|
1713
|
+
buf;
|
|
1714
|
+
buf32;
|
|
1715
|
+
pos;
|
|
1716
|
+
key;
|
|
1717
|
+
nonce32;
|
|
1718
|
+
curBlock;
|
|
1719
|
+
curBlock32;
|
|
1720
|
+
view;
|
|
1721
|
+
// Sampler
|
|
1722
|
+
b01;
|
|
1723
|
+
b11;
|
|
1724
|
+
g00;
|
|
1725
|
+
g01;
|
|
1726
|
+
g11;
|
|
1727
|
+
constructor(logn, seed, b00, b01, b10, b11) {
|
|
1728
|
+
this.logn = logn;
|
|
1729
|
+
// Shake
|
|
1730
|
+
this.shake = shake256.create().update(seed);
|
|
1731
|
+
this.shakeBuf = new Uint8Array(56);
|
|
1732
|
+
this.key = this.shakeBuf.subarray(0, 32);
|
|
1733
|
+
this.nonce32 = u32(this.shakeBuf.subarray(32, 48)); // 4 u32s
|
|
1734
|
+
this.ctrView = createView(this.shakeBuf.subarray(48, 56));
|
|
1735
|
+
// Signle chacha20 instance buffer
|
|
1736
|
+
this.curBlock = new Uint8Array(64);
|
|
1737
|
+
this.curBlock32 = u32(this.curBlock);
|
|
1738
|
+
// whole rng buffer
|
|
1739
|
+
this.buf = new Uint8Array(8 * this.curBlock.length);
|
|
1740
|
+
this.buf32 = u32(this.buf);
|
|
1741
|
+
this.pos = this.buf.length; // not filled yet!
|
|
1742
|
+
this.view = createView(this.buf);
|
|
1743
|
+
// Sampler
|
|
1744
|
+
this.b01 = b01;
|
|
1745
|
+
this.b11 = b11;
|
|
1746
|
+
const { g00, g01, g11 } = this.gramFFT(b00, b10);
|
|
1747
|
+
this.g00 = g00;
|
|
1748
|
+
this.g01 = g01;
|
|
1749
|
+
this.g11 = g11;
|
|
1750
|
+
}
|
|
1751
|
+
destroy() {
|
|
1752
|
+
this.shake.destroy();
|
|
1753
|
+
cleanBytes(this.shakeBuf, this.curBlock, this.buf);
|
|
1754
|
+
cleanCPoly(this.b01, this.b11, this.g00, this.g01, this.g11);
|
|
1755
|
+
}
|
|
1756
|
+
refill(minBytes) {
|
|
1757
|
+
if (this.buf.length - this.pos >= minBytes)
|
|
1758
|
+
return;
|
|
1759
|
+
const out32 = swap32IfBE(this.buf32);
|
|
1760
|
+
for (let i = 0; i < 8; i++, this.ctr++) {
|
|
1761
|
+
const n = swap32IfBE(this.nonce32.slice()); // [n0, n1, n2, n3]
|
|
1762
|
+
n[2] ^= Number(this.ctr & 0xffffffffn);
|
|
1763
|
+
n[3] ^= Number(this.ctr >> 32n);
|
|
1764
|
+
// chacha20() takes raw nonce bytes; on BE the word-normalized temp must be swapped back.
|
|
1765
|
+
swap32IfBE(n.subarray(1));
|
|
1766
|
+
chacha20(this.key, u8(n.subarray(1)), EMPTY_CHACHA20_BLOCK, this.curBlock, n[0]);
|
|
1767
|
+
// Interleave like Falcon's AVX2 layout (by u32 chunks from 8 parallel chacha20)
|
|
1768
|
+
const block32 = swap32IfBE(this.curBlock32);
|
|
1769
|
+
for (let j = 0; j < 16; j++)
|
|
1770
|
+
out32[i + j * 8] = block32[j];
|
|
1771
|
+
swap32IfBE(block32);
|
|
1772
|
+
}
|
|
1773
|
+
swap32IfBE(out32);
|
|
1774
|
+
this.pos = 0;
|
|
1775
|
+
}
|
|
1776
|
+
// Sampler
|
|
1777
|
+
gaussian0() {
|
|
1778
|
+
// Algorithm 12: BaseSampler()
|
|
1779
|
+
// (Page 41)
|
|
1780
|
+
// Require: -
|
|
1781
|
+
// Ensure: An integer z₀ ∈ {0, ..., 18} such that z ~ χ ▷ χ is uniquely defined by (3.33)
|
|
1782
|
+
// 1: u ← UniformBits(72) ▷ See (3.32)
|
|
1783
|
+
// 2: z₀ ← 0
|
|
1784
|
+
// 3: for i = 0, ..., 17 do
|
|
1785
|
+
// 4: z₀ ← z₀ + [u < RCDT[i]] ▷ Note that one should use RCDT, not pdt or cdt
|
|
1786
|
+
// 5: return z₀
|
|
1787
|
+
this.refill(9);
|
|
1788
|
+
const t0 = this.view.getUint32(this.pos, true);
|
|
1789
|
+
const t1 = this.view.getUint32(this.pos + 4, true);
|
|
1790
|
+
const t2 = this.buf[this.pos + 8];
|
|
1791
|
+
this.pos += 9;
|
|
1792
|
+
const v0 = t0 & 0xffffff;
|
|
1793
|
+
const v1 = ((t0 >>> 24) & 0xff) | ((t1 & 0xffff) << 8);
|
|
1794
|
+
const v2 = ((t1 >>> 16) & 0xffff) | (t2 << 16);
|
|
1795
|
+
let z = 0;
|
|
1796
|
+
for (let i = 0; i < GAUSS0.length; i += 3) {
|
|
1797
|
+
let cc = (v0 - GAUSS0[i + 2]) >>> 31;
|
|
1798
|
+
cc = (((v1 - GAUSS0[i + 1]) | 0) - cc) >>> 31;
|
|
1799
|
+
cc = (((v2 - GAUSS0[i + 0]) | 0) - cc) >>> 31;
|
|
1800
|
+
z += cc;
|
|
1801
|
+
}
|
|
1802
|
+
return z;
|
|
1803
|
+
}
|
|
1804
|
+
berExp(x, ccs) {
|
|
1805
|
+
// Algorithm 14: BerExp(x, ccs) (Page 43)
|
|
1806
|
+
// Require: Floating point values x, ccs ≥ 0
|
|
1807
|
+
// Ensure: A single bit, equal to 1 with probability ≈ ccs · exp(-x)
|
|
1808
|
+
// 1: s ← ⌊x/ln(2)⌋
|
|
1809
|
+
// ▷ Compute the unique decomposition x = s · ln(2) + r,
|
|
1810
|
+
// with (r, s) ∈ [0, ln 2) × Z⁺
|
|
1811
|
+
// 2: r ← x - s · ln(2)
|
|
1812
|
+
// 3: s ← min(s, 63)
|
|
1813
|
+
// 4: z ← (2 · ApproxExp(r, ccs) - 1) >> s ▷ z ≈ 2⁶⁴⁻ˢ · ccs · exp(-r) = 2⁶⁴ · ccs · exp(-x)
|
|
1814
|
+
// 5: i ← 64
|
|
1815
|
+
// 6: do
|
|
1816
|
+
// 7: i ← i - 8
|
|
1817
|
+
// 8: w ← UniformBits(8) - ((z >> i) & 0xFF)
|
|
1818
|
+
// ▷ This loop does not need to be done in constant-time
|
|
1819
|
+
// 9: while ((w = 0) and (i > 0))
|
|
1820
|
+
// 10: return [w < 0] ▷ Return 1 with probability 2⁻⁶⁴ · z ≈ ccs · exp(-x)
|
|
1821
|
+
let s = Math.trunc(x * 1.4426950408889633870046509401);
|
|
1822
|
+
const r = x - s * 0.69314718055994530941723212146;
|
|
1823
|
+
let e = ApproxExp(r, ccs);
|
|
1824
|
+
e *= 2147483648.0;
|
|
1825
|
+
let z1 = e | 0;
|
|
1826
|
+
e = (e - z1) * 4294967296.0;
|
|
1827
|
+
let z0 = e | 0;
|
|
1828
|
+
z1 = (z1 << 1) | (z0 >>> 31);
|
|
1829
|
+
z0 <<= 1;
|
|
1830
|
+
s = (s | ((63 - s) >>> 26)) & 63;
|
|
1831
|
+
const sm = -(s >>> 5) | 0;
|
|
1832
|
+
z0 ^= sm & (z0 ^ z1);
|
|
1833
|
+
z1 &= ~sm;
|
|
1834
|
+
s &= 31;
|
|
1835
|
+
z0 = (z0 >>> s) | ((z1 << (31 - s)) << 1);
|
|
1836
|
+
z1 >>>= s;
|
|
1837
|
+
for (let j = 0; j < 2; j++) {
|
|
1838
|
+
for (let i = 24; i >= 0; i -= 8) {
|
|
1839
|
+
this.refill(1);
|
|
1840
|
+
const w = this.buf[this.pos++];
|
|
1841
|
+
const bz = (z1 >>> i) & 0xff;
|
|
1842
|
+
if (w !== bz)
|
|
1843
|
+
return w < bz;
|
|
1844
|
+
}
|
|
1845
|
+
z1 = z0;
|
|
1846
|
+
}
|
|
1847
|
+
return false;
|
|
1848
|
+
}
|
|
1849
|
+
samplerZ(mu, isigma) {
|
|
1850
|
+
// Algorithm 15: SamplerZ(μ, σ'), (Page 43)
|
|
1851
|
+
// Require: Floating-point values μ, σ' ∈ R such that σ' ∈ [σ_{min}, σ_{max}]
|
|
1852
|
+
// Ensure: An integer z ∈ Z sampled from a distribution very close to DZ,μ,σ'
|
|
1853
|
+
// 1: r ← μ - ⌊μ⌋ ▷ r must be in [0, 1)
|
|
1854
|
+
// 2: ccs ← σ_{min}/σ' ▷ ccs helps to make the algorithm running time independent of σ'
|
|
1855
|
+
// 3: while (1) do
|
|
1856
|
+
// 4: z₀ ← BaseSampler()
|
|
1857
|
+
// 5: b ← UniformBits(8) & 0x1
|
|
1858
|
+
// 6: z ← b + (2 · b - 1)z₀
|
|
1859
|
+
// 7: x ← ((z-r)²)/(2σ'²)
|
|
1860
|
+
// 8: if (BerExp(x, ccs) = 1) then
|
|
1861
|
+
// 9: return z + ⌊μ⌋
|
|
1862
|
+
const s = Math.floor(mu);
|
|
1863
|
+
const r = mu - s;
|
|
1864
|
+
const dss = isigma * isigma * 0.5;
|
|
1865
|
+
const ccs = isigma * SIGMA_MIN[this.logn];
|
|
1866
|
+
for (;;) {
|
|
1867
|
+
const z0 = this.gaussian0();
|
|
1868
|
+
this.refill(1);
|
|
1869
|
+
const b = this.buf[this.pos++] & 1;
|
|
1870
|
+
const z = (((z0 << 1) + 1) & -b) - z0;
|
|
1871
|
+
let x = z - r;
|
|
1872
|
+
x = x * x * dss - z0 * z0 * 0.1508650488753727203494747755;
|
|
1873
|
+
if (this.berExp(x, ccs))
|
|
1874
|
+
return s + z;
|
|
1875
|
+
}
|
|
1876
|
+
}
|
|
1877
|
+
ldlFFT(logn, g00t, g01t, g11t) {
|
|
1878
|
+
// Algorithm 8: LDL*(G)
|
|
1879
|
+
// (Page 37)
|
|
1880
|
+
// Require: A full-rank self-adjoint matrix G = (Gᵢⱼ) ∈ FFT(Q[x]/(φ))²ˣ²
|
|
1881
|
+
// Ensure: The LDL* decomposition G = LDL* over FFT(Q[x]/(φ))
|
|
1882
|
+
// Format: All polynomials are in FFT representation.
|
|
1883
|
+
// 1: D₀₀ ← G₀₀
|
|
1884
|
+
// 2: L₁₀ ← G₁₀/G₀₀
|
|
1885
|
+
// 3: D₁₁ ← G₁₁ - L₁₀ ⊙ L₁₀* ⊙ G₀₀
|
|
1886
|
+
// 4: L ← [ 1 0 ; L₁₀ 1 ], D ← [ D₀₀ 0 ; 0 D₁₁ ]
|
|
1887
|
+
// 5: return (L, D)
|
|
1888
|
+
// Algorithm 9: ffLDL*(G)
|
|
1889
|
+
// (Page 37)
|
|
1890
|
+
// Require: A full-rank Gram matrix G ∈ FFT(Q[x]/(xⁿ + 1))²ˣ²
|
|
1891
|
+
// Ensure: A binary tree T
|
|
1892
|
+
// Format: All polynomials are in FFT representation.
|
|
1893
|
+
// 1: (L, D) ← LDL*(G) ▷ L = [ 1 0 ; L₁₀ 1 ], D = [ D₀₀ 0 ; 0 D₁₁ ]
|
|
1894
|
+
// 2: T.value ← L₁₀
|
|
1895
|
+
// 3: if (n = 2) then
|
|
1896
|
+
// 4: T.leftchild ← D₀₀
|
|
1897
|
+
// 5: T.rightchild ← D₁₁
|
|
1898
|
+
// 6: return T
|
|
1899
|
+
// 7: else
|
|
1900
|
+
// 8: d₀₀, d₀₁ ← splitfft(D₀₀) ▷ dᵢⱼ ∈ FFT(Q[x]/(x^{n/2} + 1))
|
|
1901
|
+
// 9: d₁₀, d₁₁ ← splitfft(D₁₁)
|
|
1902
|
+
// 10: G₀ ← [ d₀₀ d₀₁ ; d₀₁* d₀₀ ], G₁ ← [ d₁₀ d₁₁ ; d₁₁* d₁₀ ]
|
|
1903
|
+
// ▷ Since D₀₀, D₁₁ are self-adjoint, (3.30) applies
|
|
1904
|
+
// 11: T.leftchild ← ffLDL*(G₀) ▷ Recursive calls
|
|
1905
|
+
// 12: T.rightchild ← ffLDL*(G₁)
|
|
1906
|
+
// 13: return T
|
|
1907
|
+
g00t = g00t.slice(); // can be same as g11t and everything will break!
|
|
1908
|
+
const hn = 1 << (logn - 1);
|
|
1909
|
+
for (let i = 0; i < hn; i++) {
|
|
1910
|
+
const g01 = g01t[i];
|
|
1911
|
+
const g11 = g11t[i];
|
|
1912
|
+
const mu = fComplex.scale(g01, 1.0 / g00t[i].re);
|
|
1913
|
+
g11t[i] = { re: g11.re - (mu.re * g01.re + mu.im * g01.im), im: g11.im };
|
|
1914
|
+
g01t[i] = fComplex.conj(mu);
|
|
1915
|
+
}
|
|
1916
|
+
return { g00: g00t, g01: g01t, g11: g11t };
|
|
1917
|
+
}
|
|
1918
|
+
splitFFT(logn, f) {
|
|
1919
|
+
// Algorithm 1: splitfft(FFT(f))
|
|
1920
|
+
// (Page 29)
|
|
1921
|
+
// Require: FFT(f) = (f(ζ))ζ for some f ∈ Q[x]/(φ)
|
|
1922
|
+
// Ensure: FFT(f₀) = (f₀(ζ'))ζ' and FFT(f₁) = (f₁(ζ'))ζ' for some f₀, f₁ ∈ Q[x]/(φ')
|
|
1923
|
+
// Format: All polynomials are in FFT representation.
|
|
1924
|
+
// 1: for ζ such that φ(ζ) = 0 and Im(ζ) > 0 do ▷ See eq. (3.19) with 0 ≤ k < n/2
|
|
1925
|
+
// 2: ζ' ← ζ²
|
|
1926
|
+
// 3: f₀(ζ') ← ½ [f(ζ) + f(−ζ)]
|
|
1927
|
+
// 4: f₁(ζ') ← (1/(2ζ)) [f(ζ) − f(−ζ)]
|
|
1928
|
+
// 5: return (FFT(f₀), FFT(f₁))
|
|
1929
|
+
const hn = 1 << (logn - 1);
|
|
1930
|
+
const qn = hn >> 1;
|
|
1931
|
+
if (logn === 1)
|
|
1932
|
+
return { f0: [{ re: f[0].re, im: 0.0 }], f1: [{ re: f[0].im, im: 0.0 }] };
|
|
1933
|
+
const f0t = new Array(qn);
|
|
1934
|
+
const f1t = new Array(qn);
|
|
1935
|
+
const ft = f;
|
|
1936
|
+
for (let i = 0; i < qn; i++) {
|
|
1937
|
+
const a = ft[(i << 1) + 0];
|
|
1938
|
+
const b = ft[(i << 1) + 1];
|
|
1939
|
+
f0t[i] = fComplex.scale(fComplex.add(a, b), 0.5);
|
|
1940
|
+
f1t[i] = fComplex.scale(fComplex.mul(fComplex.sub(a, b), fComplex.conj(COMPLEX_ROOTS_O[i + hn])), 0.5);
|
|
1941
|
+
}
|
|
1942
|
+
return { f0: f0t, f1: f1t };
|
|
1943
|
+
}
|
|
1944
|
+
splitSelfAdjFFT(logn, f) {
|
|
1945
|
+
const hn = 1 << (logn - 1);
|
|
1946
|
+
const qn = hn >> 1;
|
|
1947
|
+
if (logn === 1)
|
|
1948
|
+
return { f0: [{ re: f[0].re, im: 0.0 }], f1: [{ re: 0.0, im: 0.0 }] };
|
|
1949
|
+
const f0t = new Array(qn);
|
|
1950
|
+
const f1t = new Array(qn);
|
|
1951
|
+
const ft = f;
|
|
1952
|
+
for (let i = 0; i < qn; i++) {
|
|
1953
|
+
const a = ft[(i << 1) + 0];
|
|
1954
|
+
const b = ft[(i << 1) + 1];
|
|
1955
|
+
f0t[i] = fComplex.scale(fComplex.add(a, b), 0.5);
|
|
1956
|
+
f1t[i] = fComplex.scale(fComplex.scale(fComplex.conj(COMPLEX_ROOTS_O[i + hn]), fComplex.sub(a, b).re), 0.5);
|
|
1957
|
+
}
|
|
1958
|
+
return { f0: f0t, f1: f1t };
|
|
1959
|
+
}
|
|
1960
|
+
mergeFFT(logn, f0, f1) {
|
|
1961
|
+
// Algorithm 2: mergefft(f₀, f₁)
|
|
1962
|
+
// (Page 29)
|
|
1963
|
+
// Require: FFT(f₀) = (f₀(ζ'))ζ' and FFT(f₁) = (f₁(ζ'))ζ' for some f₀, f₁ ∈ Q[x]/(φ')
|
|
1964
|
+
// Ensure: FFT(f) = (f(ζ))ζ for some f ∈ Q[x]/(φ)
|
|
1965
|
+
// Format: All polynomials are in FFT representation.
|
|
1966
|
+
// 1: for ζ such that φ(ζ) = 0 do ▷ See eq. (3.19)
|
|
1967
|
+
// 2: ζ' ← ζ²
|
|
1968
|
+
// 3: f(ζ) ← f₀(ζ') + ζf₁(ζ')
|
|
1969
|
+
// 4: return FFT(f)
|
|
1970
|
+
const hn = 1 << (logn - 1);
|
|
1971
|
+
const qn = hn >> 1;
|
|
1972
|
+
if (logn === 1)
|
|
1973
|
+
return [{ re: f0[0].re, im: f1[0].re }];
|
|
1974
|
+
const ft = new Array(2 * qn);
|
|
1975
|
+
for (let i = 0; i < qn; i++) {
|
|
1976
|
+
const a = f0[i];
|
|
1977
|
+
const c = fComplex.mul(f1[i], COMPLEX_ROOTS_O[i + hn]);
|
|
1978
|
+
ft[(i << 1) + 0] = fComplex.add(a, c);
|
|
1979
|
+
ft[(i << 1) + 1] = fComplex.sub(a, c);
|
|
1980
|
+
}
|
|
1981
|
+
return ft;
|
|
1982
|
+
}
|
|
1983
|
+
gramFFT(b00, b10) {
|
|
1984
|
+
const { b01, b11 } = this;
|
|
1985
|
+
const hn = (1 << this.logn) >> 1;
|
|
1986
|
+
const g00 = new Array(hn);
|
|
1987
|
+
const g01 = new Array(hn);
|
|
1988
|
+
const g11 = new Array(hn);
|
|
1989
|
+
for (let i = 0; i < hn; i++) {
|
|
1990
|
+
const b00t = b00[i];
|
|
1991
|
+
const b01t = b01[i];
|
|
1992
|
+
const b10t = b10[i];
|
|
1993
|
+
const b11t = b11[i];
|
|
1994
|
+
const u = fComplex.mul(b00t, fComplex.conj(b10t));
|
|
1995
|
+
const v = fComplex.mul(b01t, fComplex.conj(b11t));
|
|
1996
|
+
g00[i] = { re: fComplex.magSqSum(b00t, b01t), im: 0.0 };
|
|
1997
|
+
g01[i] = fComplex.add(u, v);
|
|
1998
|
+
g11[i] = { re: fComplex.magSqSum(b10t, b11t), im: 0.0 };
|
|
1999
|
+
}
|
|
2000
|
+
return { g00, g01, g11 };
|
|
2001
|
+
}
|
|
2002
|
+
ffsampRec(logn, t0, t1, g00i, g01i, g11i) {
|
|
2003
|
+
// Algorithm 11: ffSamplingₙ(t, T)
|
|
2004
|
+
// (Page 40)
|
|
2005
|
+
// Require: t = (t₀, t₁) ∈ FFT(Q[x]/(xⁿ + 1))², a FALCON tree T
|
|
2006
|
+
// Ensure: z = (z₀, z₁) ∈ FFT(Z[x]/(xⁿ + 1))²
|
|
2007
|
+
// Format: All polynomials are in FFT representation.
|
|
2008
|
+
// 1: if n = 1 then
|
|
2009
|
+
// 2: σ' ← T.value ▷ It is always the case that σ' ∈ [σ_{min}, σ_{max}]
|
|
2010
|
+
// 3: z₀ ← SamplerZ(t₀, σ') ▷ Since n=1, tᵢ = invFFT(tᵢ) ∈ Q and zᵢ = invFFT(zᵢ) ∈ Z
|
|
2011
|
+
// 4: z₁ ← SamplerZ(t₁, σ')
|
|
2012
|
+
// 5: return z = (z₀, z₁)
|
|
2013
|
+
// 6: (l, T₀, T₁) ← (T.value, T.leftchild, T.rightchild)
|
|
2014
|
+
// 7: t'₁ ← splitfft(t₁) ▷ t₀, t'₁ ∈ FFT(Q[x]/(x^{n/2} + 1))²
|
|
2015
|
+
// 8: z'₁ ← ffSampling_{n/2}(t'₁, T₁) ▷ First recursive call to ffSampling_{n/2}
|
|
2016
|
+
// 9: z₁ ← mergefft(z'₁) ▷ z₀, z₁ ∈ FFT(Z[x]/(x^{n/2} + 1))²
|
|
2017
|
+
// 10: t'₀ ← t₀ + (t₁ - z₁) ⊙ l
|
|
2018
|
+
// 11: t''₀ ← splitfft(t'₀)
|
|
2019
|
+
// 12: z'₀ ← ffSampling_{n/2}(t''₀, T₀) ▷ Second recursive call to ffSampling_{n/2}
|
|
2020
|
+
// 13: z₀ ← mergefft(z'₀)
|
|
2021
|
+
// 14: return z = (z₀, z₁)
|
|
2022
|
+
if (logn === 0) {
|
|
2023
|
+
const leaf = Math.sqrt(g00i[0].re) * INV_SIGMA[this.logn];
|
|
2024
|
+
// 3: z₀ ← SamplerZ(t₀, σ')
|
|
2025
|
+
// ▷ Since n=1, tᵢ = invFFT(tᵢ) ∈ Q and zᵢ = invFFT(zᵢ) ∈ Z
|
|
2026
|
+
const t0re = this.samplerZ(t0[0].re, leaf);
|
|
2027
|
+
const t1re = this.samplerZ(t1[0].re, leaf); // 4: z₁ ← SamplerZ(t₁, σ')
|
|
2028
|
+
return { t0: [{ re: t0re, im: 0.0 }], t1: [{ re: t1re, im: 0.0 }] };
|
|
2029
|
+
}
|
|
2030
|
+
// 6: (l, T₀, T₁) ← (T.value, T.leftchild, T.rightchild)
|
|
2031
|
+
const { g00, g01, g11 } = this.ldlFFT(logn, g00i, g01i, g11i);
|
|
2032
|
+
const { f0: g00f0, f1: g00f1 } = this.splitSelfAdjFFT(logn, g00);
|
|
2033
|
+
const { f0: g11f0, f1: g11f1 } = this.splitSelfAdjFFT(logn, g11);
|
|
2034
|
+
// 7: t'₁ ← splitfft(t₁)
|
|
2035
|
+
// ▷ t₀, t'₁ ∈ FFT(Q[x]/(x^{n/2} + 1))²
|
|
2036
|
+
const { f0: t1f0in, f1: t1f1in } = this.splitFFT(logn, t1);
|
|
2037
|
+
const { t0: t1f0out, t1: t1f1out } = this.ffsampRec(logn - 1, t1f0in, t1f1in, g11f0, g11f1, g11f0); // 8: z'₁ ← ffSampling_{n/2}(t'₁, T₁) ▷ First recursive call to ffSampling_{n/2}
|
|
2038
|
+
// 9: z₁ ← mergefft(z'₁)
|
|
2039
|
+
// ▷ z₀, z₁ ∈ FFT(Z[x]/(x^{n/2} + 1))²
|
|
2040
|
+
const t1new = this.mergeFFT(logn, t1f0out, t1f1out);
|
|
2041
|
+
// 10: t'₀ ← t₀ + (t₁ - z₁) ⊙ l
|
|
2042
|
+
const t0tmp = floatPoly.add(t0, floatPoly.mul(g01, floatPoly.sub(t1, t1new)));
|
|
2043
|
+
const { f0: t0f0in, f1: t0f1in } = this.splitFFT(logn, t0tmp); // 11: t''₀ ← splitfft(t'₀)
|
|
2044
|
+
const { t0: t0f0out, t1: t0f1out } = this.ffsampRec(logn - 1, t0f0in, t0f1in, g00f0, g00f1, g00f0); // 12: z'₀ ← ffSampling_{n/2}(t''₀, T₀) ▷ Second recursive call to ffSampling_{n/2}
|
|
2045
|
+
const z1 = this.mergeFFT(logn, t0f0out, t0f1out); // 13: z₀ ← mergefft(z'₀)
|
|
2046
|
+
return { t0: z1, t1: t1new };
|
|
2047
|
+
}
|
|
2048
|
+
// sampling a preimage in FFT domain
|
|
2049
|
+
sample(hm) {
|
|
2050
|
+
const t0t = floatPoly.FFT(floatPoly.convSmall(hm));
|
|
2051
|
+
const t0f = floatPoly.mulConst(floatPoly.mul(t0t, this.b11), F_INV_Q);
|
|
2052
|
+
const t1f = floatPoly.mulConst(floatPoly.mul(t0t, this.b01), F_MINUS_INV_Q);
|
|
2053
|
+
// Set seed
|
|
2054
|
+
this.shake.xofInto(this.shakeBuf);
|
|
2055
|
+
this.ctr = this.ctrView.getBigUint64(0, true);
|
|
2056
|
+
// Actual sampling
|
|
2057
|
+
return this.ffsampRec(this.logn, t0f, t1f, this.g00, this.g01, this.g11);
|
|
2058
|
+
}
|
|
2059
|
+
}
|
|
2060
|
+
const signRaw = (sk, msg, maxLen, rnd = randomBytes) => {
|
|
2061
|
+
// Algorithm 10: Sign(m, sk, [β²]), (Page 39)
|
|
2062
|
+
// Require: A message m, a secret key sk, a bound [β²]
|
|
2063
|
+
// Ensure: A signature sig of m
|
|
2064
|
+
// 1: r ← {0, 1}³²⁰ uniformly
|
|
2065
|
+
// 2: c ← HashToPoint(r||m, q, n)
|
|
2066
|
+
// 3: t ← ( (1/q)FFT(c) ⊙ FFT(F), (1/q)FFT(c) ⊙ FFT(f) ) ▷ t = (FFT(c), FFT(0)) · B̂⁻¹
|
|
2067
|
+
// 4: do
|
|
2068
|
+
// 5: do
|
|
2069
|
+
// 6: z ← ffSamplingₙ(t, T)
|
|
2070
|
+
// 7: s = (t - z)B̂
|
|
2071
|
+
// ▷ At this point, s follows a Gaussian distribution:
|
|
2072
|
+
// s ~ D_{(c,0)+Λ(B),σ,0}
|
|
2073
|
+
// 8: while ||s||² > [β²]
|
|
2074
|
+
// ▷ Since s is in FFT representation, one may use (3.8) to compute ||s||²
|
|
2075
|
+
// 9: (s₁, s₂) ← invFFT(s) ▷ s₁ + s₂h = c mod (φ, q)
|
|
2076
|
+
// 10: s ← Compress(s₂, 8 · sbytelen - 328)
|
|
2077
|
+
// ▷ Remove 1 byte for the header, and 40 bytes for r
|
|
2078
|
+
// 11: while (s = ⊥)
|
|
2079
|
+
// 12: return sig = (r, s)
|
|
2080
|
+
abytes(msg);
|
|
2081
|
+
// One RNG stream drives both the public 40-byte nonce and the 48-byte sampler seed, so
|
|
2082
|
+
// deterministic rnd hooks make signatures deterministic for fixed secretKey/message inputs.
|
|
2083
|
+
const nonce = rnd(40);
|
|
2084
|
+
// Keep these raw 40-byte checks in sync with NONCELEN: Falcon's r <- {0,1}^320 nonce
|
|
2085
|
+
// feeds HashToPoint(r || m) and the public signature framing, so callback bugs must fail fast.
|
|
2086
|
+
abytes(nonce, 40, 'nonce');
|
|
2087
|
+
const hm = HashToPoint(nonce, msg); // 2: c ← HashToPoint(r||m, q, n)
|
|
2088
|
+
const seed = rnd(48);
|
|
2089
|
+
// Falcon implementations here use a fixed 48-byte sampler seed; reject callback bugs up front.
|
|
2090
|
+
abytes(seed, 48, 'seed');
|
|
2091
|
+
try {
|
|
2092
|
+
const [f, g, F] = secretKeyCoder.decode(sk);
|
|
2093
|
+
try {
|
|
2094
|
+
const G = completePrivate(f, g, F);
|
|
2095
|
+
const b00 = floatPoly.FFT(floatPoly.convSmall(g));
|
|
2096
|
+
const b01 = floatPoly.FFT(floatPoly.neg(floatPoly.convSmall(f)));
|
|
2097
|
+
const b10 = floatPoly.FFT(floatPoly.convSmall(G));
|
|
2098
|
+
const b11 = floatPoly.FFT(floatPoly.neg(floatPoly.convSmall(F)));
|
|
2099
|
+
const sampler = new FFSampler(logn, seed, b00, b01, b10, b11);
|
|
2100
|
+
const s2 = new Int16Array(N);
|
|
2101
|
+
try {
|
|
2102
|
+
while (true) {
|
|
2103
|
+
const { t0, t1 } = sampler.sample(hm);
|
|
2104
|
+
// t2 = b00*t0 + b10*t1
|
|
2105
|
+
const t2 = floatPoly.add(floatPoly.mul(t0, b00), floatPoly.mul(t1, b10));
|
|
2106
|
+
const t3 = floatPoly.mul(t0, b01); // t3 = b01*t0
|
|
2107
|
+
const t4 = floatPoly.iFFT(t2); // t4 = iFFT(tx)
|
|
2108
|
+
// t5 = iFFT(b11*t1 + ty)
|
|
2109
|
+
const t5 = floatPoly.iFFT(floatPoly.add(floatPoly.mul(t1, b11), t3));
|
|
2110
|
+
// Traverse imaginary in exact same order to avoid numerical instability
|
|
2111
|
+
const hn = N >> 1;
|
|
2112
|
+
let sqn = 0;
|
|
2113
|
+
for (let i = 0; i < hn; i++) {
|
|
2114
|
+
sqn += (hm[i] - (Math.round(t4[i].re) | 0)) ** 2;
|
|
2115
|
+
sqn += (hm[hn + i] - (Math.round(t4[i].im) | 0)) ** 2;
|
|
2116
|
+
const z = -Math.round(t5[i].re);
|
|
2117
|
+
sqn += z * z;
|
|
2118
|
+
s2[i] = z & 0xffff;
|
|
2119
|
+
const z2 = -Math.round(t5[i].im);
|
|
2120
|
+
sqn += z2 * z2;
|
|
2121
|
+
s2[i + hn] = z2 & 0xffff;
|
|
2122
|
+
}
|
|
2123
|
+
cleanCPoly(t0, t1, t2, t3, t4, t5);
|
|
2124
|
+
if (!(sqn <= L2BOUND[logn]))
|
|
2125
|
+
continue;
|
|
2126
|
+
// 10: s ← Compress(s₂, 8 · sbytelen - 328)
|
|
2127
|
+
// ▷ Remove 1 byte for the header, and 40 bytes for r
|
|
2128
|
+
const s2comp = compCoder(N).encode(s2);
|
|
2129
|
+
if (s2comp.length > maxLen) {
|
|
2130
|
+
cleanBytes(s2comp);
|
|
2131
|
+
continue;
|
|
2132
|
+
}
|
|
2133
|
+
return { s2: s2comp, nonce, msg };
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
finally {
|
|
2137
|
+
cleanBytes(s2);
|
|
2138
|
+
sampler.destroy();
|
|
2139
|
+
cleanCPoly(b00, b01, b10, b11);
|
|
2140
|
+
cleanBytes(G);
|
|
2141
|
+
}
|
|
2142
|
+
}
|
|
2143
|
+
finally {
|
|
2144
|
+
cleanBytes(f, g, F);
|
|
2145
|
+
}
|
|
2146
|
+
}
|
|
2147
|
+
finally {
|
|
2148
|
+
cleanBytes(seed);
|
|
2149
|
+
}
|
|
2150
|
+
};
|
|
2151
|
+
// Raw helper: malformed encodings or wrong lengths still throw here; the public verify()/open()
|
|
2152
|
+
// wrappers decide whether to translate those failures into false or an exception.
|
|
2153
|
+
const verifyRaw = (pk, s2comp, nonce, msg) => {
|
|
2154
|
+
// Algorithm 16: Verify(m, sig, pk, [β²])
|
|
2155
|
+
// (Page 45)
|
|
2156
|
+
// Require: A message m, a signature sig = (r, s), a public key pk = h ∈ Zq[x]/(φ), a bound [β²]
|
|
2157
|
+
// Ensure: Accept or reject
|
|
2158
|
+
// 1: c ← HashToPoint(r||m, q, n)
|
|
2159
|
+
// 2: s₂ ← Decompress(s, 8 · sbytelen - 328)
|
|
2160
|
+
// 3: if (s₂ = ⊥) then
|
|
2161
|
+
// 4: reject ▷ Reject invalid encodings
|
|
2162
|
+
// 5: s₁ ← c - s₂h mod q ▷ s₁ should be normalized between -q/2 and q/2
|
|
2163
|
+
// 6: if ||(s₁, s₂)||² < [β²] then
|
|
2164
|
+
// 7: accept
|
|
2165
|
+
// 8: else
|
|
2166
|
+
// 9: reject ▷ Reject signatures that are too long
|
|
2167
|
+
const s2 = compCoder(N).decode(s2comp); // 2: s₂ ← Decompress(s, 8 · sbytelen - 328)
|
|
2168
|
+
const c0 = HashToPoint(nonce, msg); // 1: c ← HashToPoint(r||m, q, n)
|
|
2169
|
+
const h = intPoly.toMontgomery(intPoly.ntt(publicKeyCoder.decode(pk)));
|
|
2170
|
+
const s1 = intPoly.intt(intPoly.mul(intPoly.ntt(signedCoder.decode(s2)), h));
|
|
2171
|
+
intPoly.sub(s1, c0); // 5: s₁ ← c - s₂h mod q ▷ s₁ should be normalized between -q/2 and q/2
|
|
2172
|
+
return intPoly.isShort(signedCoder.encode(s1), s2); // 6: if ||(s₁, s₂)||² < [β²] then
|
|
2173
|
+
};
|
|
2174
|
+
const info = { type: 'falcon' };
|
|
2175
|
+
const keyLengths = {
|
|
2176
|
+
seed: 48,
|
|
2177
|
+
publicKey: publicKeyCoder.bytesLen,
|
|
2178
|
+
secretKey: secretKeyCoder.bytesLen,
|
|
2179
|
+
};
|
|
2180
|
+
// Noble exposes a 48-byte sampler-seed hook,
|
|
2181
|
+
// but Falcon still samples/encodes a separate 40-byte nonce per signature.
|
|
2182
|
+
const getRnd = (opts = {}) => {
|
|
2183
|
+
validateSigOpts(opts);
|
|
2184
|
+
if (opts.context !== undefined)
|
|
2185
|
+
throw new Error('context is not supported');
|
|
2186
|
+
if (opts.random !== undefined)
|
|
2187
|
+
return opts.random;
|
|
2188
|
+
if (opts.extraEntropy === undefined)
|
|
2189
|
+
return randomBytes;
|
|
2190
|
+
const seed = opts.extraEntropy === false ? new Uint8Array(48) : opts.extraEntropy;
|
|
2191
|
+
abytes(seed, 48, 'opts.extraEntropy');
|
|
2192
|
+
const drbg = rngAesCtrDrbg256(seed);
|
|
2193
|
+
return (len = 0) => drbg.randomBytes(len);
|
|
2194
|
+
};
|
|
2195
|
+
const checkVerOpts = (opts = {}) => {
|
|
2196
|
+
validateVerOpts(opts);
|
|
2197
|
+
if (opts.context !== undefined)
|
|
2198
|
+
throw new Error('context is not supported');
|
|
2199
|
+
};
|
|
2200
|
+
const tests = { publicKeyCoder, privateKeyCoder: secretKeyCoder, maxS2Len: opts.maxS2Len };
|
|
2201
|
+
// `signRand` documents only the sampler-seed input length;
|
|
2202
|
+
// detached/attached signatures still include their own 40-byte nonce.
|
|
2203
|
+
const attachedLengths = { ...keyLengths, signRand: 48 };
|
|
2204
|
+
const lengths = opts.padded ? { ...attachedLengths, signature: opts.sigLen } : attachedLengths;
|
|
2205
|
+
const keygen = (seed) => {
|
|
2206
|
+
const randSeed = seed === undefined;
|
|
2207
|
+
if (randSeed)
|
|
2208
|
+
seed = randomBytes(48);
|
|
2209
|
+
abytes(seed, 48, 'seed');
|
|
2210
|
+
const [f, g, F, _G, pub] = new NTRU(logn, seed).generate();
|
|
2211
|
+
const sk = secretKeyCoder.encode([f, g, F]);
|
|
2212
|
+
const pk = publicKeyCoder.encode(pub);
|
|
2213
|
+
if (randSeed)
|
|
2214
|
+
cleanBytes(seed);
|
|
2215
|
+
cleanBytes(f, g, F, _G);
|
|
2216
|
+
return { publicKey: pk, secretKey: sk };
|
|
2217
|
+
};
|
|
2218
|
+
const getPublicKey = (sk) => {
|
|
2219
|
+
const [f, g, F] = secretKeyCoder.decode(sk);
|
|
2220
|
+
try {
|
|
2221
|
+
const h = computePublic(f, g);
|
|
2222
|
+
cleanBytes(f, g, F);
|
|
2223
|
+
return publicKeyCoder.encode(h);
|
|
2224
|
+
}
|
|
2225
|
+
catch (e) {
|
|
2226
|
+
cleanBytes(f, g, F);
|
|
2227
|
+
throw e;
|
|
2228
|
+
}
|
|
2229
|
+
};
|
|
2230
|
+
const sign = (msg, sk, sigOpts = {}) => {
|
|
2231
|
+
const { s2, nonce } = signRaw(sk, msg, opts.maxS2Len, getRnd(sigOpts));
|
|
2232
|
+
return SignatureCoderDetached(logn).encode({ nonce, s2 });
|
|
2233
|
+
};
|
|
2234
|
+
/** Verify one detached Falcon signature.
|
|
2235
|
+
* Returns `false` for malformed detached signature encodings, non-canonical detached signatures,
|
|
2236
|
+
* and well-formed signatures that do not validate. Throws on malformed API argument types or
|
|
2237
|
+
* unsupported verification options.
|
|
2238
|
+
*/
|
|
2239
|
+
const verify = (sig, msg, pk, verOpts = {}) => {
|
|
2240
|
+
checkVerOpts(verOpts);
|
|
2241
|
+
abytes(sig);
|
|
2242
|
+
abytes(msg);
|
|
2243
|
+
abytes(pk);
|
|
2244
|
+
try {
|
|
2245
|
+
const { s2, nonce } = SignatureCoderDetached(logn).decode(sig);
|
|
2246
|
+
return verifyRaw(pk, s2, nonce, msg);
|
|
2247
|
+
}
|
|
2248
|
+
catch {
|
|
2249
|
+
return false;
|
|
2250
|
+
}
|
|
2251
|
+
};
|
|
2252
|
+
const attached = {
|
|
2253
|
+
info,
|
|
2254
|
+
lengths: attachedLengths,
|
|
2255
|
+
keygen,
|
|
2256
|
+
getPublicKey,
|
|
2257
|
+
seal(msg, sk, sigOpts = {}) {
|
|
2258
|
+
const { s2, nonce } = signRaw(sk, msg, opts.maxS2Len, getRnd(sigOpts));
|
|
2259
|
+
return SignatureCoder.encode({ msg, nonce, s2 });
|
|
2260
|
+
},
|
|
2261
|
+
open(sig, pk, verOpts = {}) {
|
|
2262
|
+
checkVerOpts(verOpts);
|
|
2263
|
+
const { s2, nonce, msg } = SignatureCoder.decode(sig);
|
|
2264
|
+
// Zero-copy API: returned message aliases the caller-provided signature buffer.
|
|
2265
|
+
// Copy it if ownership is needed.
|
|
2266
|
+
if (verifyRaw(pk, s2, nonce, msg))
|
|
2267
|
+
return msg;
|
|
2268
|
+
throw new Error('invalid signature');
|
|
2269
|
+
},
|
|
2270
|
+
};
|
|
2271
|
+
const res = {
|
|
2272
|
+
info,
|
|
2273
|
+
lengths,
|
|
2274
|
+
attached,
|
|
2275
|
+
keygen,
|
|
2276
|
+
getPublicKey,
|
|
2277
|
+
sign,
|
|
2278
|
+
verify,
|
|
2279
|
+
};
|
|
2280
|
+
res.__test = tests;
|
|
2281
|
+
return res;
|
|
2282
|
+
}
|
|
2283
|
+
const falcon512opts = {
|
|
2284
|
+
N: 512,
|
|
2285
|
+
// Table 3.3 fixed padded detached bytes, including the detached header byte and 40-byte nonce.
|
|
2286
|
+
sigLen: 666,
|
|
2287
|
+
fgBits: 6,
|
|
2288
|
+
FGBits: 8,
|
|
2289
|
+
// Compressed-s payload bytes only, excluding the detached header byte and 40-byte nonce.
|
|
2290
|
+
paddedLen: 625,
|
|
2291
|
+
// Payload-only budget: genFalcon() adds the detached header byte and 40-byte nonce around it.
|
|
2292
|
+
detachedLen: 690,
|
|
2293
|
+
};
|
|
2294
|
+
/**
|
|
2295
|
+
* Falcon-512 detached-signature API with the attached helper exposed as `.attached`.
|
|
2296
|
+
* @example
|
|
2297
|
+
* Generate a Falcon-512 keypair and verify one detached signature.
|
|
2298
|
+
* ```ts
|
|
2299
|
+
* const { secretKey, publicKey } = falcon512.keygen();
|
|
2300
|
+
* const msg = new Uint8Array([1, 2, 3]);
|
|
2301
|
+
* const sig = falcon512.sign(msg, secretKey);
|
|
2302
|
+
* falcon512.verify(sig, msg, publicKey);
|
|
2303
|
+
* ```
|
|
2304
|
+
*/
|
|
2305
|
+
export const falcon512 = /* @__PURE__ */ (() => genFalcon({ ...falcon512opts, maxS2Len: 711 }))();
|
|
2306
|
+
/**
|
|
2307
|
+
* Falcon-512 padded detached-signature API with the attached helper exposed as `.attached`.
|
|
2308
|
+
* @example
|
|
2309
|
+
* Generate a Falcon-512 padded keypair and verify one detached signature.
|
|
2310
|
+
* ```ts
|
|
2311
|
+
* const { secretKey, publicKey } = falcon512padded.keygen();
|
|
2312
|
+
* const msg = new Uint8Array([1, 2, 3]);
|
|
2313
|
+
* const sig = falcon512padded.sign(msg, secretKey);
|
|
2314
|
+
* falcon512padded.verify(sig, msg, publicKey);
|
|
2315
|
+
* ```
|
|
2316
|
+
*/
|
|
2317
|
+
export const falcon512padded = /* @__PURE__ */ (() => genFalcon({
|
|
2318
|
+
...falcon512opts,
|
|
2319
|
+
padded: true,
|
|
2320
|
+
maxS2Len: 625,
|
|
2321
|
+
}))();
|
|
2322
|
+
const falcon1024opts = {
|
|
2323
|
+
N: 1024,
|
|
2324
|
+
// Table 3.3 fixed padded detached bytes, including the detached header byte and 40-byte nonce.
|
|
2325
|
+
sigLen: 1280,
|
|
2326
|
+
fgBits: 5,
|
|
2327
|
+
FGBits: 8,
|
|
2328
|
+
// Compressed-s payload bytes only, excluding the detached header byte and 40-byte nonce.
|
|
2329
|
+
paddedLen: 1239,
|
|
2330
|
+
// Payload-only budget: genFalcon() adds the detached header byte and 40-byte nonce around it.
|
|
2331
|
+
detachedLen: 1280,
|
|
2332
|
+
};
|
|
2333
|
+
/**
|
|
2334
|
+
* Falcon-1024 detached-signature API with the attached helper exposed as `.attached`.
|
|
2335
|
+
* @example
|
|
2336
|
+
* Generate a Falcon-1024 keypair and verify one detached signature.
|
|
2337
|
+
* ```ts
|
|
2338
|
+
* const { secretKey, publicKey } = falcon1024.keygen();
|
|
2339
|
+
* const msg = new Uint8Array([1, 2, 3]);
|
|
2340
|
+
* const sig = falcon1024.sign(msg, secretKey);
|
|
2341
|
+
* falcon1024.verify(sig, msg, publicKey);
|
|
2342
|
+
* ```
|
|
2343
|
+
*/
|
|
2344
|
+
export const falcon1024 = /* @__PURE__ */ (() => genFalcon({
|
|
2345
|
+
...falcon1024opts,
|
|
2346
|
+
maxS2Len: 1421,
|
|
2347
|
+
}))();
|
|
2348
|
+
/**
|
|
2349
|
+
* Falcon-1024 padded detached-signature API with the attached helper exposed as `.attached`.
|
|
2350
|
+
* @example
|
|
2351
|
+
* Generate a Falcon-1024 padded keypair and verify one detached signature.
|
|
2352
|
+
* ```ts
|
|
2353
|
+
* const { secretKey, publicKey } = falcon1024padded.keygen();
|
|
2354
|
+
* const msg = new Uint8Array([1, 2, 3]);
|
|
2355
|
+
* const sig = falcon1024padded.sign(msg, secretKey);
|
|
2356
|
+
* falcon1024padded.verify(sig, msg, publicKey);
|
|
2357
|
+
* ```
|
|
2358
|
+
*/
|
|
2359
|
+
export const falcon1024padded = /* @__PURE__ */ (() => genFalcon({
|
|
2360
|
+
...falcon1024opts,
|
|
2361
|
+
padded: true,
|
|
2362
|
+
maxS2Len: 1239,
|
|
2363
|
+
}))();
|
|
2364
|
+
// NOTE: for tests only, don't use
|
|
2365
|
+
export const __tests = /* @__PURE__ */ (() => ({
|
|
2366
|
+
BNORM_MAX,
|
|
2367
|
+
COMPLEX_ROOTS,
|
|
2368
|
+
Float,
|
|
2369
|
+
INV_SIGMA,
|
|
2370
|
+
SIGMA_MIN,
|
|
2371
|
+
getFloatPoly,
|
|
2372
|
+
cleanCPoly,
|
|
2373
|
+
falcon512: falcon512.__test,
|
|
2374
|
+
falcon512padded: falcon512padded.__test,
|
|
2375
|
+
falcon1024: falcon1024.__test,
|
|
2376
|
+
falcon1024padded: falcon1024padded.__test,
|
|
2377
|
+
}))();
|
|
2378
|
+
//# sourceMappingURL=falcon.js.map
|