@noble/post-quantum 0.6.1 → 0.7.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/README.md +191 -104
- package/_crystals.d.ts +8 -3
- package/_crystals.js +38 -10
- package/falcon.d.ts +1 -2
- package/falcon.js +202 -115
- package/hybrid.d.ts +38 -28
- package/hybrid.js +215 -86
- package/index.d.ts +0 -1
- package/index.js +1 -2
- package/ml-dsa.d.ts +31 -5
- package/ml-dsa.js +118 -34
- package/ml-kem.d.ts +45 -4
- package/ml-kem.js +206 -64
- package/package.json +16 -20
- package/slh-dsa.d.ts +23 -3
- package/slh-dsa.js +127 -60
- package/src/_crystals.ts +45 -11
- package/src/falcon.ts +206 -121
- package/src/hybrid.ts +217 -83
- package/src/index.ts +1 -1
- package/src/ml-dsa.ts +140 -43
- package/src/ml-kem.ts +239 -66
- package/src/slh-dsa.ts +149 -69
- package/src/utils.ts +186 -25
- package/src/webcrypto.ts +322 -0
- package/utils.d.ts +54 -4
- package/utils.js +167 -28
- package/webcrypto.d.ts +91 -0
- package/webcrypto.js +213 -0
- 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/src/ml-dsa.ts
CHANGED
|
@@ -27,9 +27,9 @@ import {
|
|
|
27
27
|
splitCoder,
|
|
28
28
|
type TArg,
|
|
29
29
|
type TRet,
|
|
30
|
-
validateOpts,
|
|
31
30
|
validateSigOpts,
|
|
32
31
|
validateVerOpts,
|
|
32
|
+
checkOptKeys,
|
|
33
33
|
vecCoder,
|
|
34
34
|
type VerOpts,
|
|
35
35
|
} from './utils.ts';
|
|
@@ -43,9 +43,28 @@ export type DSAInternalOpts = {
|
|
|
43
43
|
*/
|
|
44
44
|
externalMu?: boolean;
|
|
45
45
|
};
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
/**
|
|
47
|
+
* Keys each internal surface accepts.
|
|
48
|
+
*
|
|
49
|
+
* `context` is deliberately absent from both. The internal functions never read it: the
|
|
50
|
+
* public wrappers consume it when they format `M'` and must not pass it down, because a
|
|
51
|
+
* key that is accepted and then not acted on is the same silent downgrade this validation
|
|
52
|
+
* exists to prevent. `externalMu` is the mirror case, existing here and rejected above.
|
|
53
|
+
* `extraEntropy` is signing-only, so verification does not take it either.
|
|
54
|
+
*/
|
|
55
|
+
const INTERNAL_SIG_OPT_KEYS = /* @__PURE__ */ Object.freeze([
|
|
56
|
+
'extraEntropy',
|
|
57
|
+
'externalMu',
|
|
58
|
+
] as const);
|
|
59
|
+
const INTERNAL_VER_OPT_KEYS = /* @__PURE__ */ Object.freeze(['externalMu'] as const);
|
|
60
|
+
|
|
61
|
+
function validateInternalOpts<T extends TArg<DSAInternalOpts>>(
|
|
62
|
+
opts: T,
|
|
63
|
+
allowed: readonly string[]
|
|
64
|
+
): T {
|
|
65
|
+
const normalized = checkOptKeys(opts, allowed);
|
|
66
|
+
if (normalized.externalMu !== undefined) abool(normalized.externalMu, 'opts.externalMu');
|
|
67
|
+
return normalized;
|
|
49
68
|
}
|
|
50
69
|
|
|
51
70
|
/** ML-DSA signer surface with access to the internal message formatting mode. */
|
|
@@ -54,17 +73,26 @@ export type DSAInternal = CryptoKeys & {
|
|
|
54
73
|
sign: (
|
|
55
74
|
msg: TArg<Uint8Array>,
|
|
56
75
|
secretKey: TArg<Uint8Array>,
|
|
57
|
-
opts?: TArg<SigOpts & DSAInternalOpts>
|
|
76
|
+
opts?: TArg<Omit<SigOpts, 'context'> & DSAInternalOpts>
|
|
58
77
|
) => TRet<Uint8Array>;
|
|
59
78
|
verify: (
|
|
60
79
|
sig: TArg<Uint8Array>,
|
|
61
80
|
msg: TArg<Uint8Array>,
|
|
62
81
|
pubKey: TArg<Uint8Array>,
|
|
63
|
-
opts?: TArg<
|
|
82
|
+
opts?: TArg<DSAInternalOpts>
|
|
64
83
|
) => boolean;
|
|
65
84
|
};
|
|
66
85
|
/** Public ML-DSA signer surface. */
|
|
67
|
-
export type DSA = Signer & {
|
|
86
|
+
export type DSA = Signer & {
|
|
87
|
+
internal: TRet<DSAInternal>;
|
|
88
|
+
securityLevel: number;
|
|
89
|
+
/**
|
|
90
|
+
* HashML-DSA (FIPS 204 §5.4) variant which signs a pre-hashed message.
|
|
91
|
+
* @param hash - Approved hash, checked against the parameter set security level.
|
|
92
|
+
* @returns Signer which pre-hashes `msg` before formatting `M'`.
|
|
93
|
+
*/
|
|
94
|
+
prehash: (hash: TArg<CHash>) => TRet<Signer>;
|
|
95
|
+
};
|
|
68
96
|
|
|
69
97
|
// Constants
|
|
70
98
|
// FIPS 204 fixes ML-DSA over R = Z[X]/(X^256 + 1), so every polynomial has 256 coefficients.
|
|
@@ -154,6 +182,8 @@ const polyCoder = (d: number, compress: IdNum = id, verify: IdNum = id) =>
|
|
|
154
182
|
});
|
|
155
183
|
|
|
156
184
|
// Mutates `a` in place; callers must pass same-length polynomials.
|
|
185
|
+
// NOTE: conditional-reduction variants (as in ml-kem) were measured performance-neutral here —
|
|
186
|
+
// int32 `%` with 23-bit Q is already cheap — so the simpler mod() form is kept for audit.
|
|
157
187
|
const polyAdd = (a_: TArg<Poly>, b_: TArg<Poly>): TRet<Poly> => {
|
|
158
188
|
const a = a_ as Poly;
|
|
159
189
|
const b = b_ as Poly;
|
|
@@ -201,7 +231,7 @@ function RejNTTPoly(xof_: TArg<XofGet>): TRet<Poly> {
|
|
|
201
231
|
// Samples a polynomial ∈ Tq. xof() must return byte lengths divisible by 3.
|
|
202
232
|
const r = newPoly(N);
|
|
203
233
|
// NOTE: we can represent 3xu24 as 4xu32, but it doesn't improve perf :(
|
|
204
|
-
for (let j = 0; j < N;
|
|
234
|
+
for (let j = 0; j < N;) {
|
|
205
235
|
const b = xof();
|
|
206
236
|
if (b.length % 3) throw new Error('RejNTTPoly: unaligned block');
|
|
207
237
|
for (let i = 0; j < N && i <= b.length - 3; i += 3) {
|
|
@@ -275,13 +305,16 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
275
305
|
return res0;
|
|
276
306
|
};
|
|
277
307
|
|
|
308
|
+
// m = (q-1)/(2γ2): 44 for ML-DSA-44, 16 for 65/87. Hoisted out of UseHint, which runs
|
|
309
|
+
// per coefficient during verification.
|
|
310
|
+
const HINT_M = Math.floor((Q - 1) / (2 * GAMMA2));
|
|
278
311
|
const UseHint = (h: number, r: number) => {
|
|
279
312
|
// Returns the high bits of r adjusted according to hint h
|
|
280
|
-
const m = Math.floor((Q - 1) / (2 * GAMMA2));
|
|
281
313
|
const { r1, r0 } = decompose(r);
|
|
282
314
|
// 3: if h = 1 and r0 > 0 return (r1 + 1) mod m
|
|
283
315
|
// 4: if h = 1 and r0 ≤ 0 return (r1 − 1) mod m
|
|
284
|
-
if (h === 1)
|
|
316
|
+
if (h === 1)
|
|
317
|
+
return r0 > 0 ? crystals.mod(r1 + 1, HINT_M) | 0 : crystals.mod(r1 - 1, HINT_M) | 0;
|
|
285
318
|
return r1 | 0;
|
|
286
319
|
};
|
|
287
320
|
const Power2Round = (r: number) => {
|
|
@@ -360,7 +393,7 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
360
393
|
const xof = xof_ as XofGet;
|
|
361
394
|
// Samples an element a ∈ Rq with coeffcients in [−η, η] computed via rejection sampling from ρ.
|
|
362
395
|
const r: Poly = newPoly(N);
|
|
363
|
-
for (let j = 0; j < N;
|
|
396
|
+
for (let j = 0; j < N;) {
|
|
364
397
|
const b = xof();
|
|
365
398
|
for (let i = 0; j < N && i < b.length; i += 1) {
|
|
366
399
|
// half byte. Should be superfast with vector instructions. But very slow with js :(
|
|
@@ -384,7 +417,7 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
384
417
|
const masks = buf.slice(0, 8);
|
|
385
418
|
for (let i = N - TAU, pos = 8, maskPos = 0, maskBit = 0; i < N; i++) {
|
|
386
419
|
let b = i + 1;
|
|
387
|
-
for (; b > i;
|
|
420
|
+
for (; b > i;) {
|
|
388
421
|
b = buf[pos++];
|
|
389
422
|
if (pos < shake256.blockLen) continue;
|
|
390
423
|
s.xofInto(buf);
|
|
@@ -525,13 +558,34 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
525
558
|
secretKey: TArg<Uint8Array>,
|
|
526
559
|
opts: TArg<SigOpts & DSAInternalOpts> = {}
|
|
527
560
|
): TRet<Uint8Array> => {
|
|
528
|
-
validateSigOpts(opts);
|
|
529
|
-
validateInternalOpts(opts);
|
|
530
|
-
|
|
561
|
+
opts = validateSigOpts(opts, INTERNAL_SIG_OPT_KEYS);
|
|
562
|
+
opts = validateInternalOpts(opts, INTERNAL_SIG_OPT_KEYS);
|
|
563
|
+
const { extraEntropy: random, externalMu = false } = opts;
|
|
564
|
+
// FIPS 204 external-mu mode expects the 64-byte message representative µ = H(tr || M).
|
|
565
|
+
if (externalMu) abytes(msg, CRH_BYTES, 'mu');
|
|
566
|
+
// Prepare entropy before touching decoded secrets: randomBytes() may throw, and an RNG
|
|
567
|
+
// failure must not leave expanded secret-polynomial copies behind.
|
|
568
|
+
const ownRnd = random === false || random === undefined;
|
|
569
|
+
const rnd =
|
|
570
|
+
random === false
|
|
571
|
+
? new Uint8Array(32)
|
|
572
|
+
: random === undefined
|
|
573
|
+
? randomBytes(signRandBytes)
|
|
574
|
+
: (random as Uint8Array);
|
|
575
|
+
abytes(rnd, 32, 'extraEntropy');
|
|
531
576
|
// This part can be pre-cached per secretKey, but there is only minor performance improvement,
|
|
532
577
|
// since we re-use a lot of variables to computation.
|
|
533
578
|
// (ρ, K,tr, s1, s2, t0) ← skDecode(sk)
|
|
534
|
-
const
|
|
579
|
+
const decoded = (() => {
|
|
580
|
+
try {
|
|
581
|
+
return secretCoder.decode(secretKey);
|
|
582
|
+
} catch (error) {
|
|
583
|
+
// A malformed key must not strand entropy owned by the library.
|
|
584
|
+
if (ownRnd) cleanBytes(rnd);
|
|
585
|
+
throw error;
|
|
586
|
+
}
|
|
587
|
+
})();
|
|
588
|
+
const [rho, _K, tr, s1, s2, t0] = decoded;
|
|
535
589
|
// Cache matrix to avoid re-compute later
|
|
536
590
|
const A: Poly[][] = []; // A ← ExpandA(ρ)
|
|
537
591
|
const xof = XOF128(rho);
|
|
@@ -553,25 +607,18 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
553
607
|
// ▷ Compute message representative µ
|
|
554
608
|
shake256.create({ dkLen: CRH_BYTES }).update(tr).update(msg).digest();
|
|
555
609
|
|
|
556
|
-
// Compute private random seed
|
|
557
|
-
const rnd =
|
|
558
|
-
random === false
|
|
559
|
-
? new Uint8Array(32)
|
|
560
|
-
: random === undefined
|
|
561
|
-
? randomBytes(signRandBytes)
|
|
562
|
-
: random;
|
|
563
|
-
abytes(rnd, 32, 'extraEntropy');
|
|
564
610
|
const rhoprime = shake256
|
|
565
611
|
.create({ dkLen: CRH_BYTES })
|
|
566
612
|
.update(_K)
|
|
567
613
|
.update(rnd)
|
|
568
614
|
.update(mu)
|
|
569
615
|
.digest(); // ρ′← H(K||rnd||µ, 512)
|
|
570
|
-
|
|
616
|
+
// Only wipe entropy we generated; caller-provided extraEntropy stays caller-owned.
|
|
617
|
+
if (ownRnd) cleanBytes(rnd);
|
|
571
618
|
abytes(rhoprime, CRH_BYTES);
|
|
572
619
|
const x256 = XOF256(rhoprime, ZCoder.bytesLen);
|
|
573
620
|
// Rejection sampling loop
|
|
574
|
-
main_loop: for (let kappa = 0; ;
|
|
621
|
+
main_loop: for (let kappa = 0; ;) {
|
|
575
622
|
const y = [];
|
|
576
623
|
// y ← ExpandMask(ρ , κ)
|
|
577
624
|
for (let i = 0; i < L; i++, kappa++)
|
|
@@ -599,7 +646,13 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
599
646
|
const cs1 = s1.map((i) => MultiplyNTTs(i, cHat));
|
|
600
647
|
for (let i = 0; i < L; i++) {
|
|
601
648
|
polyAdd(crystals.NTT.decode(cs1[i]), y[i]); // z ← y + ⟨⟨cs1⟩⟩
|
|
602
|
-
if (polyChknorm(cs1[i], GAMMA1 - BETA))
|
|
649
|
+
if (polyChknorm(cs1[i], GAMMA1 - BETA)) {
|
|
650
|
+
// Rejected. Wipe this iteration's secret-derived buffers before retrying; the
|
|
651
|
+
// accepted path wipes the same set, and only the persistent key material (s1, s2,
|
|
652
|
+
// t0, A, rhoprime) is kept for the next iteration and cleaned at the very end.
|
|
653
|
+
cleanBytes(cTilde, cs1, cHat, w1, w, z, y);
|
|
654
|
+
continue main_loop; // ||z||∞ ≥ γ1 − β
|
|
655
|
+
}
|
|
603
656
|
}
|
|
604
657
|
// cs1 is now z (▷ Signer’s response)
|
|
605
658
|
let cnt = 0;
|
|
@@ -607,16 +660,25 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
607
660
|
for (let i = 0; i < K; i++) {
|
|
608
661
|
const cs2 = crystals.NTT.decode(MultiplyNTTs(s2[i], cHat)); // ⟨⟨cs2⟩⟩ ← NTT−1(cˆ◦ sˆ2)
|
|
609
662
|
const r0 = polySub(w[i], cs2).map(LowBits); // r0 ← LowBits(w − ⟨⟨cs2⟩⟩)
|
|
610
|
-
if (polyChknorm(r0, GAMMA2 - BETA))
|
|
663
|
+
if (polyChknorm(r0, GAMMA2 - BETA)) {
|
|
664
|
+
cleanBytes(cTilde, cs1, cHat, w1, w, z, y, h, cs2, r0);
|
|
665
|
+
continue main_loop; // ||r0||∞ ≥ γ2 − β
|
|
666
|
+
}
|
|
611
667
|
const ct0 = crystals.NTT.decode(MultiplyNTTs(t0[i], cHat)); // ⟨⟨ct0⟩⟩ ← NTT−1(cˆ◦ tˆ0)
|
|
612
|
-
if (polyChknorm(ct0, GAMMA2))
|
|
668
|
+
if (polyChknorm(ct0, GAMMA2)) {
|
|
669
|
+
cleanBytes(cTilde, cs1, cHat, w1, w, z, y, h, cs2, r0, ct0);
|
|
670
|
+
continue main_loop;
|
|
671
|
+
}
|
|
613
672
|
polyAdd(r0, ct0);
|
|
614
673
|
// ▷ Signer’s hint
|
|
615
674
|
const hint = polyMakeHint(r0, w1[i]); // h ← MakeHint(−⟨⟨ct0⟩⟩, w− ⟨⟨cs2⟩⟩ + ⟨⟨ct0⟩⟩)
|
|
616
675
|
h.push(hint.v);
|
|
617
676
|
cnt += hint.cnt;
|
|
618
677
|
}
|
|
619
|
-
if (cnt > OMEGA)
|
|
678
|
+
if (cnt > OMEGA) {
|
|
679
|
+
cleanBytes(cTilde, cs1, cHat, w1, w, z, y, h);
|
|
680
|
+
continue; // the number of 1’s in h is greater than ω
|
|
681
|
+
}
|
|
620
682
|
x256.clean();
|
|
621
683
|
const res = sigCoder.encode([cTilde, cs1, h]); // σ ← sigEncode(c˜, z mod±q, h)
|
|
622
684
|
// rho, _K, tr is subarray of secretKey, cannot clean.
|
|
@@ -636,8 +698,10 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
636
698
|
publicKey: TArg<Uint8Array>,
|
|
637
699
|
opts: TArg<DSAInternalOpts> = {}
|
|
638
700
|
) => {
|
|
639
|
-
validateInternalOpts(opts);
|
|
701
|
+
opts = validateInternalOpts(opts, INTERNAL_VER_OPT_KEYS);
|
|
640
702
|
const { externalMu = false } = opts;
|
|
703
|
+
// FIPS 204 external-mu mode expects the 64-byte message representative µ = H(tr || M).
|
|
704
|
+
if (externalMu) abytes(msg, CRH_BYTES, 'mu');
|
|
641
705
|
// ML-DSA.Verify(pk, M, σ): Verifes a signature σ for a message M.
|
|
642
706
|
const [rho, t1] = publicCoder.decode(publicKey); // (ρ, t1) ← pkDecode(pk)
|
|
643
707
|
const tr = shake256(publicKey, { dkLen: TR_BYTES }); // 6: tr ← H(BytesToBits(pk), 512)
|
|
@@ -699,9 +763,14 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
699
763
|
secretKey: TArg<Uint8Array>,
|
|
700
764
|
opts: TArg<SigOpts> = {}
|
|
701
765
|
): TRet<Uint8Array> => {
|
|
702
|
-
validateSigOpts(opts);
|
|
766
|
+
opts = validateSigOpts(opts);
|
|
703
767
|
const M = getMessage(msg, opts.context);
|
|
704
|
-
|
|
768
|
+
// `context` is consumed by getMessage() above; forwarding it would make the internal
|
|
769
|
+
// surface accept a key it never reads.
|
|
770
|
+
const res = internal.sign(M, secretKey, {
|
|
771
|
+
extraEntropy: opts.extraEntropy,
|
|
772
|
+
externalMu: false,
|
|
773
|
+
});
|
|
705
774
|
cleanBytes(M);
|
|
706
775
|
return res as TRet<Uint8Array>;
|
|
707
776
|
},
|
|
@@ -711,11 +780,13 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
711
780
|
publicKey: TArg<Uint8Array>,
|
|
712
781
|
opts: TArg<VerOpts> = {}
|
|
713
782
|
) => {
|
|
714
|
-
validateVerOpts(opts);
|
|
715
|
-
|
|
783
|
+
opts = validateVerOpts(opts);
|
|
784
|
+
abytes(sig, undefined, 'signature');
|
|
785
|
+
return internal.verify(sig, getMessage(msg, opts.context), publicKey, { externalMu: false });
|
|
716
786
|
},
|
|
717
|
-
prehash: (hash: CHash) => {
|
|
718
|
-
checkHash(hash, securityLevel);
|
|
787
|
+
prehash: (hash: TArg<CHash>): TRet<Signer> => {
|
|
788
|
+
checkHash(hash as CHash, securityLevel);
|
|
789
|
+
const rawHash = hash as CHash;
|
|
719
790
|
return Object.freeze({
|
|
720
791
|
info: Object.freeze({ type: 'hashml-dsa' }),
|
|
721
792
|
securityLevel: securityLevel,
|
|
@@ -727,9 +798,13 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
727
798
|
secretKey: TArg<Uint8Array>,
|
|
728
799
|
opts: TArg<SigOpts> = {}
|
|
729
800
|
): TRet<Uint8Array> => {
|
|
730
|
-
validateSigOpts(opts);
|
|
731
|
-
const M = getMessagePrehash(
|
|
732
|
-
|
|
801
|
+
opts = validateSigOpts(opts);
|
|
802
|
+
const M = getMessagePrehash(rawHash, msg, opts.context);
|
|
803
|
+
// As above: getMessagePrehash() consumes `context`, so it must not travel further.
|
|
804
|
+
const res = internal.sign(M, secretKey, {
|
|
805
|
+
extraEntropy: opts.extraEntropy,
|
|
806
|
+
externalMu: false,
|
|
807
|
+
});
|
|
733
808
|
cleanBytes(M);
|
|
734
809
|
return res as TRet<Uint8Array>;
|
|
735
810
|
},
|
|
@@ -739,15 +814,37 @@ function getDilithium(opts_: TArg<DilithiumOpts>): TRet<DSA> {
|
|
|
739
814
|
publicKey: TArg<Uint8Array>,
|
|
740
815
|
opts: TArg<VerOpts> = {}
|
|
741
816
|
) => {
|
|
742
|
-
validateVerOpts(opts);
|
|
743
|
-
|
|
817
|
+
opts = validateVerOpts(opts);
|
|
818
|
+
abytes(sig, undefined, 'signature');
|
|
819
|
+
return internal.verify(sig, getMessagePrehash(rawHash, msg, opts.context), publicKey, {
|
|
820
|
+
externalMu: false,
|
|
821
|
+
});
|
|
744
822
|
},
|
|
745
823
|
});
|
|
746
824
|
},
|
|
747
825
|
});
|
|
748
826
|
}
|
|
749
827
|
|
|
750
|
-
/**
|
|
828
|
+
/**
|
|
829
|
+
* ML-DSA-44 for 128-bit security level. Not recommended after 2030, as per ASD.
|
|
830
|
+
* @example
|
|
831
|
+
* Generate deterministic ML-DSA-44 keys, sign one message, and verify the signature.
|
|
832
|
+
* ```ts
|
|
833
|
+
* import { sha256 } from '@noble/hashes/sha2.js';
|
|
834
|
+
* import { ml_dsa44 } from '@noble/post-quantum/ml-dsa.js';
|
|
835
|
+
* const seed = new Uint8Array(ml_dsa44.lengths.seed!);
|
|
836
|
+
* const { secretKey, publicKey } = ml_dsa44.keygen(seed);
|
|
837
|
+
* const msg = new TextEncoder().encode('hello noble');
|
|
838
|
+
* const sig = ml_dsa44.sign(msg, secretKey);
|
|
839
|
+
* const isValid = ml_dsa44.verify(sig, msg, publicKey);
|
|
840
|
+
* const recovered = ml_dsa44.getPublicKey(secretKey);
|
|
841
|
+
* const context = new Uint8Array([1, 2, 3]);
|
|
842
|
+
* const prehash = ml_dsa44.prehash(sha256);
|
|
843
|
+
* const preSig = prehash.sign(msg, secretKey, { context });
|
|
844
|
+
* const preValid = prehash.verify(preSig, msg, publicKey, { context });
|
|
845
|
+
* const internalSig = ml_dsa44.internal.sign(msg, secretKey);
|
|
846
|
+
* ```
|
|
847
|
+
*/
|
|
751
848
|
export const ml_dsa44: TRet<DSA> = /* @__PURE__ */ (() =>
|
|
752
849
|
getDilithium({
|
|
753
850
|
...PARAMS[2],
|