@@ -19,6 +19,17 @@ pub use ::kem::{Decapsulate, Encapsulate};
1919/// A shared key resulting from an ML-KEM transaction
2020pub ( crate ) type SharedKey = B32 ;
2121
22+ #[ cfg( all( feature = "pkcs8" , feature = "alloc" ) ) ]
23+ use pkcs8:: der:: { Encode , asn1:: BitStringRef } ;
24+ #[ cfg( feature = "pkcs8" ) ]
25+ use {
26+ hybrid_array:: Array ,
27+ pkcs8:: {
28+ der:: { AnyRef , asn1:: OctetStringRef } ,
29+ spki:: AssociatedAlgorithmIdentifier ,
30+ } ,
31+ } ;
32+
2233/// A `DecapsulationKey` provides the ability to generate a new key pair, and decapsulate an
2334/// encapsulated shared key.
2435#[ derive( Clone , Debug ) ]
5768#[ cfg( feature = "zeroize" ) ]
5869impl < P > ZeroizeOnDrop for DecapsulationKey < P > where P : KemParams { }
5970
71+ impl < P > From < Seed > for DecapsulationKey < P >
72+ where
73+ P : KemParams ,
74+ {
75+ fn from ( seed : Seed ) -> Self {
76+ Self :: from_seed ( seed)
77+ }
78+ }
79+
6080impl < P > EncodedSizeUser for DecapsulationKey < P >
6181where
6282 P : KemParams ,
@@ -270,6 +290,144 @@ where
270290 }
271291}
272292
293+ /// The serialization of the private key is a choice between three different formats
294+ /// [according to PKCS#8](https://lamps-wg.github.io/kyber-certificates/draft-ietf-lamps-kyber-certificates.html#name-private-key-format).
295+ ///
296+ /// “For ML-KEM private keys, the privateKey field in `OneAsymmetricKey`
297+ /// contains one of the following DER-encoded `CHOICE` structures.
298+ /// The seed format is a fixed 64-byte `OCTET STRING` (66 bytes total
299+ /// with the 0x8040 tag and length) for all security levels,
300+ /// while the expandedKey and both formats vary in size by security level”
301+ #[ cfg( feature = "pkcs8" ) ]
302+ #[ derive( Clone , Debug , pkcs8:: der:: Choice ) ]
303+ pub enum PrivateKeyChoice < ' o > {
304+ /// FIPS 203 format for an ML-KEM private key: a 64-octet seed
305+ #[ asn1( tag_mode = "IMPLICIT" , context_specific = "0" ) ]
306+ Seed ( OctetStringRef < ' o > ) ,
307+ }
308+
309+ #[ cfg( feature = "pkcs8" ) ]
310+ impl < P > AssociatedAlgorithmIdentifier for EncapsulationKey < P >
311+ where
312+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
313+ {
314+ type Params = P :: Params ;
315+
316+ const ALGORITHM_IDENTIFIER : pkcs8:: spki:: AlgorithmIdentifier < Self :: Params > =
317+ P :: ALGORITHM_IDENTIFIER ;
318+ }
319+
320+ #[ cfg( all( feature = "pkcs8" , feature = "alloc" ) ) ]
321+ impl < P > pkcs8:: EncodePublicKey for EncapsulationKey < P >
322+ where
323+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
324+ {
325+ /// Serialize the given `EncapsulationKey` into DER format.
326+ /// Returns a `Document` which wraps the DER document in case of success.
327+ fn to_public_key_der ( & self ) -> pkcs8:: spki:: Result < pkcs8:: Document > {
328+ let public_key = self . as_bytes ( ) ;
329+ let subject_public_key = BitStringRef :: new ( 0 , & public_key) ?;
330+
331+ pkcs8:: SubjectPublicKeyInfo {
332+ algorithm : P :: ALGORITHM_IDENTIFIER ,
333+ subject_public_key,
334+ }
335+ . try_into ( )
336+ }
337+ }
338+
339+ #[ cfg( feature = "pkcs8" ) ]
340+ impl < P > TryFrom < pkcs8:: SubjectPublicKeyInfoRef < ' _ > > for EncapsulationKey < P >
341+ where
342+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
343+ {
344+ type Error = pkcs8:: spki:: Error ;
345+
346+ /// Deserialize the encapsulation key from DER format found in `spki.subject_public_key`.
347+ /// Returns an `EncapsulationKey` containing `ek_{pke}` and `h` in case of success.
348+ fn try_from ( spki : pkcs8:: SubjectPublicKeyInfoRef < ' _ > ) -> Result < Self , Self :: Error > {
349+ if spki. algorithm . oid != P :: ALGORITHM_IDENTIFIER . oid {
350+ return Err ( pkcs8:: spki:: Error :: OidUnknown {
351+ oid : P :: ALGORITHM_IDENTIFIER . oid ,
352+ } ) ;
353+ }
354+
355+ let bitstring_of_encapsulation_key = spki. subject_public_key ;
356+ let enc_key = match bitstring_of_encapsulation_key. as_bytes ( ) {
357+ Some ( bytes) => {
358+ let arr: Array < u8 , EncapsulationKeySize < P > > = match bytes. try_into ( ) {
359+ Ok ( array) => array,
360+ Err ( _) => return Err ( pkcs8:: spki:: Error :: KeyMalformed ) ,
361+ } ;
362+ EncryptionKey :: from_bytes ( & arr)
363+ }
364+ None => return Err ( pkcs8:: spki:: Error :: KeyMalformed ) ,
365+ } ;
366+
367+ Ok ( Self :: new ( enc_key) )
368+ }
369+ }
370+
371+ #[ cfg( feature = "pkcs8" ) ]
372+ impl < P > AssociatedAlgorithmIdentifier for DecapsulationKey < P >
373+ where
374+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
375+ {
376+ type Params = P :: Params ;
377+
378+ const ALGORITHM_IDENTIFIER : pkcs8:: spki:: AlgorithmIdentifier < Self :: Params > =
379+ P :: ALGORITHM_IDENTIFIER ;
380+ }
381+
382+ #[ cfg( all( feature = "pkcs8" , feature = "alloc" ) ) ]
383+ impl < P > pkcs8:: EncodePrivateKey for DecapsulationKey < P >
384+ where
385+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
386+ {
387+ /// Serialize the given `DecapsulationKey` into DER format.
388+ /// Returns a `SecretDocument` which wraps the DER document in case of success.
389+ fn to_pkcs8_der ( & self ) -> pkcs8:: Result < pkcs8:: SecretDocument > {
390+ let decaps_key_bytes = self . to_seed ( ) . ok_or ( pkcs8:: Error :: KeyMalformed ) ?;
391+ let pk_key_der =
392+ PrivateKeyChoice :: Seed ( OctetStringRef :: new ( decaps_key_bytes. as_slice ( ) ) ?) . to_der ( ) ?;
393+ let pk_key_octetstr: OctetStringRef < ' _ > = OctetStringRef :: new ( & pk_key_der) ?;
394+
395+ let private_key_info =
396+ pkcs8:: PrivateKeyInfoRef :: new ( P :: ALGORITHM_IDENTIFIER , pk_key_octetstr) ;
397+ pkcs8:: SecretDocument :: encode_msg ( & private_key_info) . map_err ( pkcs8:: Error :: Asn1 )
398+ }
399+ }
400+
401+ #[ cfg( feature = "pkcs8" ) ]
402+ impl < P > TryFrom < pkcs8:: PrivateKeyInfoRef < ' _ > > for DecapsulationKey < P >
403+ where
404+ P : KemParams + AssociatedAlgorithmIdentifier < Params = AnyRef < ' static > > ,
405+ {
406+ type Error = pkcs8:: Error ;
407+
408+ /// Deserialize the decapsulation key from DER format found in `spki.private_key`.
409+ /// Returns a `DecapsulationKey` containing `dk_{pke}`, `ek`, and `z` in case of success.
410+ fn try_from ( private_key_info_ref : pkcs8:: PrivateKeyInfoRef < ' _ > ) -> Result < Self , Self :: Error > {
411+ private_key_info_ref
412+ . algorithm
413+ . assert_algorithm_oid ( P :: ALGORITHM_IDENTIFIER . oid ) ?;
414+
415+ let decaps_key = match private_key_info_ref
416+ . private_key
417+ . decode_into :: < PrivateKeyChoice > ( )
418+ {
419+ Ok ( PrivateKeyChoice :: Seed ( seed) ) => Self :: from_seed (
420+ seed. as_bytes ( )
421+ . try_into ( )
422+ . map_err ( |_| pkcs8:: Error :: KeyMalformed ) ?,
423+ ) ,
424+ Err ( _) => return Err ( pkcs8:: Error :: KeyMalformed ) ,
425+ } ;
426+
427+ Ok ( decaps_key)
428+ }
429+ }
430+
273431#[ cfg( test) ]
274432mod test {
275433 use super :: * ;
0 commit comments