Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
ML_DSA.cs
1using System;
2using System.IO;
4
5namespace Waher.Security.PQC
6{
11 public class ML_DSA : ML_Common
12 {
17 public static readonly ML_DSA ML_DSA_44 = new ML_DSA(39, 128, 1 << 17, (q - 1) / 88, 4, 4, 2, 80, 2560, 1312, 2420);
18
23 public static readonly ML_DSA ML_DSA_65 = new ML_DSA(49, 192, 1 << 19, (q - 1) / 32, 6, 5, 4, 55, 4032, 1952, 3309);
24
29 public static readonly ML_DSA ML_DSA_87 = new ML_DSA(60, 256, 1 << 19, (q - 1) / 32, 8, 7, 2, 75, 4896, 2592, 4627);
30
37 public static ML_DSA GetModel(string Name)
38 {
39 switch (Name.ToUpper())
40 {
41 case "ML-DSA-44":
42 return ML_DSA_44;
43
44 case "ML-DSA-65":
45 return ML_DSA_65;
46
47 case "ML-DSA-87":
48 return ML_DSA_87;
49
50 default:
51 throw new ArgumentException("Unknown model name: " + Name, nameof(Name));
52 }
53 }
54
55 private const int n = 256;
56 private const int q = 8380417;
57 private const int ζ = 1753; // 512th root of unity in ℤ𝑞
58 private const int d = 13; // #dropped bits from t
59 private const uint dBitMask = 0x1fff; // & dBitMask corresponds to mod 2^d.
60 private const short twoDHalf = 1 << (d - 1); // 2^(d-1)
61 private const short twoD = 1 << d; // 2^d
62
63 private readonly int τ; // # of ±1’s in polynomial c
64 private readonly int λ; // collision strength of c
65 private readonly uint γ1; // coefficient range of y
66 private readonly uint γ2; // low-order rounding range
67 private readonly byte k; // Rows of matrix A
68 private readonly byte l; // Columns of matrix A
69 private readonly byte η; // private key range
70 private readonly int β; // τ*η
71 private readonly int ω; // max # of 1’s in the hint h
72
73 private readonly int twoγ2; // 2*γ2
74
75 private readonly int privateKeySize;
76 private readonly int publicKeySize;
77 private readonly int signatureSize;
78
94 public ML_DSA(int τ, int λ, uint γ1, uint γ2, byte k, byte l, byte η, int ω,
95 int PrivateKeySize, int PublicKeySize, int SignatureSize)
96 {
97 if (η != 2 && η != 4)
98 throw new ArgumentException("η must be 2 or 4.", nameof(η));
99
100 this.τ = τ;
101 this.λ = λ;
102 this.γ1 = γ1;
103 this.γ2 = γ2;
104 this.k = k;
105 this.l = l;
106 this.η = η;
107 this.β = τ * η;
108 this.ω = ω;
109
110 this.twoγ2 = (int)(this.γ2 << 1);
111
112 this.privateKeySize = PrivateKeySize;
113 this.publicKeySize = PublicKeySize;
114 this.signatureSize = SignatureSize;
115 }
116
120 public override int PublicKeyLength => this.publicKeySize;
121
125 public override int PrivateKeyLength => this.privateKeySize;
126
134 {
135 return this.KeyGen(false);
136 }
137
145 public ML_DSA_Keys KeyGen(bool ReturnSeed)
146 {
147 byte[] ξ = CreateSeed();
148
149 ML_DSA_Keys Result = this.KeyGen_Internal(ξ, ReturnSeed);
150
151 if (!ReturnSeed)
152 Clear(ξ);
153
154 return Result;
155 }
156
166 public byte[] Sign(byte[] PrivateKey, byte[] Message, out int RejectionCount)
167 {
168 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message,
169 out RejectionCount);
170 }
171
181 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, out int RejectionCount)
182 {
183 return this.Sign(Keys, Message, (byte[])null, out RejectionCount);
184 }
185
196 public byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context,
197 out int RejectionCount)
198 {
199 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
200 out RejectionCount);
201 }
202
213 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context,
214 out int RejectionCount)
215 {
216 byte[] ξ = CreateSeed();
217 byte[] Result = this.Sign(Keys, Message, Context, ξ, out RejectionCount);
218 Clear(ξ);
219
220 return Result;
221 }
222
234 public byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, byte[] Seed,
235 out int RejectionCount)
236 {
237 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
238 Seed, out RejectionCount);
239 }
240
252 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, byte[] Seed,
253 out int RejectionCount)
254 {
255 int MessageLen = Message.Length;
256 int ContextLen;
257
258 if (Context is null)
259 {
260 Context = Array.Empty<byte>();
261 ContextLen = 0;
262 }
263 else if ((ContextLen = Context.Length) > 255)
264 throw new ArgumentException("Context must be 255 bytes or less.", nameof(Context));
265
266 if (Seed is null)
267 Seed = new byte[32];
268 else if (Seed.Length != 32)
269 throw new ArgumentException("Seed must be 32 bytes long.", nameof(Seed));
270
271 byte[] Bin = new byte[2 + ContextLen + MessageLen];
272 Bin[0] = 0;
273 Bin[1] = (byte)ContextLen;
274
275 if (ContextLen > 0)
276 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
277
278 Buffer.BlockCopy(Message, 0, Bin, 2 + ContextLen, MessageLen);
279
280 return this.Sign_Internal(Keys, Bin, false, Seed, out RejectionCount);
281 }
282
291 public bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature)
292 {
293 return this.Verify(ML_DSA_Keys.FromPublicKey(PublicKey), Message, Signature);
294 }
295
304 public bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature)
305 {
306 return this.Verify(Keys, Message, Signature, null);
307 }
308
318 public bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature, byte[] Context)
319 {
320 return this.Verify(ML_DSA_Keys.FromPublicKey(PublicKey), Message, Signature,
321 Context);
322 }
323
333 public bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature, byte[] Context)
334 {
335 int MessageLen = Message.Length;
336 int ContextLen;
337
338 if (Context is null)
339 {
340 Context = Array.Empty<byte>();
341 ContextLen = 0;
342 }
343 else if ((ContextLen = Context.Length) > 255)
344 return false;
345
346 byte[] Bin = new byte[2 + ContextLen + MessageLen];
347 Bin[0] = 0;
348 Bin[1] = (byte)ContextLen;
349
350 if (ContextLen > 0)
351 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
352
353 Buffer.BlockCopy(Message, 0, Bin, 2 + ContextLen, MessageLen);
354
355 return this.Verify_Internal(Keys, Bin, false, Signature);
356 }
357
368 public byte[] Sign(byte[] PrivateKey, byte[] Message, string HashAlgorithm,
369 out int RejectionCount)
370 {
371 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message,
372 HashAlgorithm, out RejectionCount);
373 }
374
385 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, string HashAlgorithm,
386 out int RejectionCount)
387 {
388 return this.Sign(Keys, Message, null, HashAlgorithm, out RejectionCount);
389 }
390
402 public byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context,
403 string HashAlgorithm, out int RejectionCount)
404 {
405 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
406 HashAlgorithm, out RejectionCount);
407 }
408
420 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context,
421 string HashAlgorithm, out int RejectionCount)
422 {
423 byte[] ξ = CreateSeed();
424 byte[] Result = this.Sign(Keys, Message, Context, ξ, HashAlgorithm,
425 out RejectionCount);
426
427 Clear(ξ);
428
429 return Result;
430 }
431
444 public byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, byte[] Seed,
445 string HashAlgorithm, out int RejectionCount)
446 {
447 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
448 Seed, HashAlgorithm, out RejectionCount);
449 }
450
463 public byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, byte[] Seed,
464 string HashAlgorithm, out int RejectionCount)
465 {
466 int ContextLen;
467
468 if (Context is null)
469 {
470 Context = Array.Empty<byte>();
471 ContextLen = 0;
472 }
473 else if ((ContextLen = Context.Length) > 255)
474 throw new ArgumentException("Context must be 255 bytes or less.", nameof(Context));
475
476 if (Seed is null)
477 Seed = new byte[32];
478 else if (Seed.Length != 32)
479 throw new ArgumentException("Seed must be 32 bytes long.", nameof(Seed));
480
481 if (!TryGetHashFunction(HashAlgorithm, out HashFunctionArray H, out byte[] Oid))
482 throw new ArgumentException("Unsupported hash algorithm: " + HashAlgorithm, nameof(HashAlgorithm));
483
484 Message = H(Message);
485
486 int MessageLen = Message.Length;
487 int OidLen = Oid.Length;
488 int Pos;
489
490 byte[] Bin = new byte[2 + ContextLen + OidLen + MessageLen];
491 Bin[0] = 1;
492 Bin[1] = (byte)ContextLen;
493
494 if (ContextLen > 0)
495 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
496
497 Buffer.BlockCopy(Oid, 0, Bin, Pos = 2 + ContextLen, OidLen);
498 Pos += OidLen;
499
500 Buffer.BlockCopy(Message, 0, Bin, Pos, MessageLen);
501
502 return this.Sign_Internal(Keys, Bin, false, Seed, out RejectionCount);
503 }
504
516 public byte[] Sign(byte[] PrivateKey, Stream Message, byte[] Context,
517 string HashAlgorithm, out int RejectionCount)
518 {
519 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
520 HashAlgorithm, out RejectionCount);
521 }
522
534 public byte[] Sign(ML_DSA_Keys Keys, Stream Message, byte[] Context,
535 string HashAlgorithm, out int RejectionCount)
536 {
537 byte[] ξ = CreateSeed();
538 byte[] Result = this.Sign(Keys, Message, Context, ξ, HashAlgorithm,
539 out RejectionCount);
540
541 Clear(ξ);
542
543 return Result;
544 }
545
558 public byte[] Sign(byte[] PrivateKey, Stream Message, byte[] Context, byte[] Seed,
559 string HashAlgorithm, out int RejectionCount)
560 {
561 return this.Sign(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message, Context,
562 Seed, HashAlgorithm, out RejectionCount);
563 }
564
577 public byte[] Sign(ML_DSA_Keys Keys, Stream Message, byte[] Context, byte[] Seed,
578 string HashAlgorithm, out int RejectionCount)
579 {
580 int ContextLen;
581
582 if (Context is null)
583 {
584 Context = Array.Empty<byte>();
585 ContextLen = 0;
586 }
587 else if ((ContextLen = Context.Length) > 255)
588 throw new ArgumentException("Context must be 255 bytes or less.", nameof(Context));
589
590 if (Seed is null)
591 Seed = new byte[32];
592 else if (Seed.Length != 32)
593 throw new ArgumentException("Seed must be 32 bytes long.", nameof(Seed));
594
595 if (!TryGetHashFunction(HashAlgorithm, out HashFunctionStream H, out byte[] Oid))
596 throw new ArgumentException("Unsupported hash algorithm: " + HashAlgorithm, nameof(HashAlgorithm));
597
598 Message.Position = 0;
599 byte[] Message2 = H(Message);
600
601 int Message2Len = Message2.Length;
602 int OidLen = Oid.Length;
603 int Pos;
604
605 byte[] Bin = new byte[2 + ContextLen + OidLen + Message2Len];
606 Bin[0] = 1;
607 Bin[1] = (byte)ContextLen;
608
609 if (ContextLen > 0)
610 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
611
612 Buffer.BlockCopy(Oid, 0, Bin, Pos = 2 + ContextLen, OidLen);
613 Pos += OidLen;
614
615 Buffer.BlockCopy(Message2, 0, Bin, Pos, Message2Len);
616
617 return this.Sign_Internal(Keys, Bin, false, Seed, out RejectionCount);
618 }
619
620 private static bool TryGetHashFunction(string Name, out HashFunctionArray H, out byte[] Oid)
621 {
622 switch (Name.ToUpper())
623 {
624 case "SHA256":
625 case "SHA-256":
626 case "SHA2-256":
627 Oid = oidSha256;
629 return true;
630
631 case "SHA384":
632 case "SHA-384":
633 case "SHA2-384":
634 Oid = oidSha384;
636 return true;
637
638 case "SHA512":
639 case "SHA-512":
640 case "SHA2-512":
641 Oid = oidSha512;
643 return true;
644
645 case "SHA3-224":
646 Oid = oidSha3_224;
647 H = new SHA3_224().ComputeVariable;
648 return true;
649
650 case "SHA3-256":
651 Oid = oidSha3_256;
652 H = new SHA3_256().ComputeVariable;
653 return true;
654
655 case "SHA3-384":
656 Oid = oidSha3_384;
657 H = new SHA3_384().ComputeVariable;
658 return true;
659
660 case "SHA3-512":
661 Oid = oidSha3_512;
662 H = new SHA3_512().ComputeVariable;
663 return true;
664
665 case "SHAKE128":
666 case "SHAKE-128":
667 Oid = oidShake128;
668 H = new SHAKE128(256).ComputeVariable;
669 return true;
670
671 case "SHAKE256":
672 case "SHAKE-256":
673 Oid = oidShake256;
674 H = new SHAKE256(512).ComputeVariable;
675 return true;
676
677 default:
678 Oid = null;
679 H = null;
680 return false;
681 }
682 }
683
684 private static bool TryGetHashFunction(string Name, out HashFunctionStream H, out byte[] Oid)
685 {
686 switch (Name.ToUpper())
687 {
688 case "SHA256":
689 case "SHA-256":
690 case "SHA2-256":
691 Oid = oidSha256;
693 return true;
694
695 case "SHA384":
696 case "SHA-384":
697 case "SHA2-384":
698 Oid = oidSha384;
700 return true;
701
702 case "SHA512":
703 case "SHA-512":
704 case "SHA2-512":
705 Oid = oidSha512;
707 return true;
708
709 case "SHA3-224":
710 Oid = oidSha3_224;
711 H = new SHA3_224().ComputeVariable;
712 return true;
713
714 case "SHA3-256":
715 Oid = oidSha3_256;
716 H = new SHA3_256().ComputeVariable;
717 return true;
718
719 case "SHA3-384":
720 Oid = oidSha3_384;
721 H = new SHA3_384().ComputeVariable;
722 return true;
723
724 case "SHA3-512":
725 Oid = oidSha3_512;
726 H = new SHA3_512().ComputeVariable;
727 return true;
728
729 case "SHAKE128":
730 case "SHAKE-128":
731 Oid = oidShake128;
732 H = new SHAKE128(256).ComputeVariable;
733 return true;
734
735 case "SHAKE256":
736 case "SHAKE-256":
737 Oid = oidShake256;
738 H = new SHAKE256(512).ComputeVariable;
739 return true;
740
741 default:
742 Oid = null;
743 H = null;
744 return false;
745 }
746 }
747
748 private static readonly byte[] oidSha256 = new byte[]
749 {
750 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01
751 };
752
753 private static readonly byte[] oidSha384 = new byte[]
754 {
755 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02
756 };
757
758 private static readonly byte[] oidSha512 = new byte[]
759 {
760 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03
761 };
762
763 private static readonly byte[] oidSha3_224 = new byte[]
764 {
765 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x07
766 };
767 private static readonly byte[] oidSha3_256 = new byte[]
768 {
769 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x08
770 };
771
772 private static readonly byte[] oidSha3_384 = new byte[]
773 {
774 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x09
775 };
776
777 private static readonly byte[] oidSha3_512 = new byte[]
778 {
779 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0a
780 };
781
782 private static readonly byte[] oidShake128 = new byte[]
783 {
784 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0b
785 };
786
787 private static readonly byte[] oidShake256 = new byte[]
788 {
789 0x06, 0x09, 0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x0c
790 };
791
802 public bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature, byte[] Context,
803 string HashAlgorithm)
804 {
805 return this.Verify(ML_DSA_Keys.FromPublicKey(PublicKey), Message, Signature,
806 Context, HashAlgorithm);
807 }
808
819 public bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature, byte[] Context,
820 string HashAlgorithm)
821 {
822 int ContextLen;
823
824 if (Context is null)
825 {
826 Context = Array.Empty<byte>();
827 ContextLen = 0;
828 }
829 else if ((ContextLen = Context.Length) > 255)
830 return false;
831
832 if (!TryGetHashFunction(HashAlgorithm, out HashFunctionArray H, out byte[] Oid))
833 throw new ArgumentException("Unsupported hash algorithm: " + HashAlgorithm, nameof(HashAlgorithm));
834
835 Message = H(Message);
836
837 int MessageLen = Message.Length;
838 int OidLen = Oid.Length;
839 int Pos;
840
841 byte[] Bin = new byte[2 + ContextLen + OidLen + MessageLen];
842 Bin[0] = 1;
843 Bin[1] = (byte)ContextLen;
844
845 if (ContextLen > 0)
846 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
847
848 Buffer.BlockCopy(Oid, 0, Bin, Pos = 2 + ContextLen, OidLen);
849 Pos += OidLen;
850
851 Buffer.BlockCopy(Message, 0, Bin, Pos, MessageLen);
852
853 return this.Verify_Internal(Keys, Bin, false, Signature);
854 }
855
866 public bool Verify(byte[] PublicKey, Stream Message, byte[] Signature, byte[] Context,
867 string HashAlgorithm)
868 {
869 return this.Verify(ML_DSA_Keys.FromPublicKey(PublicKey), Message, Signature,
870 Context, HashAlgorithm);
871 }
872
883 public bool Verify(ML_DSA_Keys Keys, Stream Message, byte[] Signature, byte[] Context,
884 string HashAlgorithm)
885 {
886 int ContextLen;
887
888 if (Context is null)
889 {
890 Context = Array.Empty<byte>();
891 ContextLen = 0;
892 }
893 else if ((ContextLen = Context.Length) > 255)
894 return false;
895
896 if (!TryGetHashFunction(HashAlgorithm, out HashFunctionStream H, out byte[] Oid))
897 throw new ArgumentException("Unsupported hash algorithm: " + HashAlgorithm, nameof(HashAlgorithm));
898
899 Message.Position = 0;
900 byte[] Message2 = H(Message);
901
902 int Message2Len = Message2.Length;
903 int OidLen = Oid.Length;
904 int Pos;
905
906 byte[] Bin = new byte[2 + ContextLen + OidLen + Message2Len];
907 Bin[0] = 1;
908 Bin[1] = (byte)ContextLen;
909
910 if (ContextLen > 0)
911 Buffer.BlockCopy(Context, 0, Bin, 2, ContextLen);
912
913 Buffer.BlockCopy(Oid, 0, Bin, Pos = 2 + ContextLen, OidLen);
914 Pos += OidLen;
915
916 Buffer.BlockCopy(Message2, 0, Bin, Pos, Message2Len);
917
918 return this.Verify_Internal(Keys, Bin, false, Signature);
919 }
920
928 public ML_DSA_Keys KeyGen_Internal(byte[] ξ)
929 {
930 return this.KeyGen_Internal(ξ, false);
931 }
932
941 public ML_DSA_Keys KeyGen_Internal(byte[] ξ, bool ReturnSeed)
942 {
943 if (ξ.Length != 32)
944 throw new ArgumentException("Seed must be 32 bytes long.", nameof(ξ));
945
946 byte[] Bin = new byte[34];
947 Buffer.BlockCopy(ξ, 0, Bin, 0, 32);
948 Bin[32] = this.k;
949 Bin[33] = this.l;
950
951 byte[] Bin2 = H(Bin, 128);
952 Clear(Bin);
953
954 byte[] ρ = new byte[32];
955 Buffer.BlockCopy(Bin2, 0, ρ, 0, 32);
956
957 byte[] K = new byte[32];
958 Buffer.BlockCopy(Bin2, 96, K, 0, 32);
959
960 uint[,][] Â = this.ExpandÂ(ρ);
961
962 byte[] B = new byte[66]; // ρ´ || 2 index bytes
963 Buffer.BlockCopy(Bin2, 32, B, 0, 64);
964
965 short[][] s1 = this.ExpandS(B, this.l);
966 short[][] s2 = this.ExpandS(B, this.k);
967
968 Clear(B);
969
970 uint[][] NTTs1 = NTT(s1);
971
972 uint[][] t = new uint[this.k][];
973 uint[] f;
974 int i, j;
975
976 for (i = 0; i < this.k; i++)
977 {
978 t[i] = f = new uint[n];
979
980 for (j = 0; j < this.l; j++)
981 MultiplyNTTsAndAdd(Â[i, j], NTTs1[j], f);
982 }
983
984 InverseNTT(t);
985 AddTo(t, s2);
986
987 // Power2Round, decomposes t into two arrays t1, t0, where t=t1*2^d+t0 mod q
988 // (Algorithm 35, §7.4)
989
990 short[][] t0 = new short[this.k][];
991 short[][] t1 = new short[this.k][];
992 short[] f0, f1;
993 short r0, r1;
994 uint r;
995
996 for (i = 0; i < this.k; i++)
997 {
998 f = t[i];
999 t0[i] = f0 = new short[n];
1000 t1[i] = f1 = new short[n];
1001
1002 for (j = 0; j < n; j++)
1003 {
1004 r = f[j];
1005 r0 = (short)(r & dBitMask);
1006
1007 if (r0 > twoDHalf)
1008 r0 -= twoD;
1009
1010 f0[j] = r0;
1011
1012 r1 = (short)((r - r0) >> d);
1013
1014 f1[j] = r1;
1015 }
1016 }
1017
1018 byte[] PublicKey = this.PublicKeyEncode(ρ, t1);
1019 byte[] tr = H(PublicKey, 64);
1020 byte[] PrivateKey = this.PrivateKeyEncode(ρ, K, tr, s1, s2, t0);
1021
1022 Clear(Bin2);
1023 Clear(t);
1024 Clear(ρ);
1025 Clear(K);
1026 Clear(s1);
1027 Clear(s2);
1028 Clear(t0);
1029
1030 return new ML_DSA_Keys(Â, PublicKey, PrivateKey, ReturnSeed ? ξ : null);
1031 }
1032
1039 private uint[,][] ExpandÂ(byte[] ρ)
1040 {
1041 if (ρ.Length != 32)
1042 throw new ArgumentException("Seed must be 32 bytes long.", nameof(ρ));
1043
1044 uint[,][] Â = new uint[this.k, this.l][];
1045 byte r, c;
1046
1047 byte[] B = new byte[34];
1048 Buffer.BlockCopy(ρ, 0, B, 0, 32);
1049
1050 for (r = 0; r < this.k; r++)
1051 {
1052 B[33] = r;
1053
1054 for (c = 0; c < this.l; c++)
1055 {
1056 B[32] = c;
1057 Â[r, c] = RejNttPoly(B);
1058 }
1059 }
1060
1061 Clear(B);
1062
1063 return Â;
1064 }
1065
1073 private short[][] ExpandS(byte[] Seed, int Nr)
1074 {
1075 short[][] s = new short[Nr][];
1076 byte r = 0;
1077
1078 while (Nr-- > 0)
1079 {
1080 s[r++] = this.RejBoundedPoly(Seed);
1081 Seed[64]++;
1082 }
1083
1084 return s;
1085 }
1086
1094 private byte[] PublicKeyEncode(byte[] ρ, short[][] t1)
1095 {
1096 if (ρ.Length != 32)
1097 throw new ArgumentException("Seed must be 32 bytes long.", nameof(ρ));
1098
1099 if (t1.Length != this.k)
1100 throw new ArgumentException("Invalid polynomial size.", nameof(t1));
1101
1102 byte[] PublicKey = new byte[this.publicKeySize];
1103 int Pos = 32;
1104
1105 Buffer.BlockCopy(ρ, 0, PublicKey, 0, 32);
1106
1107 for (int i = 0; i < this.k; i++)
1108 Pos += SimpleBitPack(t1[i], 10, PublicKey, Pos);
1109
1110 return PublicKey;
1111 }
1112
1124 private byte[] PrivateKeyEncode(byte[] ρ, byte[] K, byte[] tr, short[][] s1, short[][] s2, short[][] t0)
1125 {
1126 if (ρ.Length != 32)
1127 throw new ArgumentException("Seed must be 32 bytes long.", nameof(ρ));
1128
1129 if (K.Length != 32)
1130 throw new ArgumentException("K must be 32 bytes long.", nameof(K));
1131
1132 if (tr.Length != 64)
1133 throw new ArgumentException("tr must be 32 bytes long.", nameof(tr));
1134
1135 if (s1.Length != this.l)
1136 throw new ArgumentException("s1 must be " + this.l + " polynomials long.", nameof(s1));
1137
1138 if (s2.Length != this.k)
1139 throw new ArgumentException("s2 must be " + this.k + " polynomials long.", nameof(s2));
1140
1141 if (t0.Length != this.k)
1142 throw new ArgumentException("t0 must be " + this.k + " polynomials long.", nameof(t0));
1143
1144 byte[] PrivateKey = new byte[this.privateKeySize];
1145 int Pos = 128;
1146 int i;
1147
1148 Buffer.BlockCopy(ρ, 0, PrivateKey, 0, 32);
1149 Buffer.BlockCopy(K, 0, PrivateKey, 32, 32);
1150 Buffer.BlockCopy(tr, 0, PrivateKey, 64, 64);
1151
1152 for (i = 0; i < this.l; i++)
1153 Pos += BitPack(s1[i], PrivateKey, Pos, this.η, this.η);
1154
1155 for (i = 0; i < this.k; i++)
1156 Pos += BitPack(s2[i], PrivateKey, Pos, this.η, this.η);
1157
1158 uint b = 1 << (d - 1);
1159 uint a = b - 1;
1160
1161 for (i = 0; i < this.k; i++)
1162 Pos += BitPack(t0[i], PrivateKey, Pos, a, b);
1163
1164 return PrivateKey;
1165 }
1166
1179 private bool TryDecodePrivateKey(byte[] PrivateKey, out byte[] ρ, out byte[] K,
1180 out byte[] tr, out short[][] s1, out short[][] s2, out short[][] t0)
1181 {
1182 if (PrivateKey is null || PrivateKey.Length != this.privateKeySize)
1183 {
1184 ρ = null;
1185 K = null;
1186 tr = null;
1187 s1 = null;
1188 s2 = null;
1189 t0 = null;
1190
1191 return false;
1192 }
1193
1194 int Pos = 128;
1195 int i;
1196
1197 ρ = new byte[32];
1198 K = new byte[32];
1199 tr = new byte[64];
1200 s1 = new short[this.l][];
1201 s2 = new short[this.k][];
1202 t0 = new short[this.k][];
1203
1204 Buffer.BlockCopy(PrivateKey, 0, ρ, 0, 32);
1205 Buffer.BlockCopy(PrivateKey, 32, K, 0, 32);
1206 Buffer.BlockCopy(PrivateKey, 64, tr, 0, 64);
1207
1208 for (i = 0; i < this.l; i++)
1209 {
1210 s1[i] = new short[n];
1211 Pos += BitUnpack(s1[i], PrivateKey, Pos, this.η, this.η);
1212 }
1213
1214 for (i = 0; i < this.k; i++)
1215 {
1216 s2[i] = new short[n];
1217 Pos += BitUnpack(s2[i], PrivateKey, Pos, this.η, this.η);
1218 }
1219
1220 uint b = 1 << (d - 1);
1221 uint a = b - 1;
1222
1223 for (i = 0; i < this.k; i++)
1224 {
1225 t0[i] = new short[n];
1226 Pos += BitUnpack(t0[i], PrivateKey, Pos, a, b);
1227 }
1228
1229 return Pos == this.privateKeySize;
1230 }
1231
1238 private static uint[] RejNttPoly(byte[] Seed)
1239 {
1241 Keccak1600.Context Context = HashFunction.Absorb(Seed);
1242 uint[] Result = new uint[n];
1243 int Pos = 0;
1244
1245 while (Pos < n)
1246 {
1247 byte[] C = Context.Squeeze(3);
1248
1249 // CoeffFromThreeBytes, Algorithm 14, §7.1
1250
1251 uint i = (uint)(C[2] & 0x7f);
1252 i <<= 8;
1253 i |= C[1];
1254 i <<= 8;
1255 i |= C[0];
1256
1257 if (i < q)
1258 Result[Pos++] = i;
1259 }
1260
1261 return Result;
1262 }
1263
1270 public short[] RejBoundedPoly(byte[] Seed)
1271 {
1273 Keccak1600.Context Context = HashFunction.Absorb(Seed);
1274 short[] Result = new short[n];
1275 int Pos = 0;
1276
1277 if (this.η == 2)
1278 {
1279 while (Pos < n)
1280 {
1281 byte z = Context.Squeeze1();
1282 byte b1 = (byte)(z & 0x0f);
1283 byte b2 = (byte)(z >> 4);
1284
1285 // CoeffFromHalfByte, Algorithm 15, §7.1
1286
1287 if (b1 < 15)
1288 Result[Pos++] = (short)(2 - (b1 % 5));
1289
1290 if (Pos < n && b2 < 15)
1291 Result[Pos++] = (short)(2 - (b2 % 5));
1292 }
1293 }
1294 else if (this.η == 4)
1295 {
1296 while (Pos < n)
1297 {
1298 byte z = Context.Squeeze1();
1299 byte b1 = (byte)(z & 0x0f);
1300 byte b2 = (byte)(z >> 4);
1301
1302 // CoeffFromHalfByte, Algorithm 15, §7.1
1303
1304 if (b1 < 9)
1305 Result[Pos++] = (short)(4 - b1);
1306
1307 if (Pos < n && b2 < 9)
1308 Result[Pos++] = (short)(4 - b2);
1309 }
1310 }
1311 else
1312 throw new InvalidOperationException("Invalid η.");
1313
1314 return Result;
1315 }
1316
1322 public static void NTT(uint[] f)
1323 {
1324 if (f.Length != n)
1325 throw new ArgumentException("Polynomial must have " + n + " coefficients.", nameof(f));
1326
1327 int j;
1328 int Len;
1329 int Start;
1330 int m = 0;
1331 ulong ζ;
1332 uint t;
1333
1334 for (Len = n >> 1; Len >= 1; Len >>= 1)
1335 {
1336 for (Start = 0; Start < n; Start += Len << 1)
1337 {
1338 ζ = nttTransformZeta[++m];
1339
1340 for (j = Start; j < Start + Len; j++)
1341 {
1342 t = (uint)(ζ * f[j + Len] % q);
1343 f[j + Len] = (f[j] + q - t) % q;
1344 f[j] = (f[j] + t) % q;
1345 }
1346 }
1347 }
1348 }
1349
1354 public static void NTT(uint[][] f)
1355 {
1356 int i, c = f.Length;
1357
1358 for (i = 0; i < c; i++)
1359 NTT(f[i]);
1360 }
1361
1367 public static uint[] NTT(short[] f)
1368 {
1369 uint[] f0 = ToUIntVector(f);
1370 NTT(f0);
1371 return f0;
1372 }
1373
1374 private static uint[] ToUIntVector(short[] f)
1375 {
1376 int i, c = f.Length;
1377 uint[] f0 = new uint[n];
1378 short v;
1379
1380 for (i = 0; i < c; i++)
1381 {
1382 v = f[i];
1383 f0[i] = (uint)(v < 0 ? q + v : v);
1384 }
1385
1386 return f0;
1387 }
1388
1394 public static uint[][] NTT(short[][] f)
1395 {
1396 int i, c = f.Length;
1397 uint[][] Result = new uint[c][];
1398
1399 for (i = 0; i < c; i++)
1400 Result[i] = NTT(f[i]);
1401
1402 return Result;
1403 }
1404
1410 public static void InverseNTT(uint[] f)
1411 {
1412 if (f.Length != n)
1413 throw new ArgumentException("Polynomial must have " + n + " coefficients.", nameof(f));
1414
1415 int j;
1416 int Len;
1417 int Len2;
1418 int StartLen;
1419 int Start;
1420 int m = 256;
1421 ulong ζ;
1422 uint t;
1423
1424 for (Len = 1; Len < n; Len <<= 1)
1425 {
1426 Len2 = Len << 1;
1427
1428 for (Start = 0; Start < n; Start += Len2)
1429 {
1430 ζ = nttTransformZeta[--m];
1431
1432 StartLen = Start + Len;
1433 for (j = Start; j < StartLen; j++)
1434 {
1435 t = f[j];
1436 f[j] = (t + f[j + Len]) % q;
1437 f[j + Len] = (uint)(ζ * (f[j + Len] + q - t) % q);
1438 }
1439 }
1440 }
1441
1442 for (j = 0; j < n; j++)
1443 f[j] = (uint)(8347681ul * f[j] % q); // 8347681 = 256^-1 mod q
1444 }
1445
1450 public static void InverseNTT(uint[][] f)
1451 {
1452 int i, c = f.Length;
1453
1454 for (i = 0; i < c; i++)
1455 InverseNTT(f[i]);
1456 }
1457
1468 public static void MultiplyNTTsAndAdd(uint[] f, uint[] g, uint[] Result)
1469 {
1470 if (f.Length != n || g.Length != n)
1471 throw new ArgumentException("Polynomials must have " + n + " coefficients.", nameof(f));
1472
1473 int i;
1474
1475 for (i = 0; i < n; i++)
1476 Result[i] = (uint)((Result[i] + (ulong)f[i] * g[i]) % q);
1477 }
1478
1489 public static void MultiplyNTTsAndAdd(uint[] f, short[] g, uint[] Result)
1490 {
1491 if (f.Length != n || g.Length != n)
1492 throw new ArgumentException("Polynomials must have " + n + " coefficients.", nameof(f));
1493
1494 int i;
1495 short v;
1496
1497 for (i = 0; i < n; i++)
1498 {
1499 v = g[i];
1500
1501 if (v < 0)
1502 Result[i] = (uint)((Result[i] + (ulong)f[i] * (uint)(q + v)) % q);
1503 else
1504 Result[i] = (uint)((Result[i] + (ulong)f[i] * (uint)v) % q);
1505 }
1506 }
1507
1513 public static void AddTo(int[] f, short[] g)
1514 {
1515 int i;
1516
1517 for (i = 0; i < n; i++)
1518 f[i] += g[i];
1519 }
1520
1526 public static void AddTo(int[][] f, short[][] g)
1527 {
1528 int i, c = f.Length;
1529 if (g.Length != c)
1530 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(g));
1531
1532 for (i = 0; i < c; i++)
1533 AddTo(f[i], g[i]);
1534 }
1535
1541 public static void AddTo(uint[] f, short[] g)
1542 {
1543 int i;
1544 short v;
1545
1546 for (i = 0; i < n; i++)
1547 {
1548 v = g[i];
1549
1550 if (v < 0)
1551 f[i] = (uint)((f[i] + q + v) % q);
1552 else
1553 f[i] = (uint)((f[i] + v) % q);
1554 }
1555 }
1556
1562 public static void AddTo(uint[][] f, short[][] g)
1563 {
1564 int i, c = f.Length;
1565 if (g.Length != c)
1566 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(g));
1567
1568 for (i = 0; i < c; i++)
1569 AddTo(f[i], g[i]);
1570 }
1571
1577 public static void AddTo(uint[] f, uint[] g)
1578 {
1579 int i;
1580
1581 for (i = 0; i < n; i++)
1582 f[i] = (f[i] + g[i]) % q;
1583 }
1584
1590 public static void AddTo(uint[][] f, uint[][] g)
1591 {
1592 int i, c = f.Length;
1593 if (g.Length != c)
1594 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(g));
1595
1596 for (i = 0; i < c; i++)
1597 AddTo(f[i], g[i]);
1598 }
1599
1605 public static void SubtractFrom(uint[] f, short[] g)
1606 {
1607 int i;
1608 short v;
1609
1610 for (i = 0; i < n; i++)
1611 {
1612 v = g[i];
1613
1614 if (v < 0)
1615 f[i] = (uint)((f[i] - v) % q);
1616 else
1617 f[i] = (uint)((f[i] + q - v) % q);
1618 }
1619 }
1620
1626 public static void SubtractFrom(uint[][] f, short[][] g)
1627 {
1628 int i, c = f.Length;
1629 if (g.Length != c)
1630 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(g));
1631
1632 for (i = 0; i < c; i++)
1633 SubtractFrom(f[i], g[i]);
1634 }
1635
1641 public static void SubtractFrom(uint[] f, uint[] g)
1642 {
1643 int i;
1644
1645 for (i = 0; i < n; i++)
1646 f[i] = (f[i] + q - g[i]) % q;
1647 }
1648
1654 public static void SubtractFrom(uint[][] f, uint[][] g)
1655 {
1656 int i, c = f.Length;
1657 if (g.Length != c)
1658 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(g));
1659
1660 for (i = 0; i < c; i++)
1661 SubtractFrom(f[i], g[i]);
1662 }
1663
1668 public static void Negate(uint[] f)
1669 {
1670 int i, c = f.Length;
1671
1672 for (i = 0; i < c; i++)
1673 f[i] = (q - f[i]) % q; // To avoid different CPU instructions to execute based on if bit is 0 or 1.
1674 }
1675
1680 public static void Negate(uint[][] f)
1681 {
1682 int i, c = f.Length;
1683
1684 for (i = 0; i < c; i++)
1685 Negate(f[i]);
1686 }
1687
1695 public static uint[] DotProductNTT(uint[][] v1, uint[][] v2)
1696 {
1697 int i, c = v1.Length;
1698
1699 if (v2.Length != c)
1700 throw new ArgumentException("Vectors must have the same number of polynomials.", nameof(v2));
1701
1702 uint[] Result = new uint[n];
1703
1704 for (i = 0; i < c; i++)
1705 MultiplyNTTsAndAdd(v1[i], v2[i], Result);
1706
1707 return Result;
1708 }
1709
1717 public static uint[][] ScalarProductNTT(uint[] c, uint[][] s)
1718 {
1719 if (c.Length != n)
1720 throw new ArgumentException("Polynomials must have " + n + " coefficients.", nameof(c));
1721
1722 int i, j, k = s.Length;
1723 uint[][] Result = new uint[k][];
1724 uint[] f;
1725 uint[] si;
1726
1727 for (i = 0; i < k; i++)
1728 {
1729 Result[i] = f = new uint[n];
1730 si = s[i];
1731
1732 if (si.Length != n)
1733 throw new ArgumentException("Polynomials must have " + n + " coefficients.", nameof(s));
1734
1735 for (j = 0; j < n; j++)
1736 f[j] = (uint)((long)c[j] * si[j] % q);
1737 }
1738
1739 return Result;
1740 }
1741
1747 public static void ShiftLeft(uint[] f, int d)
1748 {
1749 int i, c = f.Length;
1750
1751 for (i = 0; i < c; i++)
1752 f[i] = (uint)(((ulong)f[i] << d) % q);
1753 }
1754
1760 public static void ShiftLeft(uint[][] f, int d)
1761 {
1762 int i, c = f.Length;
1763
1764 for (i = 0; i < c; i++)
1765 ShiftLeft(f[i], d);
1766 }
1767
1778 public static int SimpleBitPack(short[] Values, int d, byte[] Output, int Index)
1779 {
1780 if (d < 1 || d > 15)
1781 throw new ArgumentOutOfRangeException(nameof(d), "d must be between 1 and 15.");
1782
1783 int i, c = Values.Length;
1784 int BitOffset = 0;
1785 int Index0 = Index;
1786
1787 for (i = 0; i < c; i++)
1788 {
1789 ushort Value = (ushort)Values[i];
1790
1791 Value &= ushortBitMask[d];
1792 Output[Index] |= (byte)(Value << BitOffset);
1793 BitOffset += d;
1794 if (BitOffset >= 8)
1795 {
1796 Index++;
1797 BitOffset -= 8;
1798
1799 if (BitOffset > 0)
1800 {
1801 Output[Index] = (byte)(Value >> (d - BitOffset));
1802
1803 if (BitOffset >= 8)
1804 {
1805 BitOffset -= 8;
1806 Index++;
1807
1808 if (BitOffset > 0)
1809 Output[Index] = (byte)(Value >> (d - BitOffset));
1810 }
1811 }
1812 }
1813 }
1814
1815 return Index - Index0;
1816 }
1817
1827 public static int SimpleBitUnpack(short[] Values, byte[] Input, int Index, int d)
1828 {
1829 if (d < 1 || d > 15)
1830 throw new ArgumentOutOfRangeException(nameof(d), "Bitlength of a+b must be between 1 and 15.");
1831
1832 int c = Values.Length;
1833 int BitOffset = 0;
1834 int Index0 = Index;
1835 int i, j;
1836 int Value;
1837
1838 for (i = 0; i < c; i++)
1839 {
1840 Value = Input[Index] >> BitOffset;
1841 BitOffset += d;
1842
1843 while (BitOffset >= 8)
1844 {
1845 Index++;
1846 BitOffset -= 8;
1847
1848 if (BitOffset > 0)
1849 {
1850 j = d - BitOffset;
1851 Value &= ushortBitMask[j];
1852 Value |= Input[Index] << j;
1853 }
1854 }
1855
1856 Values[i] = (short)(Value & ushortBitMask[d]);
1857 }
1858
1859 return Index - Index0;
1860 }
1861
1873 public static int BitPack(short[] Values, byte[] Output, int Index, uint a, uint b)
1874 {
1875 int d = BitLen(b + a);
1876 if (d < 1 || d > 15)
1877 throw new ArgumentOutOfRangeException(nameof(b), "d must be between 1 and 15.");
1878
1879 int c = Values.Length;
1880 int BitOffset = 0;
1881 int Index0 = Index;
1882 int Value;
1883 int i;
1884
1885 for (i = 0; i < c; i++)
1886 {
1887 Value = (int)(b - Values[i]);
1888 if (Value < 0)
1889 Value += q;
1890
1891 Value &= ushortBitMask[d];
1892 Output[Index] |= (byte)(Value << BitOffset);
1893 BitOffset += d;
1894
1895 while (BitOffset >= 8)
1896 {
1897 Index++;
1898 BitOffset -= 8;
1899
1900 if (BitOffset > 0)
1901 Output[Index] = (byte)(Value >> (d - BitOffset));
1902 }
1903 }
1904
1905 return Index - Index0;
1906 }
1907
1921 public static int BitPack(uint[] Values, byte[] Output, int Index, uint a, uint b, bool MakeSigned)
1922 {
1923 int d = BitLen(b + a);
1924 if (d < 1 || d > 30)
1925 throw new ArgumentOutOfRangeException(nameof(b), "d must be between 1 and 30.");
1926
1927 int c = Values.Length;
1928 int BitOffset = 0;
1929 int BitsLeft;
1930 int Index0 = Index;
1931 int Value;
1932 int i, j;
1933
1934 for (i = 0; i < c; i++)
1935 {
1936 Value = (int)Values[i];
1937
1938 if (MakeSigned)
1939 {
1940 Value %= q;
1941 if (Value < 0)
1942 Value += q;
1943
1944 if (q - Value < Value)
1945 Value -= q;
1946 }
1947
1948 Value = (int)(b - Value);
1949
1950 Value &= intBitMask[d];
1951 BitsLeft = d;
1952
1953 if (BitOffset > 0)
1954 {
1955 Output[Index] |= (byte)(Value << BitOffset);
1956 j = 8 - BitOffset;
1957
1958 if (BitsLeft >= j)
1959 {
1960 BitsLeft -= j;
1961 Value >>= j;
1962 BitOffset = 0;
1963 Index++;
1964 }
1965 else
1966 {
1967 BitOffset += BitsLeft;
1968 continue;
1969 }
1970 }
1971
1972 while (BitsLeft >= 8)
1973 {
1974 Output[Index++] = (byte)Value;
1975 Value >>= 8;
1976 BitsLeft -= 8;
1977 }
1978
1979 if (BitsLeft > 0)
1980 {
1981 Output[Index] = (byte)Value;
1982 BitOffset += BitsLeft;
1983 }
1984 }
1985
1986 if (BitOffset > 0)
1987 Index++;
1988
1989 return Index - Index0;
1990 }
1991
1997 private static int BitLen(uint i)
1998 {
1999 int d = 0;
2000
2001 while (i > 0)
2002 {
2003 i >>= 1;
2004 d++;
2005 }
2006
2007 return d;
2008 }
2009
2021 public static int BitUnpack(short[] Values, byte[] Input, int Index, uint a, uint b)
2022 {
2023 int d = BitLen(b + a);
2024 if (d < 1 || d > 15)
2025 throw new ArgumentOutOfRangeException(nameof(b), "Bitlength of a+b must be between 1 and 15.");
2026
2027 int c = Values.Length;
2028 int BitOffset = 0;
2029 int Index0 = Index;
2030 int i, j;
2031 int Value;
2032
2033 for (i = 0; i < c; i++)
2034 {
2035 Value = Input[Index] >> BitOffset;
2036 BitOffset += d;
2037
2038 while (BitOffset >= 8)
2039 {
2040 Index++;
2041 BitOffset -= 8;
2042
2043 if (BitOffset > 0)
2044 {
2045 j = d - BitOffset;
2046 Value &= ushortBitMask[j];
2047 Value |= Input[Index] << j;
2048 }
2049 }
2050
2051 Value &= ushortBitMask[d];
2052 Values[i] = (short)(b - Value);
2053 }
2054
2055 return Index - Index0;
2056 }
2057
2071 public static int BitUnpack(uint[] Values, byte[] Input, int Index, uint a, uint b, bool MakeUnsigned)
2072 {
2073 int d = BitLen(b + a);
2074 if (d < 1 || d > 30)
2075 throw new ArgumentOutOfRangeException(nameof(b), "Bitlength of a+b must be between 1 and 30.");
2076
2077 int c = Values.Length;
2078 int BitOffset = 0;
2079 int Index0 = Index;
2080 int i, j;
2081 int Value;
2082
2083 for (i = 0; i < c; i++)
2084 {
2085 Value = Input[Index] >> BitOffset;
2086 BitOffset += d;
2087
2088 while (BitOffset >= 8)
2089 {
2090 Index++;
2091 BitOffset -= 8;
2092
2093 if (BitOffset > 0)
2094 {
2095 j = d - BitOffset;
2096 Value &= intBitMask[j];
2097 Value |= Input[Index] << j;
2098 }
2099 }
2100
2101 Value &= intBitMask[d];
2102 j = Mod((int)(b - Value), q);
2103 if (MakeUnsigned)
2104 {
2105 if (j < 0)
2106 j += q;
2107 }
2108 else
2109 {
2110 if (q - j < j)
2111 j -= q;
2112 }
2113
2114 Values[i] = (uint)j;
2115 }
2116
2117 if (BitOffset > 0)
2118 Index++;
2119
2120 return Index - Index0;
2121 }
2122
2129 public static byte[] H(byte[] Data, int l)
2130 {
2131 return new SHAKE256(l << 3).ComputeVariable(Data);
2132 }
2133
2140 public static byte[] G(byte[] Data, int l)
2141 {
2142 return new SHAKE128(l << 3).ComputeVariable(Data);
2143 }
2144
2149 private static readonly uint[] nttTransformZeta = new uint[256]
2150 {
2151 0, 4808194, 3765607, 3761513, 5178923, 5496691, 5234739, 5178987,
2152 7778734, 3542485, 2682288, 2129892, 3764867, 7375178, 557458, 7159240,
2153 5010068, 4317364, 2663378, 6705802, 4855975, 7946292, 676590, 7044481,
2154 5152541, 1714295, 2453983, 1460718, 7737789, 4795319, 2815639, 2283733,
2155 3602218, 3182878, 2740543, 4793971, 5269599, 2101410, 3704823, 1159875,
2156 394148, 928749, 1095468, 4874037, 2071829, 4361428, 3241972, 2156050,
2157 3415069, 1759347, 7562881, 4805951, 3756790, 6444618, 6663429, 4430364,
2158 5483103, 3192354, 556856, 3870317, 2917338, 1853806, 3345963, 1858416,
2159 3073009, 1277625, 5744944, 3852015, 4183372, 5157610, 5258977, 8106357,
2160 2508980, 2028118, 1937570, 4564692, 2811291, 5396636, 7270901, 4158088,
2161 1528066, 482649, 1148858, 5418153, 7814814, 169688, 2462444, 5046034,
2162 4213992, 4892034, 1987814, 5183169, 1736313, 235407, 5130263, 3258457,
2163 5801164, 1787943, 5989328, 6125690, 3482206, 4197502, 7080401, 6018354,
2164 7062739, 2461387, 3035980, 621164, 3901472, 7153756, 2925816, 3374250,
2165 1356448, 5604662, 2683270, 5601629, 4912752, 2312838, 7727142, 7921254,
2166 348812, 8052569, 1011223, 6026202, 4561790, 6458164, 6143691, 1744507,
2167 ζ, 6444997, 5720892, 6924527, 2660408, 6600190, 8321269, 2772600,
2168 1182243, 87208, 636927, 4415111, 4423672, 6084020, 5095502, 4663471,
2169 8352605, 822541, 1009365, 5926272, 6400920, 1596822, 4423473, 4620952,
2170 6695264, 4969849, 2678278, 4611469, 4829411, 635956, 8129971, 5925040,
2171 4234153, 6607829, 2192938, 6653329, 2387513, 4768667, 8111961, 5199961,
2172 3747250, 2296099, 1239911, 4541938, 3195676, 2642980, 1254190, 8368000,
2173 2998219, 141835, 8291116, 2513018, 7025525, 613238, 7070156, 6161950,
2174 7921677, 6458423, 4040196, 4908348, 2039144, 6500539, 7561656, 6201452,
2175 6757063, 2105286, 6006015, 6346610, 586241, 7200804, 527981, 5637006,
2176 6903432, 1994046, 2491325, 6987258, 507927, 7192532, 7655613, 6545891,
2177 5346675, 8041997, 2647994, 3009748, 5767564, 4148469, 749577, 4357667,
2178 3980599, 2569011, 6764887, 1723229, 1665318, 2028038, 1163598, 5011144,
2179 3994671, 8368538, 7009900, 3020393, 3363542, 214880, 545376, 7609976,
2180 3105558, 7277073, 508145, 7826699, 860144, 3430436, 140244, 6866265,
2181 6195333, 3123762, 2358373, 6187330, 5365997, 6663603, 2926054, 7987710,
2182 8077412, 3531229, 4405932, 4606686, 1900052, 7598542, 1054478, 7648983
2183 };
2184
2195 public byte[] Sign_Internal(byte[] PrivateKey, byte[] Message, bool μPrecomputed,
2196 byte[] Seed, out int RejectionCount)
2197 {
2198 return this.Sign_Internal(ML_DSA_Keys.FromPrivateKey(PrivateKey), Message,
2199 μPrecomputed, Seed, out RejectionCount);
2200 }
2201
2212 public byte[] Sign_Internal(ML_DSA_Keys Keys, byte[] Message, bool μPrecomputed,
2213 byte[] Seed, out int RejectionCount)
2214 {
2215 if (Keys.PrivateKey is null ||
2216 !this.TryDecodePrivateKey(Keys.PrivateKey, out byte[] ρ, out byte[] K,
2217 out byte[] tr, out short[][] s1, out short[][] s2, out short[][] t0))
2218 {
2219 throw new ArgumentException("Invalid private key.", nameof(Keys));
2220 }
2221
2222 if (Seed is null)
2223 Seed = new byte[32];
2224 else if (Seed.Length != 32)
2225 throw new ArgumentException("Seed must be 32 bytes long.", nameof(Seed));
2226
2227 RejectionCount = 0;
2228
2229 uint[][] NTTs1 = NTT(s1);
2230 uint[][] NTTs2 = NTT(s2);
2231 uint[][] NTTt0 = NTT(t0);
2232
2233 uint[,][] Â = Keys.Â;
2234
2235 if (Â is null)
2236 Keys. =  = this.ExpandÂ(ρ);
2237 else if (Â.GetLength(0) != this.k || Â.GetLength(1) != this.l)
2238 throw new ArgumentException("Matrix  must be " + this.k + "x" + this.l + ".", nameof(Â));
2239
2240 byte[] μ;
2241 byte[] Bin;
2242 bool Clearμ;
2243
2244 if (μPrecomputed)
2245 {
2246 μ = Message;
2247 Clearμ = false;
2248 }
2249 else
2250 {
2251 int MessageLen = Message.Length;
2252 Bin = new byte[64 + MessageLen];
2253
2254 Buffer.BlockCopy(tr, 0, Bin, 0, 64);
2255 Buffer.BlockCopy(Message, 0, Bin, 64, MessageLen);
2256
2257 μ = H(Bin, 64);
2258 Clear(Bin);
2259 Clearμ = true;
2260 }
2261
2262 Bin = new byte[128];
2263 Buffer.BlockCopy(K, 0, Bin, 0, 32);
2264 Buffer.BlockCopy(Seed, 0, Bin, 32, 32);
2265 Buffer.BlockCopy(μ, 0, Bin, 64, 64);
2266
2267 byte[] ρ2 = H(Bin, 64);
2268 Clear(Bin);
2269
2270 uint κ = 0;
2271 byte[] h = null;
2272 byte[] cSeed = null;
2273 uint[][] z = null;
2274 bool Found = false;
2275 int IterationLimit = 10000;
2276
2277 while (!Found)
2278 {
2279 if (--IterationLimit <= 0 || κ > ushort.MaxValue)
2280 throw new InvalidOperationException("Unable to calculate signature.");
2281
2282 uint[][] y = this.ExpandMask(ρ2, (ushort)κ);
2283
2284 if (!(z is null))
2285 Clear(z);
2286
2287 z = Clone(y);
2288 NTT(y);
2289
2290 uint[][] w = new uint[this.k][];
2291 uint[] f;
2292 int i, j;
2293
2294 for (i = 0; i < this.k; i++)
2295 {
2296 w[i] = f = new uint[n];
2297
2298 for (j = 0; j < this.l; j++)
2299 MultiplyNTTsAndAdd(Â[i, j], y[j], f);
2300 }
2301
2302 InverseNTT(w);
2303
2304 short[][] w1 = this.HighBits(w);
2305
2306 // w1Encode, Algorithm 28, §7.2
2307
2308 int w1Len = BitLen((uint)((q - 1) / this.twoγ2 - 1));
2309
2310 Bin = new byte[64 + this.k * (w1Len << 5)];
2311 Buffer.BlockCopy(μ, 0, Bin, 0, 64);
2312
2313 int Pos = 64;
2314
2315 for (i = 0; i < this.k; i++)
2316 Pos += SimpleBitPack(w1[i], w1Len, Bin, Pos);
2317
2318 cSeed = H(Bin, this.λ >> 2);
2319 Clear(Bin);
2320
2321 short[] c = this.SampleInBall(cSeed);
2322 uint[] NTTc = NTT(c);
2323
2324 uint[][] cs1 = ScalarProductNTT(NTTc, NTTs1);
2325 InverseNTT(cs1);
2326
2327 uint[][] cs2 = ScalarProductNTT(NTTc, NTTs2);
2328 InverseNTT(cs2);
2329
2330 AddTo(z, cs1);
2331
2332 if (InfinityNorm(z) < this.γ1 - this.β)
2333 {
2334 SubtractFrom(w, cs2);
2335 int[][] r0 = this.LowBits(w);
2336
2337 if (InfinityNorm(r0) < this.γ2 - this.β)
2338 {
2339 uint[][] ct0 = ScalarProductNTT(NTTc, NTTt0);
2340 InverseNTT(ct0);
2341
2342 AddTo(w, ct0);
2343 Negate(ct0);
2344
2345 if (InfinityNorm(ct0) < this.γ2)
2346 Found = true;
2347 else
2348 RejectionCount++;
2349
2350 h = this.MakeAndEncodeHint(ct0, w); // Alters ct0
2351 if (h is null)
2352 {
2353 Found = false;
2354 RejectionCount++;
2355 }
2356
2357 Clear(ct0);
2358 }
2359 else
2360 RejectionCount++;
2361
2362 Clear(r0);
2363 }
2364 else
2365 RejectionCount++;
2366
2367 Clear(y);
2368 Clear(w);
2369 Clear(cs1);
2370 Clear(cs2);
2371
2372 κ += this.l;
2373 }
2374
2375 if (Clearμ)
2376 Clear(μ);
2377
2378 Clear(tr);
2379 Clear(ρ);
2380 Clear(K);
2381 Clear(NTTs1);
2382 Clear(NTTs2);
2383 Clear(NTTt0);
2384
2385 return this.EncodeSignature(cSeed, z, h);
2386 }
2387
2388 private static uint[][] Clone(uint[][] v)
2389 {
2390 int i, c = v.Length;
2391 uint[][] Result = new uint[c][];
2392
2393 for (i = 0; i < c; i++)
2394 Result[i] = (uint[])v[i].Clone();
2395
2396 return Result;
2397 }
2398
2404 private static uint InfinityNorm(uint[] f)
2405 {
2406 uint Result = 0;
2407 int i, c = f.Length;
2408 uint v, w;
2409
2410 for (i = 0; i < c; i++)
2411 {
2412 v = f[i];
2413
2414 w = q - v;
2415 if (w < v)
2416 v = w;
2417
2418 if (v > Result)
2419 Result = v;
2420 }
2421
2422 return Result;
2423 }
2424
2430 private static uint InfinityNorm(uint[][] f)
2431 {
2432 uint Result = 0;
2433 int i, c = f.Length;
2434 uint v;
2435
2436 for (i = 0; i < c; i++)
2437 {
2438 v = InfinityNorm(f[i]);
2439 if (v > Result)
2440 Result = v;
2441 }
2442
2443 return Result;
2444 }
2445
2451 private static int InfinityNorm(int[] f)
2452 {
2453 int Result = 0;
2454 int i, c = f.Length;
2455 int v, w;
2456
2457 for (i = 0; i < c; i++)
2458 {
2459 v = f[i];
2460
2461 if (v < 0)
2462 v = -v;
2463 else
2464 {
2465 w = q - v;
2466 if (w < v)
2467 v = w;
2468 }
2469
2470 if (v > Result)
2471 Result = v;
2472 }
2473
2474 return Result;
2475 }
2476
2482 private static int InfinityNorm(int[][] f)
2483 {
2484 int Result = 0;
2485 int i, c = f.Length;
2486 int v;
2487
2488 for (i = 0; i < c; i++)
2489 {
2490 v = InfinityNorm(f[i]);
2491 if (v > Result)
2492 Result = v;
2493 }
2494
2495 return Result;
2496 }
2497
2498 private uint[][] ExpandMask(byte[] ρ, ushort μ)
2499 {
2500 uint[][] y = new uint[this.l][];
2501 int c = BitLen(this.γ1 - 1) + 1;
2502 int r;
2503 byte[] v;
2504
2505 byte[] ρ1 = new byte[66];
2506 Buffer.BlockCopy(ρ, 0, ρ1, 0, 64);
2507
2508 for (r = 0; r < this.l; r++)
2509 {
2510 ρ1[64] = (byte)μ;
2511 ρ1[65] = (byte)(μ >> 8);
2512 μ++;
2513
2514 v = H(ρ1, c << 5);
2515
2516 y[r] = new uint[n];
2517 BitUnpack(y[r], v, 0, this.γ1 - 1, this.γ1, true);
2518 }
2519
2520 return y;
2521 }
2522
2529 private short[][] HighBits(uint[][] w)
2530 {
2531 this.Decompose(w, out _, out short[][] Result);
2532 return Result;
2533 }
2534
2541 private int[][] LowBits(uint[][] w)
2542 {
2543 this.Decompose(w, out int[][] Result, out _);
2544 return Result;
2545 }
2546
2554 private void Decompose(uint[][] w, out int[][] w0, out short[][] w1)
2555 {
2556 w0 = new int[this.k][]; // LowBits
2557 w1 = new short[this.k][]; // HighBits
2558
2559 int[] f0;
2560 short[] f1;
2561 uint[] f;
2562 int r0, r1;
2563 int r;
2564 int i, j;
2565
2566 for (i = 0; i < this.k; i++)
2567 {
2568 f = w[i];
2569 w0[i] = f0 = new int[n];
2570 w1[i] = f1 = new short[n];
2571
2572 for (j = 0; j < n; j++)
2573 {
2574 r = (int)f[j];
2575 r0 = r % this.twoγ2;
2576
2577 if (r0 > this.γ2)
2578 r0 -= this.twoγ2;
2579
2580 r1 = r - r0;
2581 if (r1 == q - 1)
2582 {
2583 r1 = 0;
2584 r0--;
2585 }
2586 else
2587 r1 /= this.twoγ2;
2588
2589 f0[j] = r0;
2590 f1[j] = (short)r1;
2591 }
2592 }
2593 }
2594
2595 private short[] SampleInBall(byte[] Seed)
2596 {
2597 short[] c = new short[n];
2599 Keccak1600.Context Context = HashFunction.Absorb(Seed);
2600 byte[] s = Context.Squeeze(8);
2601 int i, k;
2602 byte j;
2603 byte Bit;
2604
2605 for (i = n - this.τ, k = 0, Bit = 1; i < n; i++)
2606 {
2607 j = Context.Squeeze1();
2608 while (j > i)
2609 j = Context.Squeeze1();
2610
2611 c[i] = c[j];
2612 c[j] = (s[k] & Bit) == 0 ? (short)1 : (short)-1;
2613
2614 Bit <<= 1;
2615 if (Bit == 0)
2616 {
2617 k++;
2618 Bit = 1;
2619 }
2620 }
2621
2622 return c;
2623 }
2624
2633 private byte[] MakeAndEncodeHint(uint[][] z, uint[][] r)
2634 {
2635 // MakeHint, (Algorithm 39, §7.4)
2636
2637 short[][] r1 = this.HighBits(r);
2638
2639 AddTo(r, z);
2640
2641 short[][] v1 = this.HighBits(r);
2642
2643 // HintBitPack, (Algorithm 20, §7.1)
2644
2645 byte[] y = new byte[this.ω + this.k];
2646 byte Index = 0;
2647 int i, j;
2648 short[] f, g;
2649
2650 for (i = 0; i < this.k; i++)
2651 {
2652 f = r1[i];
2653 g = v1[i];
2654
2655 for (j = 0; j < n; j++)
2656 {
2657 if (f[j] != g[j])
2658 {
2659 if (Index >= this.ω)
2660 return null; // ???
2661
2662 y[Index++] = (byte)j;
2663 }
2664 }
2665
2666 y[this.ω + i] = Index;
2667 }
2668
2669 return y;
2670 }
2671
2680 private byte[] EncodeSignature(byte[] c, uint[][] z, byte[] h)
2681 {
2682 int l1 = c.Length;
2683 int l2 = this.l * ((1 + BitLen(this.γ1 - 1)) << 5);
2684 int l3 = h.Length;
2685 int Len = l1 + l2 + l3;
2686 int i;
2687 byte[] Result = new byte[Len];
2688
2689 Buffer.BlockCopy(c, 0, Result, 0, l1);
2690
2691 for (i = 0; i < this.l; i++)
2692 l1 += BitPack(z[i], Result, l1, this.γ1 - 1, this.γ1, true);
2693
2694 Buffer.BlockCopy(h, 0, Result, l1, l3);
2695
2696 return Result;
2697 }
2698
2707 public bool Verify_Internal(byte[] PublicKey, byte[] Message, bool μPrecomputed,
2708 byte[] Signature)
2709 {
2710 return this.Verify_Internal(ML_DSA_Keys.FromPublicKey(PublicKey), Message,
2711 μPrecomputed, Signature);
2712 }
2713
2722 public bool Verify_Internal(ML_DSA_Keys Keys, byte[] Message, bool μPrecomputed,
2723 byte[] Signature)
2724 {
2725 if (Keys.PublicKey is null ||
2726 !this.TryDecodePublicKey(Keys.PublicKey, out byte[] ρ, out short[][] t1))
2727 {
2728 return false;
2729 }
2730
2731 if (!this.TryDecodeSignature(Signature, out byte[] cSeed, out uint[][] z,
2732 out bool[][] h))
2733 {
2734 return false;
2735 }
2736
2737 if (InfinityNorm(z) >= this.γ1 - this.β)
2738 return false;
2739
2740 uint[,][] Â = Keys.Â;
2741
2742 if (Â is null)
2743 Keys. =  = this.ExpandÂ(ρ);
2744 else if (Â.GetLength(0) != this.k || Â.GetLength(1) != this.l)
2745 throw new ArgumentException("Matrix  must be " + this.k + "x" + this.l + ".", nameof(Â));
2746
2747 byte[] tr = H(Keys.PublicKey, 64);
2748 byte[] μ;
2749 byte[] Bin;
2750
2751 if (μPrecomputed)
2752 μ = Message;
2753 else
2754 {
2755 int MessageLen = Message.Length;
2756 Bin = new byte[64 + MessageLen];
2757
2758 Buffer.BlockCopy(tr, 0, Bin, 0, 64);
2759 Buffer.BlockCopy(Message, 0, Bin, 64, MessageLen);
2760
2761 μ = H(Bin, 64);
2762 }
2763
2764 short[] c = this.SampleInBall(cSeed);
2765
2766 NTT(z);
2767
2768 uint[] NTTc = NTT(c);
2769 uint[][] NTTt1 = NTT(t1);
2770
2771 ShiftLeft(NTTt1, d);
2772 uint[][] w = ScalarProductNTT(NTTc, NTTt1);
2773 uint[] f;
2774 int i, j;
2775
2776 Negate(w);
2777
2778 for (i = 0; i < this.k; i++)
2779 {
2780 f = w[i];
2781
2782 for (j = 0; j < this.l; j++)
2783 MultiplyNTTsAndAdd(Â[i, j], z[j], f);
2784 }
2785
2786 InverseNTT(w);
2787
2788 short[][] w1 = this.UseHint(h, w);
2789
2790 // w1Encode, Algorithm 28, §7.2
2791
2792 int w1Len = BitLen((uint)((q - 1) / this.twoγ2 - 1));
2793
2794 Bin = new byte[64 + this.k * (w1Len << 5)];
2795 Buffer.BlockCopy(μ, 0, Bin, 0, 64);
2796
2797 int Pos = 64;
2798
2799 for (i = 0; i < this.k; i++)
2800 Pos += SimpleBitPack(w1[i], w1Len, Bin, Pos);
2801
2802 int cLen = this.λ >> 2;
2803 byte[] cSeed2 = H(Bin, cLen);
2804
2805 for (i = 0; i < cLen; i++)
2806 {
2807 if (cSeed[i] != cSeed2[i])
2808 return false;
2809 }
2810
2811 return true;
2812 }
2813
2820 private short[][] UseHint(bool[][] h, uint[][] r)
2821 {
2822 short[][] Result = new short[this.k][];
2823 short[] f;
2824 int[] r0v2;
2825 short[] r1v2;
2826 bool[] h2;
2827 int i, j;
2828 int m = (q - 1) / this.twoγ2;
2829 int r0;
2830 short r1;
2831
2832 this.Decompose(r, out int[][] r0v, out short[][] r1v);
2833
2834 for (i = 0; i < this.k; i++)
2835 {
2836 Result[i] = f = new short[n];
2837 r0v2 = r0v[i];
2838 r1v2 = r1v[i];
2839 h2 = h[i];
2840
2841 for (j = 0; j < n; j++)
2842 {
2843 r0 = r0v2[j];
2844 r1 = r1v2[j];
2845
2846 if (h2[j])
2847 {
2848 if (r0 > 0)
2849 f[j] = (short)((r1 + 1) % m);
2850 else
2851 f[j] = (short)((r1 + m - 1) % m);
2852 }
2853 else
2854 f[j] = r1;
2855 }
2856 }
2857
2858 return Result;
2859 }
2860
2869 private bool TryDecodePublicKey(byte[] PublicKey, out byte[] ρ, out short[][] t1)
2870 {
2871 ρ = null;
2872 t1 = null;
2873
2874 if (PublicKey is null || PublicKey.Length != this.publicKeySize)
2875 return false;
2876
2877 ρ = new byte[32];
2878 Buffer.BlockCopy(PublicKey, 0, ρ, 0, 32);
2879
2880 int Pos = 32;
2881 short[] f;
2882
2883 t1 = new short[this.k][];
2884
2885 for (int i = 0; i < this.k; i++)
2886 {
2887 t1[i] = f = new short[n];
2888 Pos += SimpleBitUnpack(f, PublicKey, Pos, 10);
2889 }
2890
2891 return Pos == this.publicKeySize;
2892 }
2893
2894
2904 private bool TryDecodeSignature(byte[] Signature, out byte[] c, out uint[][] z,
2905 out bool[][] h)
2906 {
2907 c = null;
2908 z = null;
2909 h = null;
2910
2911 if (Signature is null || Signature.Length != this.signatureSize)
2912 return false;
2913
2914 int l1 = this.λ >> 2;
2915 c = new byte[l1];
2916
2917 Buffer.BlockCopy(Signature, 0, c, 0, l1);
2918
2919 uint[] f;
2920 int i;
2921
2922 z = new uint[this.l][];
2923
2924 for (i = 0; i < this.l; i++)
2925 {
2926 z[i] = f = new uint[n];
2927 l1 += BitUnpack(f, Signature, l1, this.γ1 - 1, this.γ1, true);
2928 }
2929
2930 int l3 = Signature.Length - l1;
2931 if (l3 != this.ω + this.k)
2932 return false;
2933
2934 h = this.HintBitUnpack(Signature, l1);
2935 if (h is null)
2936 return false;
2937
2938 return true;
2939 }
2940
2948 private bool[][] HintBitUnpack(byte[] Input, int Pos)
2949 {
2950 bool[][] h = new bool[this.k][];
2951 bool[] f;
2952 int Index = 0;
2953 bool First;
2954 int i, j;
2955 byte b, b1, b2;
2956
2957 for (i = 0, j = this.ω; i < this.k; i++, j++)
2958 {
2959 h[i] = f = new bool[n];
2960
2961 b = Input[j + Pos];
2962 if (b < Index || b > this.ω)
2963 return null;
2964
2965 First = true;
2966
2967 b2 = b1 = Input[Index + Pos];
2968
2969 while (Index < b)
2970 {
2971 if (First)
2972 First = false;
2973 else if (b2 >= (b1 = Input[Index + Pos]))
2974 return null;
2975
2976 f[b1] = true;
2977 b2 = b1;
2978 Index++;
2979 }
2980 }
2981
2982 while (Index < this.ω)
2983 {
2984 if (Input[Index++ + Pos] != 0)
2985 return null;
2986 }
2987
2988 return h;
2989 }
2990
2997 private static int Mod(int x, int y)
2998 {
2999 x %= y;
3000 if (x < 0)
3001 x += y;
3002 return x;
3003 }
3004 }
3005}
Contains methods for simple hash calculations.
Definition: Hashes.cs:57
static byte[] ComputeSHA384Hash(byte[] Data)
Computes the SHA-384 hash of a block of binary data.
Definition: Hashes.cs:523
static byte[] ComputeSHA512Hash(byte[] Data)
Computes the SHA-512 hash of a block of binary data.
Definition: Hashes.cs:577
static byte[] ComputeSHA256Hash(byte[] Data)
Computes the SHA-256 hash of a block of binary data.
Definition: Hashes.cs:469
Methods common to ML algorithms.
Definition: ML_Common.cs:10
static void Clear(byte[] Bin)
Clears a byte array.
Definition: ML_Common.cs:53
static readonly int[] intBitMask
Bit masks corresponding to mod 2^d arithmetic, where d is the index of the mask in the array.
Definition: ML_Common.cs:94
static readonly ushort[] ushortBitMask
Bit masks corresponding to mod 2^d arithmetic, where d is the index of the mask in the array.
Definition: ML_Common.cs:83
static byte[] CreateSeed()
Creates a random seed value.
Definition: ML_Common.cs:44
ML-DSA public and private keys, as defined in §6.1.
Definition: ML_DSA_Keys.cs:7
uint[,][] Â
Matrix of encryption keys, as defined in §6.1.
Definition: ML_DSA_Keys.cs:27
static ML_DSA_Keys FromPublicKey(byte[] PublicKey)
Creates a key object instance from a public key.
Definition: ML_DSA_Keys.cs:57
byte[] PublicKey
Encoded public key, as defined in §6.1.
Definition: ML_DSA_Keys.cs:35
static ML_DSA_Keys FromPrivateKey(byte[] PrivateKey)
Creates a key object instance from a private key.
Definition: ML_DSA_Keys.cs:67
byte[] PrivateKey
Encoded private key, as defined in §6.1.
Definition: ML_DSA_Keys.cs:40
Implements the ML-DSA algorithm for post-quantum cryptography, as defined in NIST FIPS 204: https://n...
Definition: ML_DSA.cs:12
bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature)
Verifies a digital signature using the ML-DSA algorithm. (Algorithm 3 ML-DSA.Verify() in §5....
Definition: ML_DSA.cs:291
byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, byte[] Seed, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:234
static byte[] G(byte[] Data, int l)
Hash function G, as defined in §3.7.
Definition: ML_DSA.cs:2140
bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature)
Verifies a digital signature using the ML-DSA algorithm. (Algorithm 3 ML-DSA.Verify() in §5....
Definition: ML_DSA.cs:304
static int BitUnpack(uint[] Values, byte[] Input, int Index, uint a, uint b, bool MakeUnsigned)
Decodes an array of integers between [-a,b] from a byte array, as defined by Algorithm 19 in §7....
Definition: ML_DSA.cs:2071
static void SubtractFrom(uint[] f, short[] g)
Subtracts g from f .
Definition: ML_DSA.cs:1605
static void SubtractFrom(uint[][] f, short[][] g)
Subtracts g from f .
Definition: ML_DSA.cs:1626
static void ShiftLeft(uint[][] f, int d)
Multiplies a vector of polynomials by 2^d
Definition: ML_DSA.cs:1760
bool Verify(byte[] PublicKey, Stream Message, byte[] Signature, byte[] Context, string HashAlgorithm)
Verifies a digital signature using the ML-DSA pre-hash algorithm. (Algorithm 5 HashML-DSA....
Definition: ML_DSA.cs:866
static void AddTo(uint[][] f, short[][] g)
Adds vector g to vector f .
Definition: ML_DSA.cs:1562
static void AddTo(uint[] f, short[] g)
Adds g to f .
Definition: ML_DSA.cs:1541
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:420
bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature, byte[] Context, string HashAlgorithm)
Verifies a digital signature using the ML-DSA pre-hash algorithm. (Algorithm 5 HashML-DSA....
Definition: ML_DSA.cs:802
static uint[] DotProductNTT(uint[][] v1, uint[][] v2)
Computes the dot product of two vectors of polynomials in 𝑇𝑞.
Definition: ML_DSA.cs:1695
static void NTT(uint[][] f)
Canonical extension of NTT(uint[]).
Definition: ML_DSA.cs:1354
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:181
byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:196
bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature, byte[] Context, string HashAlgorithm)
Verifies a digital signature using the ML-DSA pre-hash algorithm. (Algorithm 5 HashML-DSA....
Definition: ML_DSA.cs:819
static int SimpleBitPack(short[] Values, int d, byte[] Output, int Index)
Encodes an array of integers (mod 2^d) into a byte array, as defined by Algorithm 16 in §7....
Definition: ML_DSA.cs:1778
byte[] Sign_Internal(ML_DSA_Keys Keys, byte[] Message, bool μPrecomputed, byte[] Seed, out int RejectionCount)
Internal signature interface (Algorithm 7, §6.2)
Definition: ML_DSA.cs:2212
static int SimpleBitUnpack(short[] Values, byte[] Input, int Index, int d)
Decodes an array of integers (mod 2^d) from a byte array, as defined by Algorithm 18 in §7....
Definition: ML_DSA.cs:1827
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:213
byte[] Sign_Internal(byte[] PrivateKey, byte[] Message, bool μPrecomputed, byte[] Seed, out int RejectionCount)
Internal signature interface (Algorithm 7, §6.2)
Definition: ML_DSA.cs:2195
byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:402
bool Verify(ML_DSA_Keys Keys, Stream Message, byte[] Signature, byte[] Context, string HashAlgorithm)
Verifies a digital signature using the ML-DSA pre-hash algorithm. (Algorithm 5 HashML-DSA....
Definition: ML_DSA.cs:883
static void InverseNTT(uint[] f)
Computes the NTT^-1 representation f of the given polynomial f̂ ∈ 𝑇𝑞. (Algorithm 42 in §7....
Definition: ML_DSA.cs:1410
byte[] Sign(ML_DSA_Keys Keys, Stream Message, byte[] Context, byte[] Seed, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:577
static readonly ML_DSA ML_DSA_44
Model parameters for a required RBG strength 128 (cryptographic security strength),...
Definition: ML_DSA.cs:17
static void AddTo(uint[][] f, uint[][] g)
Adds vector g to vector f .
Definition: ML_DSA.cs:1590
bool Verify_Internal(byte[] PublicKey, byte[] Message, bool μPrecomputed, byte[] Signature)
Internal signature verification interface (Algorithm 8, §6.3)
Definition: ML_DSA.cs:2707
static void ShiftLeft(uint[] f, int d)
Multiplies a polynomial by 2^d
Definition: ML_DSA.cs:1747
static void NTT(uint[] f)
Computes the NTT representation f̂ of the given polynomial f ∈ 𝑅𝑞. (Algorithm 41 in §7....
Definition: ML_DSA.cs:1322
static void Negate(uint[][] f)
Negates an array of polynomials in 𝑅𝑞.
Definition: ML_DSA.cs:1680
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:385
ML_DSA(int τ, int λ, uint γ1, uint γ2, byte k, byte l, byte η, int ω, int PrivateKeySize, int PublicKeySize, int SignatureSize)
Implements the ML-DSA algorithm for post-quantum cryptography, as defined in NIST FIPS 204: https://n...
Definition: ML_DSA.cs:94
byte[] Sign(byte[] PrivateKey, Stream Message, byte[] Context, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:516
bool Verify_Internal(ML_DSA_Keys Keys, byte[] Message, bool μPrecomputed, byte[] Signature)
Internal signature verification interface (Algorithm 8, §6.3)
Definition: ML_DSA.cs:2722
bool Verify(ML_DSA_Keys Keys, byte[] Message, byte[] Signature, byte[] Context)
Verifies a digital signature using the ML-DSA algorithm. (Algorithm 3 ML-DSA.Verify() in §5....
Definition: ML_DSA.cs:333
short[] RejBoundedPoly(byte[] Seed)
The algorithm RejBoundedPoly (Algorithm 31, §7.3) converts a seed together with an index into a polyn...
Definition: ML_DSA.cs:1270
static void InverseNTT(uint[][] f)
Canonical extension of InverseNTT(uint[]).
Definition: ML_DSA.cs:1450
ML_DSA_Keys KeyGen(bool ReturnSeed)
Generates a public and private key. (Algorithm 1 ML-DSA.KeyGen() in §5.1)
Definition: ML_DSA.cs:145
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, byte[] Seed, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:252
override int PrivateKeyLength
Length of the private key.
Definition: ML_DSA.cs:125
static void SubtractFrom(uint[][] f, uint[][] g)
Subtracts g from f .
Definition: ML_DSA.cs:1654
static void SubtractFrom(uint[] f, uint[] g)
Subtracts g from f .
Definition: ML_DSA.cs:1641
static void AddTo(int[] f, short[] g)
Adds g to f .
Definition: ML_DSA.cs:1513
static uint[] NTT(short[] f)
Canonical extension of NTT(uint[]).
Definition: ML_DSA.cs:1367
static int BitPack(short[] Values, byte[] Output, int Index, uint a, uint b)
Encodes an array of integers between [-a,b] into a byte array, as defined by Algorithm 17 in §7....
Definition: ML_DSA.cs:1873
byte[] Sign(ML_DSA_Keys Keys, byte[] Message, byte[] Context, byte[] Seed, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:463
ML_DSA_Keys KeyGen()
Generates a public and private key. (Algorithm 1 ML-DSA.KeyGen() in §5.1)
Definition: ML_DSA.cs:133
static readonly ML_DSA ML_DSA_65
Model parameters for a required RBG strength 192 (cryptographic security strength),...
Definition: ML_DSA.cs:23
static int BitPack(uint[] Values, byte[] Output, int Index, uint a, uint b, bool MakeSigned)
Encodes an array of integers between [-a,b] into a byte array, as defined by Algorithm 17 in §7....
Definition: ML_DSA.cs:1921
bool Verify(byte[] PublicKey, byte[] Message, byte[] Signature, byte[] Context)
Verifies a digital signature using the ML-DSA algorithm. (Algorithm 3 ML-DSA.Verify() in §5....
Definition: ML_DSA.cs:318
static ML_DSA GetModel(string Name)
Gets a model by name, as defined in §8.
Definition: ML_DSA.cs:37
static void AddTo(int[][] f, short[][] g)
Adds vector g to vector f .
Definition: ML_DSA.cs:1526
byte[] Sign(byte[] PrivateKey, byte[] Message, byte[] Context, byte[] Seed, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:444
static void MultiplyNTTsAndAdd(uint[] f, short[] g, uint[] Result)
Computes the product (in the ring 𝑇𝑞) of two NTT representations (Algorithm 45 in §7....
Definition: ML_DSA.cs:1489
override int PublicKeyLength
Length of the public key.
Definition: ML_DSA.cs:120
static int BitUnpack(short[] Values, byte[] Input, int Index, uint a, uint b)
Decodes an array of integers between [-a,b] from a byte array, as defined by Algorithm 19 in §7....
Definition: ML_DSA.cs:2021
static void MultiplyNTTsAndAdd(uint[] f, uint[] g, uint[] Result)
Computes the product (in the ring 𝑇𝑞) of two NTT representations (Algorithm 45 in §7....
Definition: ML_DSA.cs:1468
byte[] Sign(byte[] PrivateKey, Stream Message, byte[] Context, byte[] Seed, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:558
ML_DSA_Keys KeyGen_Internal(byte[] ξ)
Generates a public and private key. (Algorithm 6 ML-DSA.KeyGen_Internal() in §6.1)
Definition: ML_DSA.cs:928
byte[] Sign(byte[] PrivateKey, byte[] Message, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:368
byte[] Sign(ML_DSA_Keys Keys, Stream Message, byte[] Context, string HashAlgorithm, out int RejectionCount)
Signs a message using the ML-DSA pre-hash algorithm. (Algorithm 4 HashML-DSA.Sign() in §5....
Definition: ML_DSA.cs:534
static uint[][] ScalarProductNTT(uint[] c, uint[][] s)
Computes the scalar product of a polynomial in 𝑇𝑞 with a vector of polynomials in 𝑇𝑞.
Definition: ML_DSA.cs:1717
ML_DSA_Keys KeyGen_Internal(byte[] ξ, bool ReturnSeed)
Generates a public and private key. (Algorithm 6 ML-DSA.KeyGen_Internal() in §6.1)
Definition: ML_DSA.cs:941
static void Negate(uint[] f)
Negates a polynomial in 𝑅𝑞.
Definition: ML_DSA.cs:1668
static readonly ML_DSA ML_DSA_87
Model parameters for a required RBG strength 256 (cryptographic security strength),...
Definition: ML_DSA.cs:29
static void AddTo(uint[] f, uint[] g)
Adds g to f .
Definition: ML_DSA.cs:1577
byte[] Sign(byte[] PrivateKey, byte[] Message, out int RejectionCount)
Signs a message using the ML-DSA algorithm. (Algorithm 2 ML-DSA.Sign() in §5.2)
Definition: ML_DSA.cs:166
static byte[] H(byte[] Data, int l)
Hash function H, as defined in §3.7.
Definition: ML_DSA.cs:2129
static uint[][] NTT(short[][] f)
Canonical extension of NTT(short[]).
Definition: ML_DSA.cs:1394
Hash digest computation context.
Definition: Keccak1600.cs:558
byte[] Squeeze(int NrBytes)
Calculates another NrBytes number of bytes of the digest.
Definition: Keccak1600.cs:581
byte Squeeze1()
Calculates another byte of the digest.
Definition: Keccak1600.cs:612
byte[] ComputeVariable(byte[] N)
Computes the SPONGE function, as defined in section 4 of NIST FIPS 202.
Definition: Keccak1600.cs:408
Implements the SHA3-224 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_224.cs:9
Implements the SHA3-256 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_256.cs:9
Implements the SHA3-384 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_384.cs:9
Implements the SHA3-512 hash function, as defined in section 6.1 in the NIST FIPS 202: https://nvlpub...
Definition: SHA3_512.cs:9
Implements the SHA3 SHAKE128 extendable-output functions, as defined in section 6....
Definition: SHAKE128.cs:9
Implements the SHA3 SHAKE256 extendable-output functions, as defined in section 6....
Definition: SHAKE256.cs:9
Definition: ImplTypes.g.cs:58
delegate byte[] HashFunctionStream(Stream Data)
Delegate to hash function.
delegate byte[] HashFunctionArray(byte[] Data)
Delegate to hash function.
HashFunction
Hash method enumeration.
Definition: Hashes.cs:26