@@ -56,6 +56,26 @@ use rand_core::CryptoRngCore;
5656#[ cfg( feature = "zeroize" ) ]
5757use zeroize:: { Zeroize , ZeroizeOnDrop } ;
5858
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+
5979use crate :: algebra:: { AlgebraExt , Elem , NttMatrix , NttVector , Truncate , Vector } ;
6080use crate :: crypto:: H ;
6181use crate :: hint:: Hint ;
@@ -124,6 +144,24 @@ impl<P: MlDsaParams> signature::SignatureEncoding for Signature<P> {
124144 type Repr = EncodedSignature < P > ;
125145}
126146
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+
127165// This method takes a slice of slices so that we can accommodate the varying calculations (direct
128166// for test vectors, 0... for sign/sign_deterministic, 1... for the pre-hashed version) without
129167// having to allocate memory for components.
@@ -156,6 +194,46 @@ impl<P: MlDsaParams> signature::KeypairRef for KeyPair<P> {
156194 type VerifyingKey = VerifyingKey < P > ;
157195}
158196
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+
159237/// An ML-DSA signing key
160238#[ derive( Clone , PartialEq ) ]
161239pub struct SigningKey < P : MlDsaParams > {
@@ -384,8 +462,35 @@ impl<P: MlDsaParams> signature::RandomizedSigner<Signature<P>> for SigningKey<P>
384462 }
385463}
386464
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+
387492/// An ML-DSA verification key
388- #[ derive( Clone , PartialEq ) ]
493+ #[ derive( Clone , Debug , PartialEq ) ]
389494pub struct VerifyingKey < P : ParameterSet > {
390495 rho : B32 ,
391496 t1 : Vector < P :: K > ,
@@ -488,6 +593,61 @@ impl<P: MlDsaParams> signature::Verifier<Signature<P>> for VerifyingKey<P> {
488593 }
489594}
490595
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+
491651/// `MlDsa44` is the parameter set for security category 2.
492652#[ derive( Default , Clone , Debug , PartialEq ) ]
493653pub struct MlDsa44 ;
@@ -505,6 +665,16 @@ impl ParameterSet for MlDsa44 {
505665 const TAU : usize = 39 ;
506666}
507667
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+
508678/// `MlDsa65` is the parameter set for security category 3.
509679#[ derive( Default , Clone , Debug , PartialEq ) ]
510680pub struct MlDsa65 ;
@@ -522,6 +692,16 @@ impl ParameterSet for MlDsa65 {
522692 const TAU : usize = 49 ;
523693}
524694
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+
525705/// `MlKem87` is the parameter set for security category 5.
526706#[ derive( Default , Clone , Debug , PartialEq ) ]
527707pub struct MlDsa87 ;
@@ -539,6 +719,16 @@ impl ParameterSet for MlDsa87 {
539719 const TAU : usize = 60 ;
540720}
541721
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+
542732/// A parameter set that knows how to generate key pairs
543733pub trait KeyGen : MlDsaParams {
544734 /// The type that is returned by key generation
0 commit comments