@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/hybrid.js CHANGED
@@ -79,15 +79,38 @@ import {} from '@noble/curves/abstract/montgomery.js';
79
79
  import {} from '@noble/curves/abstract/weierstrass.js';
80
80
  import { x25519 } from '@noble/curves/ed25519.js';
81
81
  import { p256, p384 } from '@noble/curves/nist.js';
82
- import { asciiToBytes, bytesToNumberBE, bytesToNumberLE, concatBytes, numberToBytesBE, } from '@noble/curves/utils.js';
82
+ import { abool, afunction, asciiToBytes, bytesToNumberBE, bytesToNumberLE, concatBytes, numberToBytesBE, } from '@noble/curves/utils.js';
83
83
  import { expand, extract } from '@noble/hashes/hkdf.js';
84
84
  import { sha256 } from '@noble/hashes/sha2.js';
85
85
  import { sha3_256, shake256 } from '@noble/hashes/sha3.js';
86
86
  import { abytes, ahash, anumber } from '@noble/hashes/utils.js';
87
87
  import { ml_kem1024, ml_kem768 } from "./ml-kem.js";
88
- import { cleanBytes, copyBytes, randomBytes, splitCoder, validateSigOpts, validateVerOpts, } from "./utils.js";
88
+ import { aobject, astring, cleanBytes, copyBytes, randomBytes, splitCoder, validateSigOpts, validateVerOpts, } from "./utils.js";
89
+ const validateKEM = (kem, title) => {
90
+ const k = aobject(kem, title);
91
+ aobject(k.lengths, `${title}.lengths`);
92
+ afunction(k.keygen, `${title}.keygen`);
93
+ afunction(k.getPublicKey, `${title}.getPublicKey`);
94
+ afunction(k.encapsulate, `${title}.encapsulate`);
95
+ afunction(k.decapsulate, `${title}.decapsulate`);
96
+ return k;
97
+ };
98
+ const validateSigner = (signer, title) => {
99
+ const s = aobject(signer, title);
100
+ aobject(s.lengths, `${title}.lengths`);
101
+ afunction(s.keygen, `${title}.keygen`);
102
+ afunction(s.getPublicKey, `${title}.getPublicKey`);
103
+ afunction(s.sign, `${title}.sign`);
104
+ afunction(s.verify, `${title}.verify`);
105
+ return s;
106
+ };
89
107
  // Can re-use if decide to signatures support, on other hand getSecretKey is specific and ugly
90
108
  function ecKeygen(curve, allowZeroKey = false) {
109
+ const c = aobject(curve, 'curve');
110
+ aobject(c.lengths, 'curve.lengths');
111
+ afunction(c.keygen, 'curve.keygen');
112
+ afunction(c.getPublicKey, 'curve.getPublicKey');
113
+ abool(allowZeroKey, 'allowZeroKey');
91
114
  const lengths = curve.lengths;
92
115
  let keygen = curve.keygen;
93
116
  if (allowZeroKey) {
@@ -110,12 +133,15 @@ function ecKeygen(curve, allowZeroKey = false) {
110
133
  const seedScalar = Fn.isLE ? bytesToNumberLE(seed) : bytesToNumberBE(seed);
111
134
  // Reduce directly into [0, ORDER); scalar 0 still stays invalid.
112
135
  const secretKey = Fn.toBytes(Fn.create(seedScalar));
113
- return { secretKey, publicKey: curve.getPublicKey(secretKey) };
136
+ return {
137
+ secretKey: secretKey,
138
+ publicKey: curve.getPublicKey(secretKey),
139
+ };
114
140
  };
115
141
  }
116
142
  return {
117
143
  lengths: { secretKey: lengths.secretKey, publicKey: lengths.publicKey, seed: lengths.seed },
118
- keygen,
144
+ keygen: (seed) => keygen(seed),
119
145
  getPublicKey: (secretKey) => curve.getPublicKey(secretKey),
120
146
  };
121
147
  }
