@@ -56,6 +56,26 @@ use rand_core::CryptoRngCore;
56
56
#[ cfg( feature = "zeroize" ) ]
57
57
use zeroize:: { Zeroize , ZeroizeOnDrop } ;
58
58
59
+ #[ cfg( feature = "pkcs8" ) ]
60
+ use {
61
+ const_oid:: db:: fips204,
62
+ pkcs8:: {
63
+ der:: { self , AnyRef } ,
64
+ spki:: {
65
+ self , AlgorithmIdentifier , AssociatedAlgorithmIdentifier , SignatureAlgorithmIdentifier ,
66
+ SubjectPublicKeyInfoRef ,
67
+ } ,
68
+ AlgorithmIdentifierRef , PrivateKeyInfoRef ,
69
+ } ,
70
+ } ;
71
+
72
+ #[ cfg( all( feature = "alloc" , feature = "pkcs8" ) ) ]
73
+ use pkcs8:: {
74
+ der:: asn1:: { BitString , BitStringRef } ,
75
+ spki:: { SignatureBitStringEncoding , SubjectPublicKeyInfo } ,
76
+ EncodePublicKey ,
77
+ } ;
78
+
59
79
use crate :: algebra:: { AlgebraExt , Elem , NttMatrix , NttVector , Truncate , Vector } ;
60
80
use crate :: crypto:: H ;
61
81
use crate :: hint:: Hint ;
@@ -124,6 +144,24 @@ impl<P: MlDsaParams> signature::SignatureEncoding for Signature<P> {
124
144
type Repr = EncodedSignature < P > ;
125
145
}
126
146
147
+ #[ cfg( feature = "alloc" ) ]
148
+ impl < P : MlDsaParams > SignatureBitStringEncoding for Signature < P > {
149
+ fn to_bitstring ( & self ) -> der:: Result < BitString > {
150
+ BitString :: new ( 0 , self . encode ( ) . to_vec ( ) )
151
+ }
152
+ }
153
+
154
+ #[ cfg( feature = "pkcs8" ) ]
155
+ impl < P > AssociatedAlgorithmIdentifier for Signature < P >
156
+ where
157
+ P : MlDsaParams ,
158
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
159
+ {
160
+ type Params = AnyRef < ' static > ;
161
+
162
+ const ALGORITHM_IDENTIFIER : AlgorithmIdentifierRef < ' static > = P :: ALGORITHM_IDENTIFIER ;
163
+ }
164
+
127
165
// This method takes a slice of slices so that we can accommodate the varying calculations (direct
128
166
// for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
129
167
// having to allocate memory for components.
@@ -156,6 +194,46 @@ impl<P: MlDsaParams> signature::KeypairRef for KeyPair<P> {
156
194
type VerifyingKey = VerifyingKey < P > ;
157
195
}
158
196
197
+ #[ cfg( feature = "pkcs8" ) ]
198
+ impl < P > TryFrom < PrivateKeyInfoRef < ' _ > > for KeyPair < P >
199
+ where
200
+ P : MlDsaParams ,
201
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
202
+ {
203
+ type Error = pkcs8:: Error ;
204
+
205
+ fn try_from ( private_key_info : pkcs8:: PrivateKeyInfoRef < ' _ > ) -> pkcs8:: Result < Self > {
206
+ match private_key_info. algorithm {
207
+ alg if alg == P :: ALGORITHM_IDENTIFIER => { }
208
+ other => return Err ( spki:: Error :: OidUnknown { oid : other. oid } . into ( ) ) ,
209
+ } ;
210
+
211
+ let seed = Array :: try_from ( private_key_info. private_key . as_bytes ( ) )
212
+ . map_err ( |_| pkcs8:: Error :: KeyMalformed ) ?;
213
+ Ok ( P :: key_gen_internal ( & seed) )
214
+ }
215
+ }
216
+
217
+ /// The `Signer` implementation for `KeyPair` uses the optional deterministic variant of ML-DSA, and
218
+ /// only supports signing with an empty context string.
219
+ impl < P : MlDsaParams > signature:: Signer < Signature < P > > for KeyPair < P > {
220
+ fn try_sign ( & self , msg : & [ u8 ] ) -> Result < Signature < P > , Error > {
221
+ self . signing_key . sign_deterministic ( msg, & [ ] )
222
+ }
223
+ }
224
+
225
+ #[ cfg( feature = "pkcs8" ) ]
226
+ impl < P > SignatureAlgorithmIdentifier for KeyPair < P >
227
+ where
228
+ P : MlDsaParams ,
229
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
230
+ {
231
+ type Params = AnyRef < ' static > ;
232
+
233
+ const SIGNATURE_ALGORITHM_IDENTIFIER : AlgorithmIdentifier < Self :: Params > =
234
+ Signature :: < P > :: ALGORITHM_IDENTIFIER ;
235
+ }
236
+
159
237
/// An ML-DSA signing key
160
238
#[ derive( Clone , PartialEq ) ]
161
239
pub struct SigningKey < P : MlDsaParams > {
@@ -384,8 +462,35 @@ impl<P: MlDsaParams> signature::RandomizedSigner<Signature<P>> for SigningKey<P>
384
462
}
385
463
}
386
464
465
+ #[ cfg( feature = "pkcs8" ) ]
466
+ impl < P > SignatureAlgorithmIdentifier for SigningKey < P >
467
+ where
468
+ P : MlDsaParams ,
469
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
470
+ {
471
+ type Params = AnyRef < ' static > ;
472
+
473
+ const SIGNATURE_ALGORITHM_IDENTIFIER : AlgorithmIdentifier < Self :: Params > =
474
+ Signature :: < P > :: ALGORITHM_IDENTIFIER ;
475
+ }
476
+
477
+ #[ cfg( feature = "pkcs8" ) ]
478
+ impl < P > TryFrom < PrivateKeyInfoRef < ' _ > > for SigningKey < P >
479
+ where
480
+ P : MlDsaParams ,
481
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
482
+ {
483
+ type Error = pkcs8:: Error ;
484
+
485
+ fn try_from ( private_key_info : pkcs8:: PrivateKeyInfoRef < ' _ > ) -> pkcs8:: Result < Self > {
486
+ let keypair = KeyPair :: try_from ( private_key_info) ?;
487
+
488
+ Ok ( keypair. signing_key )
489
+ }
490
+ }
491
+
387
492
/// An ML-DSA verification key
388
- #[ derive( Clone , PartialEq ) ]
493
+ #[ derive( Clone , Debug , PartialEq ) ]
389
494
pub struct VerifyingKey < P : ParameterSet > {
390
495
rho : B32 ,
391
496
t1 : Vector < P :: K > ,
@@ -488,6 +593,61 @@ impl<P: MlDsaParams> signature::Verifier<Signature<P>> for VerifyingKey<P> {
488
593
}
489
594
}
490
595
596
+ #[ cfg( feature = "pkcs8" ) ]
597
+ impl < P > SignatureAlgorithmIdentifier for VerifyingKey < P >
598
+ where
599
+ P : MlDsaParams ,
600
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
601
+ {
602
+ type Params = AnyRef < ' static > ;
603
+
604
+ const SIGNATURE_ALGORITHM_IDENTIFIER : AlgorithmIdentifier < Self :: Params > =
605
+ Signature :: < P > :: ALGORITHM_IDENTIFIER ;
606
+ }
607
+
608
+ #[ cfg( feature = "alloc" ) ]
609
+ impl < P > EncodePublicKey for VerifyingKey < P >
610
+ where
611
+ P : MlDsaParams ,
612
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
613
+ {
614
+ fn to_public_key_der ( & self ) -> spki:: Result < der:: Document > {
615
+ let public_key = self . encode ( ) ;
616
+ let subject_public_key = BitStringRef :: new ( 0 , & public_key) ?;
617
+
618
+ SubjectPublicKeyInfo {
619
+ algorithm : P :: ALGORITHM_IDENTIFIER ,
620
+ subject_public_key,
621
+ }
622
+ . try_into ( )
623
+ }
624
+ }
625
+
626
+ #[ cfg( feature = "pkcs8" ) ]
627
+ impl < P > TryFrom < SubjectPublicKeyInfoRef < ' _ > > for VerifyingKey < P >
628
+ where
629
+ P : MlDsaParams ,
630
+ P : AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
631
+ {
632
+ type Error = spki:: Error ;
633
+
634
+ fn try_from ( spki : SubjectPublicKeyInfoRef < ' _ > ) -> spki:: Result < Self > {
635
+ match spki. algorithm {
636
+ alg if alg == P :: ALGORITHM_IDENTIFIER => { }
637
+ other => return Err ( spki:: Error :: OidUnknown { oid : other. oid } ) ,
638
+ } ;
639
+
640
+ Ok ( Self :: decode (
641
+ & EncodedVerifyingKey :: < P > :: try_from (
642
+ spki. subject_public_key
643
+ . as_bytes ( )
644
+ . ok_or_else ( || der:: Tag :: BitString . value_error ( ) ) ?,
645
+ )
646
+ . map_err ( |_| pkcs8:: Error :: KeyMalformed ) ?,
647
+ ) )
648
+ }
649
+ }
650
+
491
651
/// `MlDsa44` is the parameter set for security category 2.
492
652
#[ derive( Default , Clone , Debug , PartialEq ) ]
493
653
pub struct MlDsa44 ;
@@ -505,6 +665,16 @@ impl ParameterSet for MlDsa44 {
505
665
const TAU : usize = 39 ;
506
666
}
507
667
668
+ #[ cfg( feature = "pkcs8" ) ]
669
+ impl AssociatedAlgorithmIdentifier for MlDsa44 {
670
+ type Params = AnyRef < ' static > ;
671
+
672
+ const ALGORITHM_IDENTIFIER : AlgorithmIdentifierRef < ' static > = AlgorithmIdentifierRef {
673
+ oid : fips204:: ID_ML_DSA_44 ,
674
+ parameters : None ,
675
+ } ;
676
+ }
677
+
508
678
/// `MlDsa65` is the parameter set for security category 3.
509
679
#[ derive( Default , Clone , Debug , PartialEq ) ]
510
680
pub struct MlDsa65 ;
@@ -522,6 +692,16 @@ impl ParameterSet for MlDsa65 {
522
692
const TAU : usize = 49 ;
523
693
}
524
694
695
+ #[ cfg( feature = "pkcs8" ) ]
696
+ impl AssociatedAlgorithmIdentifier for MlDsa65 {
697
+ type Params = AnyRef < ' static > ;
698
+
699
+ const ALGORITHM_IDENTIFIER : AlgorithmIdentifierRef < ' static > = AlgorithmIdentifierRef {
700
+ oid : fips204:: ID_ML_DSA_65 ,
701
+ parameters : None ,
702
+ } ;
703
+ }
704
+
525
705
/// `MlKem87` is the parameter set for security category 5.
526
706
#[ derive( Default , Clone , Debug , PartialEq ) ]
527
707
pub struct MlDsa87 ;
@@ -539,6 +719,16 @@ impl ParameterSet for MlDsa87 {
539
719
const TAU : usize = 60 ;
540
720
}
541
721
722
+ #[ cfg( feature = "pkcs8" ) ]
723
+ impl AssociatedAlgorithmIdentifier for MlDsa87 {
724
+ type Params = AnyRef < ' static > ;
725
+
726
+ const ALGORITHM_IDENTIFIER : AlgorithmIdentifierRef < ' static > = AlgorithmIdentifierRef {
727
+ oid : fips204:: ID_ML_DSA_87 ,
728
+ parameters : None ,
729
+ } ;
730
+ }
731
+
542
732
/// A parameter set that knows how to generate key pairs
543
733
pub trait KeyGen : MlDsaParams {
544
734
/// The type that is returned by key generation
0 commit comments