@noble/post-quantum 0.6.0 → 0.6.1

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/src/falcon.ts CHANGED
@@ -24,6 +24,7 @@ import {
24
24
  } from '@noble/hashes/utils.js';
25
25
  import { genCrystals, type TypedCons } from './_crystals.ts';
26
26
  import {
27
+ baswap64If,
27
28
  type BytesCoderLen,
28
29
  cleanBytes,
29
30
  type Coder,
@@ -31,8 +32,9 @@ import {
31
32
  getMask,
32
33
  type Signer,
33
34
  type SigOpts,
34
- baswap64If,
35
35
  splitCoder,
36
+ type TArg,
37
+ type TRet,
36
38
  validateSigOpts,
37
39
  validateVerOpts,
38
40
  type VerOpts,
@@ -121,12 +123,12 @@ const bitsCoderMSB = <T extends TypedArray>(
121
123
  N: number,
122
124
  d: number,
123
125
  c: Coder<number, number>
124
- ): BytesCoderLen<T> => {
126
+ ): TRet<BytesCoderLen<T>> => {
125
127
  const mask = getMask(d);
126
128
  const bytesLen = d * (N / 8);
127
129
  return {
128
130
  bytesLen,
129
- encode: (poly: T): Uint8Array => {
131
+ encode: (poly: TArg<T>): TRet<Uint8Array> => {
130
132
  if (poly.length !== N) throw new Error(`wrong length: expected ${N}, got ${poly.length}`);
131
133
  const r = new Uint8Array(bytesLen);
132
134
  for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < poly.length; i++) {
@@ -134,38 +136,39 @@ const bitsCoderMSB = <T extends TypedArray>(
134
136
  bufLen += d;
135
137
  for (; bufLen >= 8; bufLen -= 8) r[pos++] = (buf >>> (bufLen - 8)) & 0xff;
136
138
  }
137
- return r;
139
+ return r as TRet<Uint8Array>;
138
140
  },
139
- decode: (bytes: Uint8Array): T => {
141
+ decode: (bytes: TArg<Uint8Array>): TRet<T> => {
140
142
  const r = newPoly(N);
141
143
  for (let i = 0, buf = 0, bufLen = 0, pos = 0; i < bytes.length; i++) {
142
144
  buf = (buf << 8) | bytes[i];
143
145
  bufLen += 8;
144
146
  for (; bufLen >= d; bufLen -= d) r[pos++] = c.decode((buf >>> (bufLen - d)) & mask);
145
147
  }
146
- return r;
148
+ return r as TRet<T>;
147
149
  },
148
- };
150
+ } as TRet<BytesCoderLen<T>>;
149
151
  };
150
152
  // Adds a single leading tag byte. Exact body validation is delegated to `restCoder.decode()`.
151
153
  // encode() zeroizes the temporary encoded body after copying, so wrapped encoders must return
152
154
  // owned scratch bytes rather than caller-owned buffers.
153
- const headerCoder = <T>(tag: number, restCoder: BytesCoderLen<T>): BytesCoderLen<T> => {
155
+ const headerCoder = <T>(tag: number, restCoder: TArg<BytesCoderLen<T>>): TRet<BytesCoderLen<T>> => {
156
+ const coder = restCoder as BytesCoderLen<T>;
154
157
  return {
155
- bytesLen: 1 + restCoder.bytesLen,
156
- encode(value: T): Uint8Array {
157
- const body = restCoder.encode(value);
158
+ bytesLen: 1 + coder.bytesLen,
159
+ encode(value: TArg<T>): TRet<Uint8Array> {
160
+ const body = coder.encode(value as T);
158
161
  const out = new Uint8Array(1 + body.length);
159
162
  out[0] = tag;
160
163
  out.set(body, 1);
161
164
  cleanBytes(body);
162
- return out;
165
+ return out as TRet<Uint8Array>;
163
166
  },
164
- decode(data: Uint8Array): T {
167
+ decode(data: TArg<Uint8Array>): TRet<T> {
165
168
  if (data[0] !== tag) throw new Error(`wrong tag: expected ${tag}, got 0x${data[0]}`);
166
- return restCoder.decode(data.subarray(1));
169
+ return coder.decode(data.subarray(1)) as TRet<T>;
167
170
  },
168
- };
171
+ } as TRet<BytesCoderLen<T>>;
169
172
  };
170
173
 
171
174
  // Fun, but overengineered. Hoping FIPS would fix this.
@@ -176,7 +179,7 @@ const headerCoder = <T>(tag: number, restCoder: BytesCoderLen<T>): BytesCoderLen
176
179
  const compCoder = (n: number) => {
177
180
  const LIMIT = 2047;
178
181
  return {
179
- encode(data: Int16Array): Uint8Array {
182
+ encode(data: TArg<Int16Array>): TRet<Uint8Array> {
180
183
  // Algorithm 17: Compress(s, slen) (Page 47)
181
184
  // Require: A polynomial s = Σ sᵢxⁱ ∈ Z[x] of degree < n, a string bitlength slen
182
185
  // Ensure: A compressed representation str of s of slen bits, or ⊥
@@ -216,9 +219,9 @@ const compCoder = (n: number) => {
216
219
  writeBits((v >>> 7) + 1, 1); // high (unary)
217
220
  }
218
221
  if (bufLen > 0) res.push((buf << (8 - bufLen)) & 0xff);
219
- return new Uint8Array(res);
222
+ return new Uint8Array(res) as TRet<Uint8Array>;
220
223
  },
221
- decode(data: Uint8Array): Int16Array {
224
+ decode(data: TArg<Uint8Array>): TRet<Int16Array> {
222
225
  // Algorithm 18: Decompress(str, slen), (Page 48)
223
226
  // Require: A bitstring str = (str[i])_{i=0,...,slen-1}, a bitlength slen
224
227
  // Ensure: A polynomial s = Σ sᵢxⁱ ∈ Z[x], or ⊥
@@ -260,7 +263,7 @@ const compCoder = (n: number) => {
260
263
  res[resPos] = sign ? -v : v;
261
264
  }
262
265
  if (buf) throw new Error('non-empty accumulator');
263
- return res;
266
+ return res as TRet<Int16Array>;
264
267
  },
265
268
  };
266
269
  };
@@ -268,12 +271,12 @@ const compCoder = (n: number) => {
268
271
  // Falcon padded-signature helper. encode() assumes `data.length <= len`; decode() strips trailing
269
272
  // zero padding and returns a subarray view, so it is not a generic byte-string codec.
270
273
  const pad = (len: number) => ({
271
- encode(data: Uint8Array) {
274
+ encode(data: TArg<Uint8Array>) {
272
275
  const res = new Uint8Array(len);
273
276
  res.set(data);
274
277
  return res;
275
278
  },
276
- decode(data: Uint8Array) {
279
+ decode(data: TArg<Uint8Array>) {
277
280
  let end = data.length;
278
281
  while (end > 0 && data[end - 1] === 0) end--;
279
282
  return data.subarray(0, end);
@@ -391,22 +394,22 @@ const ComplexArrInterleaved = {
391
394
  },
392
395
  };
393
396
  // Alias a Float64Array as bytes for the root-table hash pin; not a portable serialization.
394
- const u8f = (arr: Float64Array): Uint8Array =>
395
- new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
397
+ const u8f = (arr: TArg<Float64Array>): TRet<Uint8Array> =>
398
+ new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength) as TRet<Uint8Array>;
396
399
 
397
400
  // Alias bytes as Float64Array lanes. Falcon's exact binary64 tables are stored as little-endian
398
401
  // payload bytes, so BE runtimes must decode lane-by-lane instead of aliasing host-endian floats.
399
402
  // Copy/truncate to whole 8-byte lanes first
400
403
  // so BE byte swaps cannot mutate caller-owned bytes
401
404
  // or read a partial float.
402
- const f64a = (arr: Uint8Array): Float64Array =>
405
+ const f64a = (arr: TArg<Uint8Array>): TRet<Float64Array> =>
403
406
  new Float64Array(
404
407
  baswap64If(Uint8Array.from(arr.subarray(0, Math.floor(arr.byteLength / 8) * 8))).buffer
405
- );
408
+ ) as TRet<Float64Array>;
406
409
 
407
410
  // Exact big-endian binary64 hex helper for constants. Only decode() is currently used; malformed
408
411
  // inputs fail through lower-level hex / DataView checks instead of an explicit wrapper guard.
409
- const Float = {
412
+ const Float = /* @__PURE__ */ Object.freeze({
410
413
  encode(n: number): string {
411
414
  const bytes = new Uint8Array(8);
412
415
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
@@ -418,7 +421,7 @@ const Float = {
418
421
  const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
419
422
  return view.getFloat64(0, false);
420
423
  },
421
- };
424
+ });
422
425
  // Decode a 64-bit bigint bit pattern into the exact binary64 value.
423
426
  const f64b = (n: bigint): number => Float.decode(numberToHexUnpadded(n));
424
427
 
@@ -426,8 +429,6 @@ const f64b = (n: bigint): number => Float.decode(numberToHexUnpadded(n));
426
429
  type SignatureRaw = { msg: Uint8Array; nonce: Uint8Array; s2: Uint8Array };
427
430
  type BPoly = bigint[];
428
431
  type FPoly = Float64Array;
429
- // Keep these non-generic to match the rest of noble;
430
- // newer typed-array defs get handled at call sites.
431
432
  type SPoly = Int8Array; // Small poly (f/g/F/G)
432
433
  type IPoly = Uint16Array; // Integer poly mod Q
433
434
 
@@ -510,7 +511,7 @@ const gauss_1024_12289 = [
510
511
 
511
512
  // Exact binary64 1/sigma payloads from round-3 fpr.h. Nearby decimal spellings round 1 ULP low in
512
513
  // JS, so keep these as decoded bit patterns and recheck the raw payloads after edits.
513
- const INV_SIGMA = [
514
+ const INV_SIGMA = /* @__PURE__ */ Object.freeze([
514
515
  0.0, // unused
515
516
  f64b(BigInt('4574611497772390042')),
516
517
  f64b(BigInt('4574501679055810265')),
@@ -522,12 +523,12 @@ const INV_SIGMA = [
522
523
  f64b(BigInt('4573721358406441454')),
523
524
  f64b(BigInt('4573606369665796042')),
524
525
  f64b(BigInt('4573496814039276259')),
525
- ];
526
+ ]);
526
527
 
527
528
  // Exact binary64 sigma_min constants from round-3 fpr.h indexed by logn; despite one PQClean
528
529
  // summary comment, these are sigma_min itself, not 1/sigma_min, which is why this table stays
529
530
  // separate from INV_SIGMA.
530
- const SIGMA_MIN = [
531
+ const SIGMA_MIN = /* @__PURE__ */ Object.freeze([
531
532
  0.0, // unused
532
533
  f64b(BigInt('4607707126469777035')),
533
534
  f64b(BigInt('4607777455861499430')),
@@ -539,7 +540,7 @@ const SIGMA_MIN = [
539
540
  f64b(BigInt('4608340089478362016')),
540
541
  f64b(BigInt('4608433670533905013')),
541
542
  f64b(BigInt('4608525754002622308')),
542
- ];
543
+ ]);
543
544
 
544
545
  // Falcon Table 3.1 RCDT values for chi, split into 24-bit limbs; storage is [high, mid, low],
545
546
  // so gaussian0() intentionally compares them against v0, v1, v2 in reverse order. The final
@@ -1018,19 +1019,19 @@ function getIntPoly(logn: number) {
1018
1019
  });
1019
1020
  // Keep Falcon source compatible with older TS parsers: avoid spelling newer
1020
1021
  // `Uint16Array<ArrayBuffer>` syntax directly and cast the callee side at the boundary.
1021
- const ntt = (r: IPoly): IPoly => (NTT.encode as any)(r);
1022
- const intt = (r: IPoly): IPoly => (NTT.decode as any)(r);
1022
+ const ntt = (r: TArg<IPoly>): TRet<IPoly> => (NTT.encode as any)(r);
1023
+ const intt = (r: TArg<IPoly>): TRet<IPoly> => (NTT.decode as any)(r);
1023
1024
  // Falcon integer helpers mutate their first argument in place; div() also performs intt()
1024
1025
  // before returning, so callers must treat these as owned-temporary transforms, not pure helpers.
1025
1026
  // Centered representatives are in [-6144, 6144] for odd q = 12289,
1026
1027
  // not a generic [-q/2, q/2] range.
1027
1028
  const signedCoder = {
1028
- encode: (p: IPoly) => Int16Array.from(p, (x) => smod(x)),
1029
- decode: (p: SPoly | Int16Array) => Uint16Array.from(p, (x) => mod(x)),
1029
+ encode: (p: TArg<IPoly>) => Int16Array.from(p, (x) => smod(x)),
1030
+ decode: (p: TArg<SPoly | Int16Array>) => Uint16Array.from(p, (x) => mod(x)),
1030
1031
  };
1031
1032
  const intPoly = {
1032
1033
  create: newPoly,
1033
- smallSqnorm(f: SPoly) {
1034
+ smallSqnorm(f: TArg<SPoly>) {
1034
1035
  let s = 0;
1035
1036
  let ng = 0;
1036
1037
  for (let u = 0; u < n; u++) {
@@ -1040,7 +1041,7 @@ function getIntPoly(logn: number) {
1040
1041
  }
1041
1042
  return (s | -(ng >>> 31)) >>> 0;
1042
1043
  },
1043
- isShort(s1: Int16Array, s2: Int16Array) {
1044
+ isShort(s1: TArg<Int16Array>, s2: TArg<Int16Array>) {
1044
1045
  let s = 0 >>> 0;
1045
1046
  let ng = 0 >>> 0;
1046
1047
  for (let u = 0; u < n; u++) {
@@ -1054,24 +1055,24 @@ function getIntPoly(logn: number) {
1054
1055
  if (ng & 0x80000000) s = 0xffffffff;
1055
1056
  return s <= L2BOUND[logn];
1056
1057
  },
1057
- sub(a: IPoly, b: IPoly): IPoly {
1058
+ sub(a: TArg<IPoly>, b: TArg<IPoly>): TRet<IPoly> {
1058
1059
  for (let i = 0; i < n; i++) a[i] = mod(a[i] - b[i]);
1059
- return a;
1060
+ return a as TRet<IPoly>;
1060
1061
  },
1061
1062
  ntt,
1062
1063
  intt,
1063
- toMontgomery(d: IPoly): IPoly {
1064
+ toMontgomery(d: TArg<IPoly>): TRet<IPoly> {
1064
1065
  for (let i = 0; i < n; i++) d[i] = intField.mul(d[i], R2);
1065
- return d;
1066
+ return d as TRet<IPoly>;
1066
1067
  },
1067
- mul(f: IPoly, d: IPoly): IPoly {
1068
+ mul(f: TArg<IPoly>, d: TArg<IPoly>): TRet<IPoly> {
1068
1069
  for (let i = 0; i < n; i++) f[i] = intField.mul(f[i], d[i]);
1069
- return f;
1070
+ return f as TRet<IPoly>;
1070
1071
  },
1071
- div(f: IPoly, d: IPoly): IPoly {
1072
+ div(f: TArg<IPoly>, d: TArg<IPoly>): TRet<IPoly> {
1072
1073
  for (let i = 0; i < n; i++) f[i] = intField.div(f[i], d[i]);
1073
1074
  this.intt(f);
1074
- return f;
1075
+ return f as TRet<IPoly>;
1075
1076
  },
1076
1077
  };
1077
1078
  return { newPoly, intPoly, signedCoder };
@@ -1127,19 +1128,19 @@ function getFloatPoly(logn: number) {
1127
1128
  const fftOpts = { N: N_COMPLEX, invertButterflies: true, skipStages: 0, brp: false };
1128
1129
  const inv = 1.0 / N_COMPLEX;
1129
1130
  return {
1130
- to: (f: FPoly) => ComplexArr.decode(Array.from(f)),
1131
- from: (f: CPoly): FPoly => new Float64Array(ComplexArr.encode(f)),
1131
+ to: (f: TArg<FPoly>) => ComplexArr.decode(Array.from(f)),
1132
+ from: (f: CPoly): TRet<FPoly> => new Float64Array(ComplexArr.encode(f)) as TRet<FPoly>,
1132
1133
  // Runtime callers also pass HashToPoint's Uint16Array output here;
1133
1134
  // the implementation only needs a numeric typed-array shape,
1134
1135
  // even though the local type is narrower.
1135
- convSmall: (f: SPoly): CPoly => ComplexArr.decode(Array.from(f)),
1136
+ convSmall: (f: TArg<SPoly>): CPoly => ComplexArr.decode(Array.from(f)),
1136
1137
  add: (a: CPoly, b: CPoly): CPoly => a.map((i, j) => fComplex.add(i, b[j])),
1137
1138
  sub: (a: CPoly, b: CPoly): CPoly => a.map((i, j) => fComplex.sub(i, b[j])),
1138
1139
  neg: (a: CPoly): CPoly => a.map((i) => fComplex.neg(i)),
1139
1140
  mul: (a: CPoly, b: CPoly): CPoly => a.map((i, j) => fComplex.mul(i, b[j])),
1140
1141
  conj: (a: CPoly): CPoly => a.map((i) => fComplex.conj(i)),
1141
1142
  mulConst: (a: CPoly, x: number): CPoly => a.map((i) => fComplex.scale(i, x)),
1142
- scaleNorm: (a: CPoly, b: FPoly): CPoly => a.map((i, j) => fComplex.scale(i, b[j])),
1143
+ scaleNorm: (a: CPoly, b: TArg<FPoly>): CPoly => a.map((i, j) => fComplex.scale(i, b[j])),
1143
1144
  invNorm: (a: CPoly, b: CPoly) =>
1144
1145
  new Float64Array(a.map((i, j) => 1.0 / fComplex.magSqSum(i, b[j]))),
1145
1146
  FFT: (f: CPoly): CPoly =>
@@ -1199,7 +1200,8 @@ type FalconOpts = {
1199
1200
  maxS2Len: number;
1200
1201
  };
1201
1202
 
1202
- type FalconSigOpts = SigOpts & { random?: typeof randomBytes };
1203
+ type FalconRandom = (bytesLength?: number) => TRet<Uint8Array>;
1204
+ type FalconSigOpts = SigOpts & { random?: FalconRandom };
1203
1205
  /** Falcon attached-signature API. */
1204
1206
  export type FalconAttached = CryptoKeys & {
1205
1207
  /** Key lengths plus the 48-byte sampler-seed hook for signing. */
@@ -1227,7 +1229,7 @@ export type Falcon = Signer & {
1227
1229
  attached: FalconAttached;
1228
1230
  };
1229
1231
 
1230
- function genFalcon(opts: FalconOpts): Falcon {
1232
+ function genFalcon(opts: FalconOpts): TRet<Falcon> {
1231
1233
  const { N } = opts;
1232
1234
  const logn = Math.log2(N);
1233
1235
  const id = <T>(n: T): T => n;
@@ -1544,14 +1546,14 @@ function genFalcon(opts: FalconOpts): Falcon {
1544
1546
  }) as BytesCoderLen<IPoly>;
1545
1547
  return {
1546
1548
  bytesLen: coder.bytesLen,
1547
- encode(poly: Uint16Array) {
1549
+ encode(poly: TArg<Uint16Array>) {
1548
1550
  // Keep these raw checks in sync with Q:
1549
1551
  // Falcon public-key coefficients must stay in [0, q - 1].
1550
1552
  for (let i = 0; i < poly.length; i++)
1551
1553
  if (poly[i] >= 12289) throw new Error('public key coeff out of range');
1552
1554
  return coder.encode(poly);
1553
1555
  },
1554
- decode(bytes: Uint8Array) {
1556
+ decode(bytes: TArg<Uint8Array>) {
1555
1557
  // Round-3 Falcon requires exact body length here;
1556
1558
  // otherwise truncated keys decode as zero-padded
1557
1559
  // and overlong keys silently ignore the tail in this generic bit decoder.
@@ -1577,7 +1579,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1577
1579
  }) as BytesCoderLen<SPoly>;
1578
1580
  return {
1579
1581
  bytesLen: coder.bytesLen,
1580
- encode(poly: Int8Array) {
1582
+ encode(poly: TArg<Int8Array>) {
1581
1583
  // Secret-key trim encodings keep a symmetric signed range and reserve the most-negative
1582
1584
  // value as a non-canonical sentinel,
1583
1585
  // so encode() and decode() intentionally use different bounds.
@@ -1587,7 +1589,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1587
1589
  if (poly[i] < min || poly[i] > max) throw new Error('private key coeff out of range');
1588
1590
  return coder.encode(poly);
1589
1591
  },
1590
- decode(bytes: Uint8Array) {
1592
+ decode(bytes: TArg<Uint8Array>) {
1591
1593
  const poly = coder.decode(bytes);
1592
1594
  const min = -(1 << (bits - 1));
1593
1595
  for (let i = 0; i < poly.length; i++)
@@ -1606,7 +1608,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1606
1608
  splitCoder('falcon.secretKey', fgCoder, fgCoder, FGCoder)
1607
1609
  ) as BytesCoderLen<[Int8Array, Int8Array, Int8Array]>;
1608
1610
  const publicKeyCoder = headerCoder(0x00 + logn, modqCoder()) as BytesCoderLen<Uint16Array>;
1609
- const decodePaddedSig = (s2: Uint8Array) => {
1611
+ const decodePaddedSig = (s2: TArg<Uint8Array>) => {
1610
1612
  // The fixed padded form accepts only a canonical compressed payload
1611
1613
  // followed by an all-zero tail.
1612
1614
  const normalized = compCoder(N).encode(compCoder(N).decode(s2));
@@ -1614,7 +1616,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1614
1616
  if (s2[i] !== 0) throw new Error('non-zero padding');
1615
1617
  return normalized;
1616
1618
  };
1617
- const decodeUnpaddedSig = (s2: Uint8Array) => {
1619
+ const decodeUnpaddedSig = (s2: TArg<Uint8Array>) => {
1618
1620
  // Unpadded attached and detached signatures require the compressed payload to use its exact
1619
1621
  // canonical bitlength. Appending a zero tail and adjusting the outer container length must
1620
1622
  // still be rejected.
@@ -1628,7 +1630,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1628
1630
  const SignatureCoderBasic = (logn: number) => {
1629
1631
  const TYPE_BYTE = 0x20 + logn;
1630
1632
  return {
1631
- encode({ msg, nonce, s2 }: SignatureRaw): Uint8Array {
1633
+ encode({ msg, nonce, s2 }: TArg<SignatureRaw>): TRet<Uint8Array> {
1632
1634
  let compressed: Uint8Array = s2;
1633
1635
  const payloadLen = 1 + compressed.length;
1634
1636
  const totalLen = 2 + NONCELEN + msg.length + payloadLen;
@@ -1642,9 +1644,9 @@ function genFalcon(opts: FalconOpts): Falcon {
1642
1644
  i += msg.length;
1643
1645
  out[i++] = TYPE_BYTE;
1644
1646
  out.set(compressed, i);
1645
- return out;
1647
+ return out as TRet<Uint8Array>;
1646
1648
  },
1647
- decode(data: Uint8Array): SignatureRaw {
1649
+ decode(data: TArg<Uint8Array>): TRet<SignatureRaw> {
1648
1650
  if (!data || data.length < NONCELEN + 3) throw new Error('signature coder: wrong length');
1649
1651
  const len = (data[0] << 8) | data[1];
1650
1652
  const s2Len = len - 1;
@@ -1656,50 +1658,50 @@ function genFalcon(opts: FalconOpts): Falcon {
1656
1658
  const msg = data.subarray(2 + NONCELEN, 2 + NONCELEN + msgLen);
1657
1659
  const s2 = decodeUnpaddedSig(data.subarray(2 + NONCELEN + msgLen + 1));
1658
1660
  if (s2.length !== s2Len) throw new Error('signature coder: wrong s2 length');
1659
- return { msg, nonce, s2 };
1661
+ return { msg, nonce, s2 } as TRet<SignatureRaw>;
1660
1662
  },
1661
1663
  };
1662
1664
  };
1663
1665
  const SignatureCoderPadded = (logn: number) => {
1664
1666
  const sigLen = opts.paddedLen;
1665
1667
  return {
1666
- encode({ msg, nonce, s2 }: SignatureRaw): Uint8Array {
1668
+ encode({ msg, nonce, s2 }: TArg<SignatureRaw>): TRet<Uint8Array> {
1667
1669
  return headerCoder(
1668
1670
  0x30 + logn,
1669
1671
  splitCoder('falcon.signature', NONCELEN, sigLen, msg.length)
1670
1672
  ).encode([nonce, pad(sigLen).encode(s2), msg]);
1671
1673
  },
1672
- decode(data: Uint8Array): SignatureRaw {
1674
+ decode(data: TArg<Uint8Array>): TRet<SignatureRaw> {
1673
1675
  const msgLen = data.length - NONCELEN - sigLen - 1;
1674
1676
  const [nonce, s2, msg] = headerCoder(
1675
1677
  0x30 + logn,
1676
1678
  splitCoder('falcon.signature', NONCELEN, sigLen, msgLen)
1677
1679
  ).decode(data);
1678
- return { nonce, s2: decodeSig(s2), msg };
1680
+ return { nonce, s2: decodeSig(s2), msg } as TRet<SignatureRaw>;
1679
1681
  },
1680
1682
  };
1681
1683
  };
1682
1684
  // [ 1B header ] [ 40B nonce ] [ compressed_sig ]
1683
1685
  const SignatureCoderDetached = (logn: number) => {
1684
1686
  const sigLen = opts.padded ? opts.sigLen - 1 - NONCELEN : opts.detachedLen;
1685
- const getSigLen = (s2: Uint8Array) => (opts.padded ? sigLen : s2.length);
1687
+ const getSigLen = (s2: TArg<Uint8Array>) => (opts.padded ? sigLen : s2.length);
1686
1688
  return {
1687
- encode({ nonce, s2 }: { nonce: Uint8Array; s2: Uint8Array }): Uint8Array {
1689
+ encode({ nonce, s2 }: TArg<{ nonce: Uint8Array; s2: Uint8Array }>): TRet<Uint8Array> {
1688
1690
  return headerCoder(
1689
1691
  0x30 + logn,
1690
1692
  splitCoder('falcon.detachedSignature', NONCELEN, getSigLen(s2))
1691
1693
  ).encode([nonce, opts.padded ? pad(sigLen).encode(s2) : s2]);
1692
1694
  },
1693
- decode(data: Uint8Array): {
1695
+ decode(data: TArg<Uint8Array>): TRet<{
1694
1696
  nonce: Uint8Array;
1695
1697
  s2: Uint8Array;
1696
- } {
1698
+ }> {
1697
1699
  const [nonce, raw] = headerCoder(
1698
1700
  0x30 + logn,
1699
1701
  splitCoder('falcon.detachedSignature', NONCELEN, data.length - NONCELEN - 1)
1700
1702
  ).decode(data);
1701
1703
  const s2 = decodeSig(raw);
1702
- return { nonce, s2 };
1704
+ return { nonce, s2 } as TRet<{ nonce: Uint8Array; s2: Uint8Array }>;
1703
1705
  },
1704
1706
  };
1705
1707
  };
@@ -1708,13 +1710,13 @@ function genFalcon(opts: FalconOpts): Falcon {
1708
1710
  // otherwise malformed secret keys leak a raw arithmetic error.
1709
1711
  // Returns NTT(f) after the nonzero-lane check;
1710
1712
  // callers still apply f^{-1} via coefficient-wise division.
1711
- const invertF = (f: SPoly) => {
1713
+ const invertF = (f: TArg<SPoly>) => {
1712
1714
  const tt = intPoly.ntt(signedCoder.decode(f));
1713
1715
  for (let u = 0; u < N; u++)
1714
1716
  if (tt[u] === 0) throw new Error('invalid secretKey: non-invertible f');
1715
1717
  return tt;
1716
1718
  };
1717
- function computePublic(f: SPoly, g: SPoly) {
1719
+ function computePublic(f: TArg<SPoly>, g: TArg<SPoly>) {
1718
1720
  const tt = invertF(f);
1719
1721
  const h = intPoly.ntt(signedCoder.decode(g));
1720
1722
  // intPoly.div() returns to coefficient form via intt(), so public keys are encoded from the
@@ -1725,7 +1727,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1725
1727
  }
1726
1728
  // Reconstruct the omitted secret-key limb G as g*F/f mod q, then mirror round-3 Falcon's centered
1727
1729
  // reduction and small-coefficient check before using the completed basis for signing.
1728
- function completePrivate(f: SPoly, g: SPoly, F: SPoly) {
1730
+ function completePrivate(f: TArg<SPoly>, g: TArg<SPoly>, F: TArg<SPoly>) {
1729
1731
  let t1 = intPoly.toMontgomery(intPoly.ntt(signedCoder.decode(g)));
1730
1732
  const t2 = intPoly.ntt(signedCoder.decode(F));
1731
1733
  const tt = invertF(f);
@@ -1747,7 +1749,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1747
1749
  cleanBytes(t1, t2, tt);
1748
1750
  return G;
1749
1751
  }
1750
- function HashToPoint(nonce: Uint8Array, msg: Uint8Array) {
1752
+ function HashToPoint(nonce: TArg<Uint8Array>, msg: TArg<Uint8Array>): TRet<IPoly> {
1751
1753
  // Algorithm 3: HashToPoint(str, q, n)
1752
1754
  // (Page 31)
1753
1755
  // Require: A string str, a modulus q ≤ 2¹⁶, a degree n ∈ N*
@@ -1772,7 +1774,7 @@ function genFalcon(opts: FalconOpts): Falcon {
1772
1774
  let w = (tmp[0] << 8) | tmp[1];
1773
1775
  if (w < kQ) c[i++] = w % Q; // 8: cᵢ ← t mod q
1774
1776
  }
1775
- return c;
1777
+ return c as TRet<IPoly>;
1776
1778
  }
1777
1779
  // This is basically one sampling routine,
1778
1780
  // but it carries a lot of internal state and gets complex quickly.
@@ -2154,7 +2156,12 @@ function genFalcon(opts: FalconOpts): Falcon {
2154
2156
  }
2155
2157
  }
2156
2158
 
2157
- const signRaw = (sk: Uint8Array, msg: Uint8Array, maxLen: number, rnd = randomBytes) => {
2159
+ const signRaw = (
2160
+ sk: TArg<Uint8Array>,
2161
+ msg: TArg<Uint8Array>,
2162
+ maxLen: number,
2163
+ rnd: TArg<FalconRandom> = randomBytes
2164
+ ): TRet<SignatureRaw> => {
2158
2165
  // Algorithm 10: Sign(m, sk, [β²]), (Page 39)
2159
2166
  // Require: A message m, a secret key sk, a bound [β²]
2160
2167
  // Ensure: A signature sig of m
@@ -2226,7 +2233,7 @@ function genFalcon(opts: FalconOpts): Falcon {
2226
2233
  cleanBytes(s2comp);
2227
2234
  continue;
2228
2235
  }
2229
- return { s2: s2comp, nonce, msg };
2236
+ return { s2: s2comp, nonce, msg } as TRet<SignatureRaw>;
2230
2237
  }
2231
2238
  } finally {
2232
2239
  cleanBytes(s2);
@@ -2244,7 +2251,12 @@ function genFalcon(opts: FalconOpts): Falcon {
2244
2251
 
2245
2252
  // Raw helper: malformed encodings or wrong lengths still throw here; the public verify()/open()
2246
2253
  // wrappers decide whether to translate those failures into false or an exception.
2247
- const verifyRaw = (pk: Uint8Array, s2comp: Uint8Array, nonce: Uint8Array, msg: Uint8Array) => {
2254
+ const verifyRaw = (
2255
+ pk: TArg<Uint8Array>,
2256
+ s2comp: TArg<Uint8Array>,
2257
+ nonce: TArg<Uint8Array>,
2258
+ msg: TArg<Uint8Array>
2259
+ ) => {
2248
2260
  // Algorithm 16: Verify(m, sig, pk, [β²])
2249
2261
  // (Page 45)
2250
2262
  // Require: A message m, a signature sig = (r, s), a public key pk = h ∈ Zq[x]/(φ), a bound [β²]
@@ -2266,34 +2278,42 @@ function genFalcon(opts: FalconOpts): Falcon {
2266
2278
  return intPoly.isShort(signedCoder.encode(s1), s2); // 6: if ||(s₁, s₂)||² < [β²] then
2267
2279
  };
2268
2280
 
2269
- const info = { type: 'falcon' };
2270
- const keyLengths = {
2281
+ const info = Object.freeze({ type: 'falcon' });
2282
+ const keyLengths = Object.freeze({
2271
2283
  seed: 48,
2272
2284
  publicKey: publicKeyCoder.bytesLen,
2273
2285
  secretKey: secretKeyCoder.bytesLen,
2274
- };
2286
+ });
2275
2287
  // Noble exposes a 48-byte sampler-seed hook,
2276
2288
  // but Falcon still samples/encodes a separate 40-byte nonce per signature.
2277
- const getRnd = (opts: FalconSigOpts = {}) => {
2289
+ const getRnd = (opts: TArg<FalconSigOpts> = {}): TRet<FalconRandom> => {
2278
2290
  validateSigOpts(opts);
2279
2291
  if (opts.context !== undefined) throw new Error('context is not supported');
2280
- if (opts.random !== undefined) return opts.random;
2292
+ if (opts.random !== undefined) return opts.random as TRet<FalconRandom>;
2281
2293
  if (opts.extraEntropy === undefined) return randomBytes;
2282
2294
  const seed = opts.extraEntropy === false ? new Uint8Array(48) : opts.extraEntropy;
2283
2295
  abytes(seed, 48, 'opts.extraEntropy');
2284
2296
  const drbg = rngAesCtrDrbg256(seed);
2285
- return (len = 0) => drbg.randomBytes(len);
2297
+ return (len = 0) => drbg.randomBytes(len) as TRet<Uint8Array>;
2286
2298
  };
2287
- const checkVerOpts = (opts: VerOpts = {}) => {
2299
+ const checkVerOpts = (opts: TArg<VerOpts> = {}) => {
2288
2300
  validateVerOpts(opts);
2289
2301
  if (opts.context !== undefined) throw new Error('context is not supported');
2290
2302
  };
2291
- const tests = { publicKeyCoder, privateKeyCoder: secretKeyCoder, maxS2Len: opts.maxS2Len };
2303
+ const tests = Object.freeze({
2304
+ publicKeyCoder: Object.freeze(publicKeyCoder),
2305
+ privateKeyCoder: Object.freeze(secretKeyCoder),
2306
+ maxS2Len: opts.maxS2Len,
2307
+ });
2292
2308
  // `signRand` documents only the sampler-seed input length;
2293
2309
  // detached/attached signatures still include their own 40-byte nonce.
2294
- const attachedLengths = { ...keyLengths, signRand: 48 };
2295
- const lengths = opts.padded ? { ...attachedLengths, signature: opts.sigLen } : attachedLengths;
2296
- const keygen = (seed?: Uint8Array) => {
2310
+ const attachedLengths = Object.freeze({ ...keyLengths, signRand: 48 });
2311
+ const lengths = opts.padded
2312
+ ? Object.freeze({ ...attachedLengths, signature: opts.sigLen })
2313
+ : attachedLengths;
2314
+ const keygen = (
2315
+ seed?: TArg<Uint8Array>
2316
+ ): TRet<{ publicKey: Uint8Array; secretKey: Uint8Array }> => {
2297
2317
  const randSeed = seed === undefined;
2298
2318
  if (randSeed) seed = randomBytes(48);
2299
2319
  abytes(seed!, 48, 'seed');
@@ -2302,20 +2322,27 @@ function genFalcon(opts: FalconOpts): Falcon {
2302
2322
  const pk = publicKeyCoder.encode(pub);
2303
2323
  if (randSeed) cleanBytes(seed!);
2304
2324
  cleanBytes(f, g, F, _G);
2305
- return { publicKey: pk, secretKey: sk };
2325
+ return { publicKey: pk, secretKey: sk } as TRet<{
2326
+ publicKey: Uint8Array;
2327
+ secretKey: Uint8Array;
2328
+ }>;
2306
2329
  };
2307
- const getPublicKey = (sk: Uint8Array) => {
2330
+ const getPublicKey = (sk: TArg<Uint8Array>): TRet<Uint8Array> => {
2308
2331
  const [f, g, F] = secretKeyCoder.decode(sk);
2309
2332
  try {
2310
2333
  const h = computePublic(f, g);
2311
2334
  cleanBytes(f, g, F);
2312
- return publicKeyCoder.encode(h);
2335
+ return publicKeyCoder.encode(h) as TRet<Uint8Array>;
2313
2336
  } catch (e) {
2314
2337
  cleanBytes(f, g, F);
2315
2338
  throw e;
2316
2339
  }
2317
2340
  };
2318
- const sign = (msg: Uint8Array, sk: Uint8Array, sigOpts: FalconSigOpts = {}) => {
2341
+ const sign = (
2342
+ msg: TArg<Uint8Array>,
2343
+ sk: TArg<Uint8Array>,
2344
+ sigOpts: TArg<FalconSigOpts> = {}
2345
+ ): TRet<Uint8Array> => {
2319
2346
  const { s2, nonce } = signRaw(sk, msg, opts.maxS2Len, getRnd(sigOpts));
2320
2347
  return SignatureCoderDetached(logn).encode({ nonce, s2 });
2321
2348
  };
@@ -2324,7 +2351,12 @@ function genFalcon(opts: FalconOpts): Falcon {
2324
2351
  * and well-formed signatures that do not validate. Throws on malformed API argument types or
2325
2352
  * unsupported verification options.
2326
2353
  */
2327
- const verify = (sig: Uint8Array, msg: Uint8Array, pk: Uint8Array, verOpts: VerOpts = {}) => {
2354
+ const verify = (
2355
+ sig: TArg<Uint8Array>,
2356
+ msg: TArg<Uint8Array>,
2357
+ pk: TArg<Uint8Array>,
2358
+ verOpts: TArg<VerOpts> = {}
2359
+ ) => {
2328
2360
  checkVerOpts(verOpts);
2329
2361
  abytes(sig);
2330
2362
  abytes(msg);
@@ -2336,16 +2368,16 @@ function genFalcon(opts: FalconOpts): Falcon {
2336
2368
  return false;
2337
2369
  }
2338
2370
  };
2339
- const attached: FalconAttached = {
2371
+ const attached: TRet<FalconAttached> = Object.freeze({
2340
2372
  info,
2341
2373
  lengths: attachedLengths,
2342
2374
  keygen,
2343
2375
  getPublicKey,
2344
- seal(msg: Uint8Array, sk: Uint8Array, sigOpts: FalconSigOpts = {}) {
2376
+ seal(msg: TArg<Uint8Array>, sk: TArg<Uint8Array>, sigOpts: TArg<FalconSigOpts> = {}) {
2345
2377
  const { s2, nonce } = signRaw(sk, msg, opts.maxS2Len, getRnd(sigOpts));
2346
2378
  return SignatureCoder.encode({ msg, nonce, s2 });
2347
2379
  },
2348
- open(sig: Uint8Array, pk: Uint8Array, verOpts: VerOpts = {}) {
2380
+ open(sig: TArg<Uint8Array>, pk: TArg<Uint8Array>, verOpts: TArg<VerOpts> = {}) {
2349
2381
  checkVerOpts(verOpts);
2350
2382
  const { s2, nonce, msg } = SignatureCoder.decode(sig);
2351
2383
  // Zero-copy API: returned message aliases the caller-provided signature buffer.
@@ -2353,7 +2385,7 @@ function genFalcon(opts: FalconOpts): Falcon {
2353
2385
  if (verifyRaw(pk, s2, nonce, msg)) return msg;
2354
2386
  throw new Error('invalid signature');
2355
2387
  },
2356
- };
2388
+ });
2357
2389
  const res = {
2358
2390
  info,
2359
2391
  lengths,
@@ -2364,7 +2396,7 @@ function genFalcon(opts: FalconOpts): Falcon {
2364
2396
  verify,
2365
2397
  };
2366
2398
  (res as any).__test = tests;
2367
- return res;
2399
+ return Object.freeze(res);
2368
2400
  }
2369
2401
 
2370
2402
  const falcon512opts = {
@@ -2389,7 +2421,7 @@ const falcon512opts = {
2389
2421
  * falcon512.verify(sig, msg, publicKey);
2390
2422
  * ```
2391
2423
  */
2392
- export const falcon512: Falcon = /* @__PURE__ */ (() =>
2424
+ export const falcon512: TRet<Falcon> = /* @__PURE__ */ (() =>
2393
2425
  genFalcon({ ...falcon512opts, maxS2Len: 711 }))();
2394
2426
  /**
2395
2427
  * Falcon-512 padded detached-signature API with the attached helper exposed as `.attached`.
@@ -2402,7 +2434,7 @@ export const falcon512: Falcon = /* @__PURE__ */ (() =>
2402
2434
  * falcon512padded.verify(sig, msg, publicKey);
2403
2435
  * ```
2404
2436
  */
2405
- export const falcon512padded: Falcon = /* @__PURE__ */ (() =>
2437
+ export const falcon512padded: TRet<Falcon> = /* @__PURE__ */ (() =>
2406
2438
  genFalcon({
2407
2439
  ...falcon512opts,
2408
2440
  padded: true,
@@ -2431,7 +2463,7 @@ const falcon1024opts = {
2431
2463
  * falcon1024.verify(sig, msg, publicKey);
2432
2464
  * ```
2433
2465
  */
2434
- export const falcon1024: Falcon = /* @__PURE__ */ (() =>
2466
+ export const falcon1024: TRet<Falcon> = /* @__PURE__ */ (() =>
2435
2467
  genFalcon({
2436
2468
  ...falcon1024opts,
2437
2469
  maxS2Len: 1421,
@@ -2447,7 +2479,7 @@ export const falcon1024: Falcon = /* @__PURE__ */ (() =>
2447
2479
  * falcon1024padded.verify(sig, msg, publicKey);
2448
2480
  * ```
2449
2481
  */
2450
- export const falcon1024padded: Falcon = /* @__PURE__ */ (() =>
2482
+ export const falcon1024padded: TRet<Falcon> = /* @__PURE__ */ (() =>
2451
2483
  genFalcon({
2452
2484
  ...falcon1024opts,
2453
2485
  padded: true,
@@ -2455,16 +2487,17 @@ export const falcon1024padded: Falcon = /* @__PURE__ */ (() =>
2455
2487
  }))();
2456
2488
 
2457
2489
  // NOTE: for tests only, don't use
2458
- export const __tests: any = /* @__PURE__ */ (() => ({
2459
- BNORM_MAX,
2460
- COMPLEX_ROOTS,
2461
- Float,
2462
- INV_SIGMA,
2463
- SIGMA_MIN,
2464
- getFloatPoly,
2465
- cleanCPoly,
2466
- falcon512: (falcon512 as any).__test,
2467
- falcon512padded: (falcon512padded as any).__test,
2468
- falcon1024: (falcon1024 as any).__test,
2469
- falcon1024padded: (falcon1024padded as any).__test,
2470
- }))();
2490
+ export const __tests: any = /* @__PURE__ */ (() =>
2491
+ Object.freeze({
2492
+ BNORM_MAX,
2493
+ COMPLEX_ROOTS,
2494
+ Float,
2495
+ INV_SIGMA,
2496
+ SIGMA_MIN,
2497
+ getFloatPoly,
2498
+ cleanCPoly,
2499
+ falcon512: (falcon512 as any).__test,
2500
+ falcon512padded: (falcon512padded as any).__test,
2501
+ falcon1024: (falcon1024 as any).__test,
2502
+ falcon1024padded: (falcon1024padded as any).__test,
2503
+ }))();