@@ -146,6 +172,11 @@ export function ecdhKem(curve, allowZeroKey = false) {
146
172
  const kg = ecKeygen(curve, allowZeroKey);
147
173
  if (!curve.getSharedSecret)
148
174
  throw new Error('wrong curve'); // ed25519 doesn't have one!
175
+ // Standalone (not `this.decapsulate`) so encapsulate works even when methods are destructured.
176
+ const decapsulate = (cipherText, secretKey) => {
177
+ const res = curve.getSharedSecret(secretKey, cipherText);
178
+ return (curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res);
179
+ };
149
180
  return {
150
181
  lengths: { ...kg.lengths, msg: kg.lengths.seed, cipherText: kg.lengths.publicKey },
151
182
  keygen: kg.keygen,
@@ -156,8 +187,8 @@ export function ecdhKem(curve, allowZeroKey = false) {
156
187
  const seed = copyBytes(rand);
157
188
  let ek = undefined;
158
189
  try {
159
- ek = this.keygen(seed).secretKey;
160
- const sharedSecret = this.decapsulate(publicKey, ek);
190
+ ek = kg.keygen(seed).secretKey;
191
+ const sharedSecret = decapsulate(publicKey, ek);
161
192
  const cipherText = curve.getPublicKey(ek);
162
193
  return { sharedSecret, cipherText };
163
194
  }
@@ -169,10 +200,7 @@ export function ecdhKem(curve, allowZeroKey = false) {
169
200
  cleanBytes(ek);
170
201
  }
171
202
  },
172
- decapsulate(cipherText, secretKey) {
173
- const res = curve.getSharedSecret(secretKey, cipherText);
174
- return curve.lengths.publicKeyHasPrefix ? res.subarray(1) : res;
175
- },
203
+ decapsulate,
176
204
  };
177
205
  }
