@noble/post-quantum 0.6.0 → 0.7.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 +106 -80
- package/_crystals.d.ts +23 -16
- package/_crystals.js +50 -12
- package/falcon.d.ts +7 -8
- package/falcon.js +109 -74
- package/hybrid.d.ts +21 -32
- package/hybrid.js +157 -78
- package/index.d.ts +0 -1
- package/index.js +8 -1
- package/ml-dsa.d.ts +35 -9
- package/ml-dsa.js +116 -43
- package/ml-kem.d.ts +46 -5
- package/ml-kem.js +175 -67
- package/package.json +10 -18
- package/slh-dsa.d.ts +44 -24
- package/slh-dsa.js +150 -84
- package/src/_crystals.ts +86 -35
- package/src/falcon.ts +230 -169
- package/src/hybrid.ts +243 -129
- package/src/index.ts +8 -0
- package/src/ml-dsa.ts +194 -87
- package/src/ml-kem.ts +283 -122
- package/src/slh-dsa.ts +310 -182
- package/src/utils.ts +244 -48
- package/utils.d.ts +99 -24
- package/utils.js +92 -24
- package/_crystals.d.ts.map +0 -1
- package/_crystals.js.map +0 -1
- package/falcon.d.ts.map +0 -1
- package/falcon.js.map +0 -1
- package/hybrid.d.ts.map +0 -1
- package/hybrid.js.map +0 -1
- package/index.d.ts.map +0 -1
- package/index.js.map +0 -1
- package/ml-dsa.d.ts.map +0 -1
- package/ml-dsa.js.map +0 -1
- package/ml-kem.d.ts.map +0 -1
- package/ml-kem.js.map +0 -1
- package/slh-dsa.d.ts.map +0 -1
- package/slh-dsa.js.map +0 -1
- package/utils.d.ts.map +0 -1
- package/utils.js.map +0 -1
package/falcon.js
CHANGED
|
@@ -12,7 +12,7 @@ import { bytesToNumberLE, numberToHexUnpadded } from '@noble/curves/utils.js';
|
|
|
12
12
|
import { shake256 } from '@noble/hashes/sha3.js';
|
|
13
13
|
import { abytes, bytesToHex, createView, hexToBytes, randomBytes, swap32IfBE, u32, u8, } from '@noble/hashes/utils.js';
|
|
14
14
|
import { genCrystals } from "./_crystals.js";
|
|
15
|
-
import { cleanBytes, getMask,
|
|
15
|
+
import { baswap64If, cleanBytes, getMask, splitCoder, validateSigOpts, validateVerOpts, } from "./utils.js";
|
|
16
16
|
/*
|
|
17
17
|
FIPS-206 would likely improve the situation with spec.
|
|
18
18
|
|
|
@@ -124,10 +124,11 @@ const bitsCoderMSB = (newPoly, N, d, c) => {
|
|
|
124
124
|
// encode() zeroizes the temporary encoded body after copying, so wrapped encoders must return
|
|
125
125
|
// owned scratch bytes rather than caller-owned buffers.
|
|
126
126
|
const headerCoder = (tag, restCoder) => {
|
|
127
|
+
const coder = restCoder;
|
|
127
128
|
return {
|
|
128
|
-
bytesLen: 1 +
|
|
129
|
+
bytesLen: 1 + coder.bytesLen,
|
|
129
130
|
encode(value) {
|
|
130
|
-
const body =
|
|
131
|
+
const body = coder.encode(value);
|
|
131
132
|
const out = new Uint8Array(1 + body.length);
|
|
132
133
|
out[0] = tag;
|
|
133
134
|
out.set(body, 1);
|
|
@@ -137,7 +138,7 @@ const headerCoder = (tag, restCoder) => {
|
|
|
137
138
|
decode(data) {
|
|
138
139
|
if (data[0] !== tag)
|
|
139
140
|
throw new Error(`wrong tag: expected ${tag}, got 0x${data[0]}`);
|
|
140
|
-
return
|
|
141
|
+
return coder.decode(data.subarray(1));
|
|
141
142
|
},
|
|
142
143
|
};
|
|
143
144
|
};
|
|
@@ -377,7 +378,7 @@ const u8f = (arr) => new Uint8Array(arr.buffer, arr.byteOffset, arr.byteLength);
|
|
|
377
378
|
const f64a = (arr) => new Float64Array(baswap64If(Uint8Array.from(arr.subarray(0, Math.floor(arr.byteLength / 8) * 8))).buffer);
|
|
378
379
|
// Exact big-endian binary64 hex helper for constants. Only decode() is currently used; malformed
|
|
379
380
|
// inputs fail through lower-level hex / DataView checks instead of an explicit wrapper guard.
|
|
380
|
-
const Float = {
|
|
381
|
+
const Float = /* @__PURE__ */ Object.freeze({
|
|
381
382
|
encode(n) {
|
|
382
383
|
const bytes = new Uint8Array(8);
|
|
383
384
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
@@ -389,7 +390,7 @@ const Float = {
|
|
|
389
390
|
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
390
391
|
return view.getFloat64(0, false);
|
|
391
392
|
},
|
|
392
|
-
};
|
|
393
|
+
});
|
|
393
394
|
// Decode a 64-bit bigint bit pattern into the exact binary64 value.
|
|
394
395
|
const f64b = (n) => Float.decode(numberToHexUnpadded(n));
|
|
395
396
|
// Constants
|
|
@@ -402,6 +403,17 @@ const Q = 12289; // 12 * 1024 + 1
|
|
|
402
403
|
// Falcon's midpoint floor(q/2); the only live use is the mirrored G-reconstruction reduction below.
|
|
403
404
|
const Qhalf = Q >> 1;
|
|
404
405
|
const QBig = BigInt(Q);
|
|
406
|
+
const _0n = /* @__PURE__ */ BigInt(0);
|
|
407
|
+
const _1n = /* @__PURE__ */ BigInt(1);
|
|
408
|
+
const _10n = /* @__PURE__ */ BigInt(10);
|
|
409
|
+
const _25n = /* @__PURE__ */ BigInt(25);
|
|
410
|
+
const _31n = /* @__PURE__ */ BigInt(31);
|
|
411
|
+
const _32n = /* @__PURE__ */ BigInt(32);
|
|
412
|
+
const _63n = /* @__PURE__ */ BigInt(63);
|
|
413
|
+
const _64n = /* @__PURE__ */ BigInt(64);
|
|
414
|
+
// Low 32 bits and low 63 bits of a bigint, used by the chacha20 counter and gaussian sampler.
|
|
415
|
+
const MASK_32n = /* @__PURE__ */ BigInt('0xffffffff');
|
|
416
|
+
const MASK_63n = /* @__PURE__ */ BigInt('0x7fffffffffffffff');
|
|
405
417
|
//const R = 4091; // 2^16 mod q
|
|
406
418
|
// This 16-bit Montgomery kernel uses R = 2^16, so mul(x, R2) converts x into Montgomery form.
|
|
407
419
|
const R2 = 10952; // 2^32 mod q
|
|
@@ -438,38 +450,38 @@ const BITLENGTH = [
|
|
|
438
450
|
// Smaller Falcon dimensions reuse the N = 1024, q = 12289 table by summing 2^(10-logn) draws.
|
|
439
451
|
// The trailing 0 sentinel guarantees gaussSingle()
|
|
440
452
|
// always selects a tail bucket when x = 0 is missed.
|
|
441
|
-
const gauss_1024_12289 = [
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
];
|
|
453
|
+
const gauss_1024_12289 = /* @__PURE__ */ [
|
|
454
|
+
'1283868770400643928',
|
|
455
|
+
'6416574995475331444',
|
|
456
|
+
'4078260278032692663',
|
|
457
|
+
'2353523259288686585',
|
|
458
|
+
'1227179971273316331',
|
|
459
|
+
'575931623374121527',
|
|
460
|
+
'242543240509105209',
|
|
461
|
+
'91437049221049666',
|
|
462
|
+
'30799446349977173',
|
|
463
|
+
'9255276791179340',
|
|
464
|
+
'2478152334826140',
|
|
465
|
+
'590642893610164',
|
|
466
|
+
'125206034929641',
|
|
467
|
+
'23590435911403',
|
|
468
|
+
'3948334035941',
|
|
469
|
+
'586753615614',
|
|
470
|
+
'77391054539',
|
|
471
|
+
'9056793210',
|
|
472
|
+
'940121950',
|
|
473
|
+
'86539696',
|
|
474
|
+
'7062824',
|
|
475
|
+
'510971',
|
|
476
|
+
'32764',
|
|
477
|
+
'1862',
|
|
478
|
+
'94',
|
|
479
|
+
'4',
|
|
480
|
+
'0',
|
|
481
|
+
].map(BigInt);
|
|
470
482
|
// Exact binary64 1/sigma payloads from round-3 fpr.h. Nearby decimal spellings round 1 ULP low in
|
|
471
483
|
// JS, so keep these as decoded bit patterns and recheck the raw payloads after edits.
|
|
472
|
-
const INV_SIGMA = [
|
|
484
|
+
const INV_SIGMA = /* @__PURE__ */ Object.freeze([
|
|
473
485
|
0.0, // unused
|
|
474
486
|
f64b(BigInt('4574611497772390042')),
|
|
475
487
|
f64b(BigInt('4574501679055810265')),
|
|
@@ -481,11 +493,11 @@ const INV_SIGMA = [
|
|
|
481
493
|
f64b(BigInt('4573721358406441454')),
|
|
482
494
|
f64b(BigInt('4573606369665796042')),
|
|
483
495
|
f64b(BigInt('4573496814039276259')),
|
|
484
|
-
];
|
|
496
|
+
]);
|
|
485
497
|
// Exact binary64 sigma_min constants from round-3 fpr.h indexed by logn; despite one PQClean
|
|
486
498
|
// summary comment, these are sigma_min itself, not 1/sigma_min, which is why this table stays
|
|
487
499
|
// separate from INV_SIGMA.
|
|
488
|
-
const SIGMA_MIN = [
|
|
500
|
+
const SIGMA_MIN = /* @__PURE__ */ Object.freeze([
|
|
489
501
|
0.0, // unused
|
|
490
502
|
f64b(BigInt('4607707126469777035')),
|
|
491
503
|
f64b(BigInt('4607777455861499430')),
|
|
@@ -497,7 +509,7 @@ const SIGMA_MIN = [
|
|
|
497
509
|
f64b(BigInt('4608340089478362016')),
|
|
498
510
|
f64b(BigInt('4608433670533905013')),
|
|
499
511
|
f64b(BigInt('4608525754002622308')),
|
|
500
|
-
];
|
|
512
|
+
]);
|
|
501
513
|
// Falcon Table 3.1 RCDT values for chi, split into 24-bit limbs; storage is [high, mid, low],
|
|
502
514
|
// so gaussian0() intentionally compares them against v0, v1, v2 in reverse order. The final
|
|
503
515
|
// RCDT[18] = 0 row is omitted because the algorithm iterates only over i = 0..17.
|
|
@@ -1146,9 +1158,9 @@ function genFalcon(opts) {
|
|
|
1146
1158
|
let val = 0;
|
|
1147
1159
|
for (let i = 0; i < g; i++) {
|
|
1148
1160
|
const r128 = bytesToNumberLE(this.shake.xof(16));
|
|
1149
|
-
const r1 = r128 &
|
|
1150
|
-
const r2 = (r128 >>
|
|
1151
|
-
const sign = Number((r128 >>
|
|
1161
|
+
const r1 = r128 & MASK_63n;
|
|
1162
|
+
const r2 = (r128 >> _64n) & MASK_63n;
|
|
1163
|
+
const sign = Number((r128 >> _63n) & _1n);
|
|
1152
1164
|
let f = r1 < gauss_1024_12289[0] ? 1 : 0;
|
|
1153
1165
|
let v = 0;
|
|
1154
1166
|
for (let k = 1; k < gauss_1024_12289.length; k++) {
|
|
@@ -1185,7 +1197,7 @@ function genFalcon(opts) {
|
|
|
1185
1197
|
const n = 1 << logn;
|
|
1186
1198
|
const d = new Array(n >> 1);
|
|
1187
1199
|
for (let k = 0; k < n; k += 2) {
|
|
1188
|
-
let s =
|
|
1200
|
+
let s = _0n;
|
|
1189
1201
|
for (let i = 0; i <= k; i += 2)
|
|
1190
1202
|
s += a[i] * a[k - i];
|
|
1191
1203
|
for (let i = k + 2; i < n; i += 2)
|
|
@@ -1193,7 +1205,7 @@ function genFalcon(opts) {
|
|
|
1193
1205
|
d[k >>> 1] = s;
|
|
1194
1206
|
}
|
|
1195
1207
|
for (let k = 0; k < n; k += 2) {
|
|
1196
|
-
let s =
|
|
1208
|
+
let s = _0n;
|
|
1197
1209
|
for (let i = 1; i < k; i += 2)
|
|
1198
1210
|
s += a[i] * a[k - i];
|
|
1199
1211
|
for (let i = k + 1; i < n; i += 2)
|
|
@@ -1205,7 +1217,7 @@ function genFalcon(opts) {
|
|
|
1205
1217
|
mulConjD(logn, d, a, b) {
|
|
1206
1218
|
const n = 1 << logn;
|
|
1207
1219
|
for (let k = 0; k < n; k++) {
|
|
1208
|
-
let s =
|
|
1220
|
+
let s = _0n;
|
|
1209
1221
|
for (let i = 0; i <= k; i += 2)
|
|
1210
1222
|
s += b[i >>> 1] * a[k - i];
|
|
1211
1223
|
for (let i = k + 2 - (k & 1); i < n; i += 2)
|
|
@@ -1220,7 +1232,7 @@ function genFalcon(opts) {
|
|
|
1220
1232
|
subMul(logn, a, b, c, e) {
|
|
1221
1233
|
const n = 1 << logn;
|
|
1222
1234
|
for (let k = 0; k < n; k++) {
|
|
1223
|
-
let s =
|
|
1235
|
+
let s = _0n;
|
|
1224
1236
|
for (let i = 0; i <= k; i++)
|
|
1225
1237
|
s += b[i] * c[k - i];
|
|
1226
1238
|
for (let i = k + 1; i < n; i++)
|
|
@@ -1264,7 +1276,7 @@ function genFalcon(opts) {
|
|
|
1264
1276
|
const Gx = new Float64Array(n);
|
|
1265
1277
|
const k = new Array(n);
|
|
1266
1278
|
while (true) {
|
|
1267
|
-
let scaleFG =
|
|
1279
|
+
let scaleFG = _31n * (FGlen - _10n);
|
|
1268
1280
|
for (let i = 0; i < n; i++) {
|
|
1269
1281
|
Fx[i] = Number(F[i] >> scaleFG);
|
|
1270
1282
|
Gx[i] = Number(G[i] >> scaleFG);
|
|
@@ -1283,16 +1295,16 @@ function genFalcon(opts) {
|
|
|
1283
1295
|
}
|
|
1284
1296
|
F = this.subMul(logn, F, f, k, scaleK); // 3: F ← F - kf
|
|
1285
1297
|
G = this.subMul(logn, G, g, k, scaleK); // 4: G ← G - kg
|
|
1286
|
-
const maxfgNew = scaleK + BigInt(Math.round(fgMaxBits)) +
|
|
1298
|
+
const maxfgNew = scaleK + BigInt(Math.round(fgMaxBits)) + _10n;
|
|
1287
1299
|
if (maxfgNew < maxFGBits)
|
|
1288
1300
|
maxFGBits = maxfgNew;
|
|
1289
|
-
if (FGlen >
|
|
1301
|
+
if (FGlen > _1n && FGlen * _31n >= maxFGBits + _31n)
|
|
1290
1302
|
FGlen--;
|
|
1291
|
-
if (scaleK <=
|
|
1303
|
+
if (scaleK <= _0n)
|
|
1292
1304
|
break;
|
|
1293
|
-
scaleK -=
|
|
1294
|
-
if (scaleK <
|
|
1295
|
-
scaleK =
|
|
1305
|
+
scaleK -= _25n;
|
|
1306
|
+
if (scaleK < _0n)
|
|
1307
|
+
scaleK = _0n;
|
|
1296
1308
|
}
|
|
1297
1309
|
return true;
|
|
1298
1310
|
}
|
|
@@ -1320,11 +1332,11 @@ function genFalcon(opts) {
|
|
|
1320
1332
|
const xf = f[0];
|
|
1321
1333
|
const xg = g[0];
|
|
1322
1334
|
// We can rely on 'invert' to throw if they are not coprime.
|
|
1323
|
-
if (xf <=
|
|
1335
|
+
if (xf <= _0n || xg <= _0n)
|
|
1324
1336
|
return false;
|
|
1325
1337
|
try {
|
|
1326
1338
|
const u1 = invert(xf, xg); // if gcd(f, g) ≠ 1 then
|
|
1327
|
-
const v1 = (
|
|
1339
|
+
const v1 = (_1n - u1 * xf) / xg;
|
|
1328
1340
|
F[0] = -v1 * QBig; // 5: (F, G) ← (vq, uq)
|
|
1329
1341
|
G[0] = u1 * QBig;
|
|
1330
1342
|
return true;
|
|
@@ -1606,7 +1618,14 @@ function genFalcon(opts) {
|
|
|
1606
1618
|
return headerCoder(0x30 + logn, splitCoder('falcon.signature', NONCELEN, sigLen, msg.length)).encode([nonce, pad(sigLen).encode(s2), msg]);
|
|
1607
1619
|
},
|
|
1608
1620
|
decode(data) {
|
|
1621
|
+
// Keep API misuse on the coder's TypeError path before reading the container length.
|
|
1622
|
+
abytes(data, undefined, 'signature');
|
|
1623
|
+
// The compressed-signature field is fixed-width here; only the message is variable. A
|
|
1624
|
+
// container shorter than the fixed part would make the s2 field borrow bytes from nowhere
|
|
1625
|
+
// and let a truncated encoding open to the same message.
|
|
1609
1626
|
const msgLen = data.length - NONCELEN - sigLen - 1;
|
|
1627
|
+
if (msgLen < 0)
|
|
1628
|
+
throw new Error('signature coder: wrong length');
|
|
1610
1629
|
const [nonce, s2, msg] = headerCoder(0x30 + logn, splitCoder('falcon.signature', NONCELEN, sigLen, msgLen)).decode(data);
|
|
1611
1630
|
return { nonce, s2: decodeSig(s2), msg };
|
|
1612
1631
|
},
|
|
@@ -1621,7 +1640,13 @@ function genFalcon(opts) {
|
|
|
1621
1640
|
return headerCoder(0x30 + logn, splitCoder('falcon.detachedSignature', NONCELEN, getSigLen(s2))).encode([nonce, opts.padded ? pad(sigLen).encode(s2) : s2]);
|
|
1622
1641
|
},
|
|
1623
1642
|
decode(data) {
|
|
1624
|
-
|
|
1643
|
+
// Padded detached signatures are fixed-length (`lengths.signature`), so the payload width
|
|
1644
|
+
// must come from the parameter set, not from the input: deriving it would accept appended
|
|
1645
|
+
// zero bytes and truncated padding as extra valid encodings of the same signature.
|
|
1646
|
+
// Unpadded signatures are variable-length; decodeUnpaddedSig() enforces the exact canonical
|
|
1647
|
+
// bitlength of whatever remains.
|
|
1648
|
+
const payloadLen = opts.padded ? sigLen : data.length - NONCELEN - 1;
|
|
1649
|
+
const [nonce, raw] = headerCoder(0x30 + logn, splitCoder('falcon.detachedSignature', NONCELEN, payloadLen)).decode(data);
|
|
1625
1650
|
const s2 = decodeSig(raw);
|
|
1626
1651
|
return { nonce, s2 };
|
|
1627
1652
|
},
|
|
@@ -1709,7 +1734,7 @@ function genFalcon(opts) {
|
|
|
1709
1734
|
shakeBuf;
|
|
1710
1735
|
ctrView;
|
|
1711
1736
|
// ChaCha
|
|
1712
|
-
ctr =
|
|
1737
|
+
ctr = _0n;
|
|
1713
1738
|
buf;
|
|
1714
1739
|
buf32;
|
|
1715
1740
|
pos;
|
|
@@ -1759,8 +1784,8 @@ function genFalcon(opts) {
|
|
|
1759
1784
|
const out32 = swap32IfBE(this.buf32);
|
|
1760
1785
|
for (let i = 0; i < 8; i++, this.ctr++) {
|
|
1761
1786
|
const n = swap32IfBE(this.nonce32.slice()); // [n0, n1, n2, n3]
|
|
1762
|
-
n[2] ^= Number(this.ctr &
|
|
1763
|
-
n[3] ^= Number(this.ctr >>
|
|
1787
|
+
n[2] ^= Number(this.ctr & MASK_32n);
|
|
1788
|
+
n[3] ^= Number(this.ctr >> _32n);
|
|
1764
1789
|
// chacha20() takes raw nonce bytes; on BE the word-normalized temp must be swapped back.
|
|
1765
1790
|
swap32IfBE(n.subarray(1));
|
|
1766
1791
|
chacha20(this.key, u8(n.subarray(1)), EMPTY_CHACHA20_BLOCK, this.curBlock, n[0]);
|
|
@@ -2077,7 +2102,8 @@ function genFalcon(opts) {
|
|
|
2077
2102
|
// ▷ Remove 1 byte for the header, and 40 bytes for r
|
|
2078
2103
|
// 11: while (s = ⊥)
|
|
2079
2104
|
// 12: return sig = (r, s)
|
|
2080
|
-
abytes(msg);
|
|
2105
|
+
abytes(msg, undefined, 'msg');
|
|
2106
|
+
abytes(sk, secretKeyCoder.bytesLen, 'secretKey');
|
|
2081
2107
|
// One RNG stream drives both the public 40-byte nonce and the 48-byte sampler seed, so
|
|
2082
2108
|
// deterministic rnd hooks make signatures deterministic for fixed secretKey/message inputs.
|
|
2083
2109
|
const nonce = rnd(40);
|
|
@@ -2171,18 +2197,20 @@ function genFalcon(opts) {
|
|
|
2171
2197
|
intPoly.sub(s1, c0); // 5: s₁ ← c - s₂h mod q ▷ s₁ should be normalized between -q/2 and q/2
|
|
2172
2198
|
return intPoly.isShort(signedCoder.encode(s1), s2); // 6: if ||(s₁, s₂)||² < [β²] then
|
|
2173
2199
|
};
|
|
2174
|
-
const info = { type: 'falcon' };
|
|
2175
|
-
const keyLengths = {
|
|
2200
|
+
const info = Object.freeze({ type: 'falcon' });
|
|
2201
|
+
const keyLengths = Object.freeze({
|
|
2176
2202
|
seed: 48,
|
|
2177
2203
|
publicKey: publicKeyCoder.bytesLen,
|
|
2178
2204
|
secretKey: secretKeyCoder.bytesLen,
|
|
2179
|
-
};
|
|
2205
|
+
});
|
|
2180
2206
|
// Noble exposes a 48-byte sampler-seed hook,
|
|
2181
2207
|
// but Falcon still samples/encodes a separate 40-byte nonce per signature.
|
|
2182
2208
|
const getRnd = (opts = {}) => {
|
|
2183
2209
|
validateSigOpts(opts);
|
|
2184
2210
|
if (opts.context !== undefined)
|
|
2185
2211
|
throw new Error('context is not supported');
|
|
2212
|
+
if (opts.random !== undefined && typeof opts.random !== 'function')
|
|
2213
|
+
throw new TypeError('"opts.random" expected function, got type=' + typeof opts.random);
|
|
2186
2214
|
if (opts.random !== undefined)
|
|
2187
2215
|
return opts.random;
|
|
2188
2216
|
if (opts.extraEntropy === undefined)
|
|
@@ -2197,11 +2225,17 @@ function genFalcon(opts) {
|
|
|
2197
2225
|
if (opts.context !== undefined)
|
|
2198
2226
|
throw new Error('context is not supported');
|
|
2199
2227
|
};
|
|
2200
|
-
const tests = {
|
|
2228
|
+
const tests = Object.freeze({
|
|
2229
|
+
publicKeyCoder: Object.freeze(publicKeyCoder),
|
|
2230
|
+
privateKeyCoder: Object.freeze(secretKeyCoder),
|
|
2231
|
+
maxS2Len: opts.maxS2Len,
|
|
2232
|
+
});
|
|
2201
2233
|
// `signRand` documents only the sampler-seed input length;
|
|
2202
2234
|
// detached/attached signatures still include their own 40-byte nonce.
|
|
2203
|
-
const attachedLengths = { ...keyLengths, signRand: 48 };
|
|
2204
|
-
const lengths = opts.padded
|
|
2235
|
+
const attachedLengths = Object.freeze({ ...keyLengths, signRand: 48 });
|
|
2236
|
+
const lengths = opts.padded
|
|
2237
|
+
? Object.freeze({ ...attachedLengths, signature: opts.sigLen })
|
|
2238
|
+
: attachedLengths;
|
|
2205
2239
|
const keygen = (seed) => {
|
|
2206
2240
|
const randSeed = seed === undefined;
|
|
2207
2241
|
if (randSeed)
|
|
@@ -2216,6 +2250,7 @@ function genFalcon(opts) {
|
|
|
2216
2250
|
return { publicKey: pk, secretKey: sk };
|
|
2217
2251
|
};
|
|
2218
2252
|
const getPublicKey = (sk) => {
|
|
2253
|
+
abytes(sk, secretKeyCoder.bytesLen, 'secretKey');
|
|
2219
2254
|
const [f, g, F] = secretKeyCoder.decode(sk);
|
|
2220
2255
|
try {
|
|
2221
2256
|
const h = computePublic(f, g);
|
|
@@ -2238,9 +2273,10 @@ function genFalcon(opts) {
|
|
|
2238
2273
|
*/
|
|
2239
2274
|
const verify = (sig, msg, pk, verOpts = {}) => {
|
|
2240
2275
|
checkVerOpts(verOpts);
|
|
2241
|
-
abytes(sig);
|
|
2242
|
-
abytes(msg);
|
|
2243
|
-
|
|
2276
|
+
abytes(sig, undefined, 'signature');
|
|
2277
|
+
abytes(msg, undefined, 'msg');
|
|
2278
|
+
// Length/canonical public-key failures are decoded below and return false; only type is fatal.
|
|
2279
|
+
abytes(pk, undefined, 'publicKey');
|
|
2244
2280
|
try {
|
|
2245
2281
|
const { s2, nonce } = SignatureCoderDetached(logn).decode(sig);
|
|
2246
2282
|
return verifyRaw(pk, s2, nonce, msg);
|
|
@@ -2249,7 +2285,7 @@ function genFalcon(opts) {
|
|
|
2249
2285
|
return false;
|
|
2250
2286
|
}
|
|
2251
2287
|
};
|
|
2252
|
-
const attached = {
|
|
2288
|
+
const attached = Object.freeze({
|
|
2253
2289
|
info,
|
|
2254
2290
|
lengths: attachedLengths,
|
|
2255
2291
|
keygen,
|
|
@@ -2267,7 +2303,7 @@ function genFalcon(opts) {
|
|
|
2267
2303
|
return msg;
|
|
2268
2304
|
throw new Error('invalid signature');
|
|
2269
2305
|
},
|
|
2270
|
-
};
|
|
2306
|
+
});
|
|
2271
2307
|
const res = {
|
|
2272
2308
|
info,
|
|
2273
2309
|
lengths,
|
|
@@ -2278,7 +2314,7 @@ function genFalcon(opts) {
|
|
|
2278
2314
|
verify,
|
|
2279
2315
|
};
|
|
2280
2316
|
res.__test = tests;
|
|
2281
|
-
return res;
|
|
2317
|
+
return Object.freeze(res);
|
|
2282
2318
|
}
|
|
2283
2319
|
const falcon512opts = {
|
|
2284
2320
|
N: 512,
|
|
@@ -2362,7 +2398,7 @@ export const falcon1024padded = /* @__PURE__ */ (() => genFalcon({
|
|
|
2362
2398
|
maxS2Len: 1239,
|
|
2363
2399
|
}))();
|
|
2364
2400
|
// NOTE: for tests only, don't use
|
|
2365
|
-
export const __tests = /* @__PURE__ */ (() => ({
|
|
2401
|
+
export const __tests = /* @__PURE__ */ (() => Object.freeze({
|
|
2366
2402
|
BNORM_MAX,
|
|
2367
2403
|
COMPLEX_ROOTS,
|
|
2368
2404
|
Float,
|
|
@@ -2375,4 +2411,3 @@ export const __tests = /* @__PURE__ */ (() => ({
|
|
|
2375
2411
|
falcon1024: falcon1024.__test,
|
|
2376
2412
|
falcon1024padded: falcon1024padded.__test,
|
|
2377
2413
|
}))();
|
|
2378
|
-
//# sourceMappingURL=falcon.js.map
|
package/hybrid.d.ts
CHANGED
|
@@ -78,7 +78,7 @@ import { type EdDSA } from '@noble/curves/abstract/edwards.js';
|
|
|
78
78
|
import { type MontgomeryECDH } from '@noble/curves/abstract/montgomery.js';
|
|
79
79
|
import { type ECDSA } from '@noble/curves/abstract/weierstrass.js';
|
|
80
80
|
import { type CHash, type CHashXOF } from '@noble/hashes/utils.js';
|
|
81
|
-
import { type KEM, type Signer } from './utils.ts';
|
|
81
|
+
import { type KEM, type Signer, type TArg, type TRet } from './utils.ts';
|
|
82
82
|
type CurveECDH = ECDSA | MontgomeryECDH;
|
|
83
83
|
type CurveSign = ECDSA | EdDSA;
|
|
84
84
|
/**
|
|
@@ -104,7 +104,7 @@ type CurveSign = ECDSA | EdDSA;
|
|
|
104
104
|
* const publicKeyLen = kem.lengths.publicKey;
|
|
105
105
|
* ```
|
|
106
106
|
*/
|
|
107
|
-
export declare function ecdhKem(curve: CurveECDH, allowZeroKey?: boolean): KEM
|
|
107
|
+
export declare function ecdhKem(curve: CurveECDH, allowZeroKey?: boolean): TRet<KEM>;
|
|
108
108
|
/**
|
|
109
109
|
* Wraps a curve signer as a generic `Signer`.
|
|
110
110
|
* Signatures stay in the wrapped curve's native byte encoding.
|
|
@@ -126,9 +126,9 @@ export declare function ecdhKem(curve: CurveECDH, allowZeroKey?: boolean): KEM;
|
|
|
126
126
|
* const sigLen = signer.lengths.signature;
|
|
127
127
|
* ```
|
|
128
128
|
*/
|
|
129
|
-
export declare function ecSigner(curve: CurveSign, allowZeroKey?: boolean): Signer
|
|
129
|
+
export declare function ecSigner(curve: CurveSign, allowZeroKey?: boolean): TRet<Signer>;
|
|
130
130
|
/** Seed-expansion callback used by the hybrid combiners. */
|
|
131
|
-
export type ExpandSeed = (seed: Uint8Array
|
|
131
|
+
export type ExpandSeed = (seed: TArg<Uint8Array>, len: number) => TRet<Uint8Array>;
|
|
132
132
|
type XOF = CHashXOF<any, {
|
|
133
133
|
dkLen: number;
|
|
134
134
|
}>;
|
|
@@ -146,9 +146,9 @@ type XOF = CHashXOF<any, {
|
|
|
146
146
|
* const seed = expandSeed(new Uint8Array([1]), 4);
|
|
147
147
|
* ```
|
|
148
148
|
*/
|
|
149
|
-
export declare function expandSeedXof(xof: XOF): ExpandSeed
|
|
149
|
+
export declare function expandSeedXof(xof: TArg<XOF>): TRet<ExpandSeed>;
|
|
150
150
|
/** Combines public keys, ciphertexts, and shared secrets into one shared secret. */
|
|
151
|
-
export type Combiner = (publicKeys: Uint8Array[]
|
|
151
|
+
export type Combiner = (publicKeys: TArg<Uint8Array[]>, cipherTexts: TArg<Uint8Array[]>, sharedSecrets: TArg<Uint8Array[]>) => TRet<Uint8Array>;
|
|
152
152
|
/**
|
|
153
153
|
* Combines multiple KEMs into one composite KEM.
|
|
154
154
|
* @param realSeedLen - Input seed length expected by `expandSeed`.
|
|
@@ -176,7 +176,7 @@ export type Combiner = (publicKeys: Uint8Array[], cipherTexts: Uint8Array[], sha
|
|
|
176
176
|
*/
|
|
177
177
|
export declare function combineKEMS(realSeedLen: number | undefined, // how much bytes expandSeed expects
|
|
178
178
|
realMsgLen: number | undefined, // how much bytes combiner returns
|
|
179
|
-
expandSeed: ExpandSeed
|
|
179
|
+
expandSeed: TArg<ExpandSeed>, combiner: TArg<Combiner>, ...kems: TArg<KEM[]>): TRet<KEM>;
|
|
180
180
|
/**
|
|
181
181
|
* Combines multiple signers into one composite signer.
|
|
182
182
|
* @param realSeedLen - Input seed length expected by `expandSeed`.
|
|
@@ -190,10 +190,14 @@ expandSeed: ExpandSeed, combiner: Combiner, ...kems: KEM[]): KEM;
|
|
|
190
190
|
* import { combineSigners, expandSeedXof } from '@noble/post-quantum/hybrid.js';
|
|
191
191
|
* import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
|
|
192
192
|
* const hybrid = combineSigners(32, expandSeedXof(shake256), ml_dsa44, ml_dsa44);
|
|
193
|
-
* const
|
|
193
|
+
* const seed = new Uint8Array(hybrid.lengths.seed!).fill(1);
|
|
194
|
+
* const { secretKey, publicKey } = hybrid.keygen(seed);
|
|
195
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
196
|
+
* const sig = hybrid.sign(msg, secretKey);
|
|
197
|
+
* const isValid = hybrid.verify(sig, msg, publicKey);
|
|
194
198
|
* ```
|
|
195
199
|
*/
|
|
196
|
-
export declare function combineSigners(realSeedLen: number | undefined, expandSeed: ExpandSeed
|
|
200
|
+
export declare function combineSigners(realSeedLen: number | undefined, expandSeed: TArg<ExpandSeed>, ...signers: TArg<Signer[]>): TRet<Signer>;
|
|
197
201
|
/**
|
|
198
202
|
* Builds a QSF hybrid KEM preset from a PQ KEM and an elliptic-curve KEM.
|
|
199
203
|
* The combined shared-secret length follows `kdf.outputLen`; the built-in presets use 32-byte
|
|
@@ -218,11 +222,11 @@ export declare function combineSigners(realSeedLen: number | undefined, expandSe
|
|
|
218
222
|
* const publicKeyLen = kem.lengths.publicKey;
|
|
219
223
|
* ```
|
|
220
224
|
*/
|
|
221
|
-
export declare function QSF(label: string, pqc: KEM
|
|
225
|
+
export declare function QSF(label: string, pqc: TArg<KEM>, curveKEM: TArg<KEM>, xof: TArg<XOF>, kdf: CHash): TRet<KEM>;
|
|
222
226
|
/** QSF preset combining ML-KEM-768 with P-256. */
|
|
223
|
-
export declare const QSF_ml_kem768_p256: KEM
|
|
227
|
+
export declare const QSF_ml_kem768_p256: TRet<KEM>;
|
|
224
228
|
/** QSF preset combining ML-KEM-1024 with P-384. */
|
|
225
|
-
export declare const QSF_ml_kem1024_p384: KEM
|
|
229
|
+
export declare const QSF_ml_kem1024_p384: TRet<KEM>;
|
|
226
230
|
/**
|
|
227
231
|
* Builds the "KitchenSink" hybrid KEM combiner.
|
|
228
232
|
* The current builder always derives a fixed 32-byte output,
|
|
@@ -249,33 +253,18 @@ export declare const QSF_ml_kem1024_p384: KEM;
|
|
|
249
253
|
* const publicKeyLen = kem.lengths.publicKey;
|
|
250
254
|
* ```
|
|
251
255
|
*/
|
|
252
|
-
export declare function createKitchenSink(label: string, pqc: KEM
|
|
256
|
+
export declare function createKitchenSink(label: string, pqc: TArg<KEM>, curveKEM: TArg<KEM>, xof: TArg<XOF>, hash: CHash): TRet<KEM>;
|
|
253
257
|
/** KitchenSink preset combining ML-KEM-768 with X25519.
|
|
254
258
|
* Caller randomness splits into 32 ML-KEM coins plus a 32-byte X25519 ephemeral-secret seed.
|
|
255
259
|
*/
|
|
256
|
-
export declare const KitchenSink_ml_kem768_x25519: KEM
|
|
260
|
+
export declare const KitchenSink_ml_kem768_x25519: TRet<KEM>;
|
|
257
261
|
/** X25519 + ML-KEM-768 hybrid preset.
|
|
258
262
|
* Uses the hard-coded domain-separation label `\\.//^\\` and hashes only `ct1 || pk1`
|
|
259
263
|
* from the X25519 side in addition to the two component shared secrets.
|
|
260
264
|
*/
|
|
261
|
-
export declare const ml_kem768_x25519: KEM
|
|
265
|
+
export declare const ml_kem768_x25519: TRet<KEM>;
|
|
262
266
|
/** P-256 + ML-KEM-768 hybrid preset. */
|
|
263
|
-
export declare const ml_kem768_p256: KEM
|
|
267
|
+
export declare const ml_kem768_p256: TRet<KEM>;
|
|
264
268
|
/** P-384 + ML-KEM-1024 hybrid preset. */
|
|
265
|
-
export declare const ml_kem1024_p384: KEM
|
|
266
|
-
/** Legacy alias for `ml_kem768_x25519`. */
|
|
267
|
-
export declare const XWing: KEM;
|
|
268
|
-
/** Legacy alias for `ml_kem768_x25519`. */
|
|
269
|
-
export declare const MLKEM768X25519: KEM;
|
|
270
|
-
/** Legacy alias for `ml_kem768_p256`. */
|
|
271
|
-
export declare const MLKEM768P256: KEM;
|
|
272
|
-
/** Legacy alias for `ml_kem1024_p384`. */
|
|
273
|
-
export declare const MLKEM1024P384: KEM;
|
|
274
|
-
/** Legacy alias for `QSF_ml_kem768_p256`. */
|
|
275
|
-
export declare const QSFMLKEM768P256: KEM;
|
|
276
|
-
/** Legacy alias for `QSF_ml_kem1024_p384`. */
|
|
277
|
-
export declare const QSFMLKEM1024P384: KEM;
|
|
278
|
-
/** Legacy alias for `KitchenSink_ml_kem768_x25519`. */
|
|
279
|
-
export declare const KitchenSinkMLKEM768X25519: KEM;
|
|
269
|
+
export declare const ml_kem1024_p384: TRet<KEM>;
|
|
280
270
|
export {};
|
|
281
|
-
//# sourceMappingURL=hybrid.d.ts.map
|