178
206
  /**
@@ -253,10 +281,12 @@ function splitLengths(lst, name) {
253
281
  export function expandSeedXof(xof) {
254
282
  // Forward the caller seed directly: XOFs are expected to treat inputs as read-only, and this
255
283
  // adapter only translates the requested byte length into the hash API's `dkLen` option.
256
- return (seed, seedLen) => xof(seed, { dkLen: seedLen });
284
+ return ((seed, seedLen) => xof(seed, { dkLen: seedLen }));
257
285
  }
258
286
  function combineKeys(realSeedLen, // how much bytes expandSeed expects
259
- expandSeed, ...ck) {
287
+ expandSeed_, ...ck_) {
288
+ const expandSeed = expandSeed_;
289
+ const ck = ck_;
260
290
  const seedCoder = splitLengths(ck, 'seed');
261
291
  const pkCoder = splitLengths(ck, 'publicKey');
262
292
  // Allows to use identity functions for combiner/expandSeed
@@ -298,26 +328,43 @@ expandSeed, ...ck) {
298
328
  cleanBytes(secretKey);
299
329
  }
300
330
  }
301
- return {
302
- info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
303
- getPublicKey(secretKey) {
304
- // Composite secret keys are root seeds, so public-key derivation reruns key expansion from
305
- // that seed instead of decoding a packed child-secret-key structure.
306
- return this.keygen(secretKey).publicKey;
307
- },
308
- keygen(seed = randomBytes(realSeedLen)) {
309
- const { publicKey: pk, secretKey } = expandDecapsulationKey(seed);
331
+ // Standalone (not a method) so getPublicKey / destructured usage never depends on `this`.
332
+ const keygen = (seed) => {
333
+ // Detach the root: the exported secretKey must not alias caller-owned seed bytes, so later
334
+ // caller mutation of the seed cannot silently change the secret key (and vice versa).
335
+ const root = seed === undefined ? randomBytes(realSeedLen) : copyBytes(seed);
336
+ let res;
337
+ try {
338
+ const { publicKey: pk, secretKey } = expandDecapsulationKey(root);
310
339
  try {
311
- const publicKey = pkCoder.encode(pk);
312
- return { secretKey: seed, publicKey };
340
+ res = {
341
+ secretKey: root,
342
+ publicKey: pkCoder.encode(pk),
343
+ };
313
344
  }
314
345
  finally {
315
- cleanBytes(pk);
316
- // The exported secretKey is the caller/root seed itself; child secret keys are internal
346
+ // The exported secretKey is the (detached) root seed; child secret keys are internal
317
347
  // expansion outputs that are cleaned whether encoding succeeds or throws.
318
- cleanBytes(secretKey);
348
+ cleanBytes(pk, secretKey);
319
349
  }
350
+ return res;
351
+ }
352
+ finally {
353
+ if (!res)
354
+ cleanBytes(root);
355
+ }
356
+ };
357
+ return {
358
+ info: { lengths: { seed: realSeedLen, publicKey: pkCoder.bytesLen, secretKey: realSeedLen } },
359
+ // Composite secret keys are root seeds, so public-key derivation reruns key expansion from
360
+ // that seed instead of decoding a packed child-secret-key structure.
361
+ getPublicKey: (secretKey) => {
362
+ const keys = keygen(secretKey);
363
+ // keygen detaches its exported root; getPublicKey discards that half of the result.
364
+ cleanBytes(keys.secretKey);
365
+ return keys.publicKey;
320
366
  },
367
+ keygen,
321
368
  expandDecapsulationKey,
322
369
  realSeedLen,
323
370
  };
@@ -351,20 +398,33 @@ expandSeed, ...ck) {
351
398
  export function combineKEMS(realSeedLen, // how much bytes expandSeed expects
352
399
  realMsgLen, // how much bytes combiner returns
353
400
  expandSeed, combiner, ...kems) {
354
- const keys = combineKeys(realSeedLen, expandSeed, ...kems);
355
- const ctCoder = splitLengths(kems, 'cipherText');
356
- const pkCoder = splitLengths(kems, 'publicKey');
357
- const msgCoder = splitLengths(kems, 'msg');
401
+ if (realSeedLen !== undefined)
402
+ anumber(realSeedLen, 'realSeedLen');
403
+ if (realMsgLen !== undefined)
404
+ anumber(realMsgLen, 'realMsgLen');
405
+ if (typeof expandSeed !== 'function')
406
+ throw new TypeError('"expandSeed" expected function, got type=' + typeof expandSeed);
407
+ if (typeof combiner !== 'function')
408
+ throw new TypeError('"combiner" expected function, got type=' + typeof combiner);
409
+ const rawCombiner = combiner;
410
+ const rawKems = kems;
411
+ for (let i = 0; i < rawKems.length; i++)
412
+ validateKEM(rawKems[i], `kems[${i}]`);
413
+ const keys = combineKeys(realSeedLen, expandSeed, ...rawKems);
414
+ const ctCoder = splitLengths(rawKems, 'cipherText');
415
+ const pkCoder = splitLengths(rawKems, 'publicKey');
416
+ const msgCoder = splitLengths(rawKems, 'msg');
358
417
  if (realMsgLen === undefined)
359
418
  realMsgLen = msgCoder.bytesLen;
360
- anumber(realMsgLen);
361
- return {
362
- lengths: {
363
- ...keys.info.lengths,
364
- msg: realMsgLen,
365
- msgRand: msgCoder.bytesLen,
366
- cipherText: ctCoder.bytesLen,
367
- },
419
+ anumber(realMsgLen, 'realMsgLen');
420
+ const lengths = Object.freeze({
421
+ ...keys.info.lengths,
422
+ msg: realMsgLen,
423
+ msgRand: msgCoder.bytesLen,
424
+ cipherText: ctCoder.bytesLen,
425
+ });
426
+ return Object.freeze({
427
+ lengths,
368
428
  getPublicKey: keys.getPublicKey,
369
429
  keygen: keys.keygen,
370
430
  encapsulate(pk, randomness = randomBytes(msgCoder.bytesLen)) {
@@ -373,15 +433,15 @@ expandSeed, combiner, ...kems) {
373
433
  const sharedSecret = [];
374
434
  const cipherText = [];
375
435
  try {
376
- for (let i = 0; i < kems.length; i++) {
377
- const enc = kems[i].encapsulate(pks[i], rand[i]);
436
+ for (let i = 0; i < rawKems.length; i++) {
437
+ const enc = rawKems[i].encapsulate(pks[i], rand[i]);
378
438
  sharedSecret.push(enc.sharedSecret);
379
439
  cipherText.push(enc.cipherText);
380
440
  }
381
441
  return {
382
442
  // Detach the combiner result before cleanup: a caller-provided combiner may alias one of
383
443
  // the child sharedSecret buffers, and those child buffers are zeroized immediately below.
384
- sharedSecret: copyBytes(combiner(pks, cipherText, sharedSecret)),
444
+ sharedSecret: copyBytes(rawCombiner(pks, cipherText, sharedSecret)),
385
445
  cipherText: ctCoder.encode(cipherText),
386
446
  };
387
447
  }
@@ -394,11 +454,11 @@ expandSeed, combiner, ...kems) {
394
454
  decapsulate(ct, seed) {
395
455
  const cts = ctCoder.decode(ct);
396
456
  const { publicKey, secretKey } = keys.expandDecapsulationKey(seed);
397
- const sharedSecret = kems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
457
+ const sharedSecret = rawKems.map((i, j) => i.decapsulate(cts[j], secretKey[j]));
398
458
  try {
399
459
  // Detach the decapsulation result before cleanup: the combiner may hand back one of the
400
460
  // child shared-secret buffers, and those temporary buffers are zeroized below.
401
- return copyBytes(combiner(publicKey, cts, sharedSecret));
461
+ return copyBytes(rawCombiner(publicKey, cts, sharedSecret));
402
462
  }
403
463
  finally {
404
464
  // Decapsulation only needs the expanded child secret keys and child shared secrets for this
@@ -406,7 +466,7 @@ expandSeed, combiner, ...kems) {
406
466
  cleanBytes(secretKey, sharedSecret);
407
467
  }
408
468
  },
409
- };
469
+ });
410
470
  }
411
471
  // There is no specs for this, but can be useful
412
472
  // realSeedLen: how much bytes expandSeed expects.
@@ -423,13 +483,24 @@ expandSeed, combiner, ...kems) {
423
483
  * import { combineSigners, expandSeedXof } from '@noble/post-quantum/hybrid.js';
424
484
  * import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
425
485
  * const hybrid = combineSigners(32, expandSeedXof(shake256), ml_dsa44, ml_dsa44);
426
- * const { publicKey } = hybrid.keygen();
486
+ * const seed = new Uint8Array(hybrid.lengths.seed!).fill(1);
487
+ * const { secretKey, publicKey } = hybrid.keygen(seed);
488
+ * const msg = new TextEncoder().encode('hello noble');
489
+ * const sig = hybrid.sign(msg, secretKey);
490
+ * const isValid = hybrid.verify(sig, msg, publicKey);
427
491
  * ```
428
492
  */
429
493
  export function combineSigners(realSeedLen, expandSeed, ...signers) {
430
- const keys = combineKeys(realSeedLen, expandSeed, ...signers);
431
- const sigCoder = splitLengths(signers, 'signature');
432
- const pkCoder = splitLengths(signers, 'publicKey');
494
+ if (realSeedLen !== undefined)
495
+ anumber(realSeedLen, 'realSeedLen');
496
+ if (typeof expandSeed !== 'function')
497
+ throw new TypeError('"expandSeed" expected function, got type=' + typeof expandSeed);
498
+ const rawSigners = signers;
499
+ for (let i = 0; i < rawSigners.length; i++)
500
+ validateSigner(rawSigners[i], `signers[${i}]`);
501
+ const keys = combineKeys(realSeedLen, expandSeed, ...rawSigners);
502
+ const sigCoder = splitLengths(rawSigners, 'signature');
503
+ const pkCoder = splitLengths(rawSigners, 'publicKey');
433
504
  return {
434
505
  lengths: { ...keys.info.lengths, signature: sigCoder.bytesLen, signRand: 0 },
435
506
  getPublicKey: keys.getPublicKey,
@@ -445,7 +516,7 @@ export function combineSigners(realSeedLen, expandSeed, ...signers) {
445
516
  throw new Error('combineSigners does not support context; use the underlying signer directly');
446
517
  const { secretKey } = keys.expandDecapsulationKey(seed);
447
518
  try {
448
- const sigs = signers.map((i, j) => i.sign(message, secretKey[j]));
519
+ const sigs = rawSigners.map((i, j) => i.sign(message, secretKey[j]));
449
520
  return sigCoder.encode(sigs);
450
521
  }
451
522
  finally {
@@ -455,17 +526,24 @@ export function combineSigners(realSeedLen, expandSeed, ...signers) {
455
526
  }
456
527
  },
457
528
  /** Verify one combined signature.
458
- * Returns `false` when the aggregate signature/publicKey decode succeeds but any child verify
459
- * check fails. Throws on unsupported generic opts or malformed aggregate encodings.
529
+ * Wrong-length aggregate signatures return `false` (matching ml-dsa / slh-dsa behavior), as
530
+ * does any failing child verify. Throws on unsupported generic opts or malformed publicKey.
460
531
  */
461
532
  verify: (signature, message, publicKey, opts = {}) => {
462
533
  validateVerOpts(opts);
463
534
  if (opts.context !== undefined)
464
535
  throw new Error('combineSigners does not support context; use the underlying signer directly');
536
+ // Malformed signature *length* is a verification failure, not a thrown type error —
537
+ // consistent with ml-dsa / slh-dsa. Must run before sigCoder.decode, which throws.
538
+ // Preserve TypeError for non-byte API arguments before treating byte lengths as invalid.
539
+ abytes(signature, undefined, 'signature');
540
+ // A signature failure must not hide malformed aggregate public-key bytes.
465
541
  const pks = pkCoder.decode(publicKey);
542
+ if (signature.length !== sigCoder.bytesLen)
543
+ return false;
466
544
  const sigs = sigCoder.decode(signature);
467
- for (let i = 0; i < signers.length; i++) {
468
- if (!signers[i].verify(sigs[i], message, pks[i]))
545
+ for (let i = 0; i < rawSigners.length; i++) {
546
+ if (!rawSigners[i].verify(sigs[i], message, pks[i]))
469
547
  return false;
470
548
  }
471
549
  return true;
@@ -497,7 +575,14 @@ export function combineSigners(realSeedLen, expandSeed, ...signers) {
497
575
  * ```
498
576
  */
499
577
  export function QSF(label, pqc, curveKEM, xof, kdf) {
578
+ astring(label, 'label');
579
+ validateKEM(pqc, 'pqc');
580
+ validateKEM(curveKEM, 'curveKEM');
581
+ if (typeof xof !== 'function' || typeof xof.create !== 'function')
582
+ throw new TypeError('"xof" expected hash function, got type=' + typeof xof);
500
583
  ahash(xof);
584
+ if (typeof kdf !== 'function' || typeof kdf.create !== 'function')
585
+ throw new TypeError('"kdf" expected hash function, got type=' + typeof kdf);
501
586
  ahash(kdf);
502
587
  return combineKEMS(32, kdf.outputLen, expandSeedXof(xof), (pk, ct, ss) => kdf(concatBytes(ss[0], ss[1], ct[1], pk[1], asciiToBytes(label))), pqc, curveKEM);
503
588
  }
@@ -532,7 +617,14 @@ export const QSF_ml_kem1024_p384 = /* @__PURE__ */ (() => QSF('QSF-KEM(ML-KEM-10
532
617
  * ```
533
618
  */
534
619
  export function createKitchenSink(label, pqc, curveKEM, xof, hash) {
620
+ astring(label, 'label');
621
+ validateKEM(pqc, 'pqc');
622
+ validateKEM(curveKEM, 'curveKEM');
623
+ if (typeof xof !== 'function' || typeof xof.create !== 'function')
624
+ throw new TypeError('"xof" expected hash function, got type=' + typeof xof);
535
625
  ahash(xof);
626
+ if (typeof hash !== 'function' || typeof hash.create !== 'function')
627
+ throw new TypeError('"hash" expected hash function, got type=' + typeof hash);
536
628
  ahash(hash);
537
629
  return combineKEMS(32, 32, expandSeedXof(xof), (pk, ct, ss) => {
538
630
  const preimage = concatBytes(ss[0], ss[1], ct[0], pk[0], ct[1], pk[1], asciiToBytes(label));
@@ -587,6 +679,11 @@ function nistCurveKem(curve, scalarLen, elemLen, nseed) {
587
679
  const publicKey = curve.getPublicKey(secretKey, false);
588
680
  return { secretKey, publicKey };
589
681
  }
682
+ // Standalone (not `this.decapsulate`) so encapsulate works even when methods are destructured.
683
+ const decapsulate = (cipherText, secretKey) => {
684
+ const full = curve.getSharedSecret(secretKey, cipherText);
685
+ return full.subarray(1);
686
+ };
590
687
  return {
591
688
  lengths: {
592
689
  secretKey: scalarLen,
@@ -607,7 +704,7 @@ function nistCurveKem(curve, scalarLen, elemLen, nseed) {
607
704
  let ek = undefined;
608
705
  try {
609
706
  ek = rejectionSampling(rand).secretKey;
610
- const sharedSecret = this.decapsulate(publicKey, ek);
707
+ const sharedSecret = decapsulate(publicKey, ek);
611
708
  const cipherText = curve.getPublicKey(ek, false);
612
709
  return { sharedSecret, cipherText };
613
710
  }
@@ -618,10 +715,7 @@ function nistCurveKem(curve, scalarLen, elemLen, nseed) {
618
715
  cleanBytes(ek);
619
716
  }
620
717
  },
621
- decapsulate(cipherText, secretKey) {
622
- const full = curve.getSharedSecret(secretKey, cipherText);
623
- return full.subarray(1);
624
- },
718
+ decapsulate,
625
719
  };
626
720
  }
627
721
  /**
@@ -641,29 +735,14 @@ function concreteHybridKem(label, mlkem, curve, nseed) {
641
735
  const totalSeedLen = mlkemSeedLen + nseed;
642
736
  return combineKEMS(32, 32, (seed) => {
643
737
  abytes(seed, 32);
644
- const expanded = shake256(seed, { dkLen: totalSeedLen });
645
- const mlkemSeed = expanded.subarray(0, mlkemSeedLen);
646
- const curveSeed = expanded.subarray(mlkemSeedLen, totalSeedLen);
647
- return concatBytes(mlkemSeed, curveSeed);
738
+ // One SHAKE256 stream split by the seed coder as mlkemSeed (64) || curveSeed (nseed).
739
+ // Returned directly: the previous concatBytes of two adjacent subarrays produced an
740
+ // identical copy while leaving this original buffer unwiped; expandDecapsulationKey
741
+ // wipes the returned buffer after the child seeds are copied out.
742
+ return shake256(seed, { dkLen: totalSeedLen });
648
743
  }, (pk, ct, ss) => sha3_256(concatBytes(ss[0], ss[1], ct[1], pk[1], asciiToBytes(label))), mlkem, curveKem);
649
744
  }
650
745
  /** P-256 + ML-KEM-768 hybrid preset. */
651
746
  export const ml_kem768_p256 = /* @__PURE__ */ (() => concreteHybridKem('MLKEM768-P256', ml_kem768, p256, 128))();
652
747
  /** P-384 + ML-KEM-1024 hybrid preset. */
653
748
  export const ml_kem1024_p384 = /* @__PURE__ */ (() => concreteHybridKem('MLKEM1024-P384', ml_kem1024, p384, 48))();
654
- // Legacy aliases
655
- /** Legacy alias for `ml_kem768_x25519`. */
656
- export const XWing = /* @__PURE__ */ (() => ml_kem768_x25519)();
657
- /** Legacy alias for `ml_kem768_x25519`. */
658
- export const MLKEM768X25519 = /* @__PURE__ */ (() => ml_kem768_x25519)();
659
- /** Legacy alias for `ml_kem768_p256`. */
660
- export const MLKEM768P256 = /* @__PURE__ */ (() => ml_kem768_p256)();
661
- /** Legacy alias for `ml_kem1024_p384`. */
662
- export const MLKEM1024P384 = /* @__PURE__ */ (() => ml_kem1024_p384)();
663
- /** Legacy alias for `QSF_ml_kem768_p256`. */
664
- export const QSFMLKEM768P256 = /* @__PURE__ */ (() => QSF_ml_kem768_p256)();
665
- /** Legacy alias for `QSF_ml_kem1024_p384`. */
666
- export const QSFMLKEM1024P384 = /* @__PURE__ */ (() => QSF_ml_kem1024_p384)();
667
- /** Legacy alias for `KitchenSink_ml_kem768_x25519`. */
668
- export const KitchenSinkMLKEM768X25519 = /* @__PURE__ */ (() => KitchenSink_ml_kem768_x25519)();
669
- //# sourceMappingURL=hybrid.js.map
package/index.d.ts CHANGED
@@ -1,2 +1 @@
1
1
  export {};
2
- //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -14,8 +14,15 @@ import {
14
14
  slh_dsa_shake_192f, slh_dsa_shake_192s,
15
15
  slh_dsa_shake_256f, slh_dsa_shake_256s,
16
16
  } from '@noble/post-quantum/slh-dsa.js';
17
+ import {
18
+ falcon512, falcon512padded, falcon1024, falcon1024padded,
19
+ } from '@noble/post-quantum/falcon.js';
20
+ import {
21
+ ml_kem768_x25519, ml_kem768_p256, ml_kem1024_p384,
22
+ KitchenSink_ml_kem768_x25519, XWing,
23
+ QSF_ml_kem768_p256, QSF_ml_kem1024_p384,
24
+ } from '@noble/post-quantum/hybrid.js';
17
25
  ```
18
26
  */
19
27
  throw new Error('root module cannot be imported: import submodules instead. Check out README');
20
28
  export {};
21
- //# sourceMappingURL=index.js.map
package/ml-dsa.d.ts CHANGED
@@ -1,4 +1,5 @@
1
- import { type CryptoKeys, type Signer, type SigOpts, type VerOpts } from './utils.ts';
1
+ import type { CHash } from '@noble/hashes/utils.js';
2
+ import { type CryptoKeys, type Signer, type SigOpts, type TArg, type TRet, type VerOpts } from './utils.ts';
2
3
  /** Internal ML-DSA options. */
3
4
  export type DSAInternalOpts = {
4
5
  /**
@@ -11,12 +12,19 @@ export type DSAInternalOpts = {
11
12
  /** ML-DSA signer surface with access to the internal message formatting mode. */
12
13
  export type DSAInternal = CryptoKeys & {
13
14
  lengths: Signer['lengths'];
14
- sign: (msg: Uint8Array, secretKey: Uint8Array, opts?: SigOpts & DSAInternalOpts) => Uint8Array;
15
- verify: (sig: Uint8Array, msg: Uint8Array, pubKey: Uint8Array, opts?: VerOpts & DSAInternalOpts) => boolean;
15
+ sign: (msg: TArg<Uint8Array>, secretKey: TArg<Uint8Array>, opts?: TArg<SigOpts & DSAInternalOpts>) => TRet<Uint8Array>;
16
+ verify: (sig: TArg<Uint8Array>, msg: TArg<Uint8Array>, pubKey: TArg<Uint8Array>, opts?: TArg<VerOpts & DSAInternalOpts>) => boolean;
16
17
  };
17
18
  /** Public ML-DSA signer surface. */
18
19
  export type DSA = Signer & {
19
- internal: DSAInternal;
20
+ internal: TRet<DSAInternal>;
21
+ securityLevel: number;
22
+ /**
23
+ * HashML-DSA (FIPS 204 §5.4) variant which signs a pre-hashed message.
24
+ * @param hash - Approved hash, checked against the parameter set security level.
25
+ * @returns Signer which pre-hashes `msg` before formatting `M'`.
26
+ */
27
+ prehash: (hash: TArg<CHash>) => TRet<Signer>;
20
28
  };
21
29
  /** Various lattice params. */
22
30
  /** Public ML-DSA parameter-set description. */
@@ -45,10 +53,28 @@ export type DSAParam = {
45
53
  * while `C_TILDE_BYTES`, `TR_BYTES`, `CRH_BYTES`, and `securityLevel` live in the preset wrappers.
46
54
  */
47
55
  export declare const PARAMS: Record<string, DSAParam>;
48
- /** ML-DSA-44 for 128-bit security level. Not recommended after 2030, as per ASD. */
49
- export declare const ml_dsa44: DSA;
56
+ /**
57
+ * ML-DSA-44 for 128-bit security level. Not recommended after 2030, as per ASD.
58
+ * @example
59
+ * Generate deterministic ML-DSA-44 keys, sign one message, and verify the signature.
60
+ * ```ts
61
+ * import { sha256 } from '@noble/hashes/sha2.js';
62
+ * import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
63
+ * const seed = new Uint8Array(ml_dsa44.lengths.seed!);
64
+ * const { secretKey, publicKey } = ml_dsa44.keygen(seed);
65
+ * const msg = new TextEncoder().encode('hello noble');
66
+ * const sig = ml_dsa44.sign(msg, secretKey);
67
+ * const isValid = ml_dsa44.verify(sig, msg, publicKey);
68
+ * const recovered = ml_dsa44.getPublicKey(secretKey);
69
+ * const context = new Uint8Array([1, 2, 3]);
70
+ * const prehash = ml_dsa44.prehash(sha256);
71
+ * const preSig = prehash.sign(msg, secretKey, { context });
72
+ * const preValid = prehash.verify(preSig, msg, publicKey, { context });
73
+ * const internalSig = ml_dsa44.internal.sign(msg, secretKey);
74
+ * ```
75
+ */
76
+ export declare const ml_dsa44: TRet<DSA>;
50
77
  /** ML-DSA-65 for 192-bit security level. Not recommended after 2030, as per ASD. */
51
- export declare const ml_dsa65: DSA;
78
+ export declare const ml_dsa65: TRet<DSA>;
52
79
  /** ML-DSA-87 for 256-bit security level. OK after 2030, as per ASD. */
53
- export declare const ml_dsa87: DSA;
54
- //# sourceMappingURL=ml-dsa.d.ts.map
80
+ export declare const ml_dsa87: TRet<DSA>;