From 9d87de0db480f1b092769888e7fec82fb22d5941 Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Mon, 23 Jun 2025 18:52:06 +0200 Subject: [PATCH 1/9] ml-kem: implement PKCS#8 serialization for v0.11, incl. testcase MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • add dependency to pkcs8 and const-oid crate and define features pkcs8 & alloc • implement AssociatedOid and AssociatedAlgorithmIdentifier for ML-KEM parameter sets {MlKem512Params,MlKem768Params,MlKem1024Params} • implement AssociatedAlgorithmIdentifier for EncapsulationKey & DecapsulationKey • serialization: implement EncodePublicKey for EncapsulationKey implement EncodePrivateKey for DecapsulationKey • deserialization: implement TryFrom for EncapsulationKey implement TryFrom for DecapsulationKey • add testcase with DER serialization roundtrip REMARK: The encapsulation key is serialized into a Subject Public Key Info as defined in RFC 5280 § 4.1.2.7 or more specifically for ML-KEM, draft-ietf-lamps-kyber-certificates-latest from 2025-05-27 § 4: https://lamps-wg.github.io/kyber-certificates/draft-ietf-lamps-kyber-certificates.html#name-subject-public-key-fields The decapsulation key is serialized into a Private Key Info as defined in RFC 5958 § 2 or more specifically for ML-KEM, draft-ietf-lamps-kyber-certificates-latest from 2025-05-27 § 6: https://lamps-wg.github.io/kyber-certificates/draft-ietf-lamps-kyber-certificates.html#name-private-key-format --- ml-kem/Cargo.toml | 4 ++ ml-kem/src/kem.rs | 117 +++++++++++++++++++++++++++++++++++++++++++++ ml-kem/src/lib.rs | 119 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 240 insertions(+) diff --git a/ml-kem/Cargo.toml b/ml-kem/Cargo.toml index 08a0e3b..9ddacc7 100644 --- a/ml-kem/Cargo.toml +++ b/ml-kem/Cargo.toml @@ -17,7 +17,9 @@ exclude = ["tests/key-gen.rs", "tests/key-gen.json", "tests/encap-decap.rs", "te [features] deterministic = [] # Expose deterministic generation and encapsulation functions +alloc = ["pkcs8?/alloc"] zeroize = ["dep:zeroize"] +pkcs8 = ["dep:const-oid", "dep:pkcs8"] [dependencies] kem = "0.3.0-pre.0" @@ -25,6 +27,8 @@ hybrid-array = { version = "0.4", features = ["extra-sizes"] } rand_core = "0.9" sha3 = { version = "0.11.0-rc.0", default-features = false } zeroize = { version = "1.8.1", optional = true, default-features = false } +pkcs8 = { version = "0.11.0-rc.4", optional = true, default-features = false } +const-oid = { version = "0.10.1", optional = true, features = ["db"], default-features = false } [dev-dependencies] criterion = "0.5.1" diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index 8c8edf7..57ae98f 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -18,6 +18,12 @@ pub use ::kem::{Decapsulate, Encapsulate}; /// A shared key resulting from an ML-KEM transaction pub(crate) type SharedKey = B32; +#[cfg(feature = "pkcs8")] +use pkcs8::{der::AnyRef, spki::AssociatedAlgorithmIdentifier}; + +#[cfg(all(feature = "pkcs8", feature = "alloc"))] +use pkcs8::der::asn1::{BitStringRef, OctetStringRef}; + /// A `DecapsulationKey` provides the ability to generate a new key pair, and decapsulate an /// encapsulated shared key. #[derive(Clone, Debug, PartialEq)] @@ -253,6 +259,117 @@ where } } +#[cfg(feature = "pkcs8")] +impl

AssociatedAlgorithmIdentifier for EncapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + type Params = P::Params; + + const ALGORITHM_IDENTIFIER: pkcs8::spki::AlgorithmIdentifier = + P::ALGORITHM_IDENTIFIER; +} + +#[cfg(all(feature = "pkcs8", feature = "alloc"))] +impl

pkcs8::EncodePublicKey for EncapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + /// Serialize the given `EncapsulationKey` into DER format. + /// Returns a `Document` which wraps the DER document in case of success. + fn to_public_key_der(&self) -> pkcs8::spki::Result { + let public_key = self.as_bytes(); + let subject_public_key = BitStringRef::new(0, &public_key)?; + + pkcs8::SubjectPublicKeyInfo { + algorithm: P::ALGORITHM_IDENTIFIER, + subject_public_key, + } + .try_into() + } +} + +#[cfg(feature = "pkcs8")] +impl

TryFrom> for EncapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + type Error = pkcs8::spki::Error; + + /// Deserialize the encapsulation key from DER format found in `spki.subject_public_key`. + /// Returns an `EncapsulationKey` containing `ek_{pke}` and `h` in case of success. + fn try_from(spki: pkcs8::SubjectPublicKeyInfoRef<'_>) -> Result { + if spki.algorithm.oid != P::ALGORITHM_IDENTIFIER.oid { + return Err(pkcs8::spki::Error::OidUnknown { + oid: P::ALGORITHM_IDENTIFIER.oid, + }); + } + + let bitstring_of_encapsulation_key = spki.subject_public_key; + let enc_key = match bitstring_of_encapsulation_key.as_bytes() { + Some(bytes) => { + let arr: hybrid_array::Array> = match bytes.try_into() { + Ok(array) => array, + Err(_) => return Err(pkcs8::spki::Error::KeyMalformed), + }; + EncryptionKey::from_bytes(&arr) + } + None => return Err(pkcs8::spki::Error::KeyMalformed), + }; + + Ok(Self::new(enc_key)) + } +} + +#[cfg(feature = "pkcs8")] +impl

AssociatedAlgorithmIdentifier for DecapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + type Params = P::Params; + + const ALGORITHM_IDENTIFIER: pkcs8::spki::AlgorithmIdentifier = + P::ALGORITHM_IDENTIFIER; +} + +#[cfg(all(feature = "pkcs8", feature = "alloc"))] +impl

pkcs8::EncodePrivateKey for DecapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + /// Serialize the given `DecapsulationKey` into DER format. + /// Returns a `SecretDocument` which wraps the DER document in case of success. + fn to_pkcs8_der(&self) -> pkcs8::Result { + let decap_bytes = self.as_bytes(); + let decap_octets_ref = OctetStringRef::new(&decap_bytes)?; + let private_key = pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, decap_octets_ref); + pkcs8::SecretDocument::encode_msg(&private_key).map_err(pkcs8::Error::Asn1) + } +} + +#[cfg(feature = "pkcs8")] +impl

TryFrom> for DecapsulationKey

+where + P: KemParams + AssociatedAlgorithmIdentifier>, +{ + type Error = pkcs8::spki::Error; + + /// Deserialize the decapsulation key from DER format found in `spki.private_key`. + /// Returns a `DecapsulationKey` containing `dk_{pke}`, `ek`, and `z` in case of success. + fn try_from(private_key_info_ref: pkcs8::PrivateKeyInfoRef<'_>) -> Result { + if private_key_info_ref.algorithm.oid != P::ALGORITHM_IDENTIFIER.oid { + return Err(pkcs8::spki::Error::OidUnknown { + oid: P::ALGORITHM_IDENTIFIER.oid, + }); + } + + let arr = Encoded::::try_from(private_key_info_ref.private_key.as_bytes()) + .map_err(|_| pkcs8::Error::KeyMalformed)?; + + Ok(Self::from_bytes(&arr)) + } +} + #[cfg(test)] mod test { use super::*; diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index a08ae00..480e0ed 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -80,6 +80,12 @@ pub use util::B32; pub use param::{ArraySize, ParameterSet}; +#[cfg(feature = "pkcs8")] +pub use pkcs8; + +#[cfg(feature = "pkcs8")] +use pkcs8::AssociatedOid; + /// An object that knows what size it is pub trait EncodedSizeUser { /// The size of an encoded object @@ -165,6 +171,22 @@ impl ParameterSet for MlKem512Params { type Dv = U4; } +#[cfg(feature = "pkcs8")] +impl AssociatedOid for MlKem512Params { + const OID: pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_512; +} + +#[cfg(feature = "pkcs8")] +impl pkcs8::spki::AssociatedAlgorithmIdentifier for MlKem512Params { + type Params = pkcs8::der::AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: pkcs8::spki::AlgorithmIdentifier = + pkcs8::spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + /// `MlKem768` is the parameter set for security category 3, corresponding to key search on a block /// cipher with a 192-bit key. #[derive(Default, Clone, Debug, PartialEq)] @@ -178,6 +200,22 @@ impl ParameterSet for MlKem768Params { type Dv = U4; } +#[cfg(feature = "pkcs8")] +impl AssociatedOid for MlKem768Params { + const OID: pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_768; +} + +#[cfg(feature = "pkcs8")] +impl pkcs8::spki::AssociatedAlgorithmIdentifier for MlKem768Params { + type Params = pkcs8::der::AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: pkcs8::spki::AlgorithmIdentifier = + pkcs8::spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + /// `MlKem1024` is the parameter set for security category 5, corresponding to key search on a block /// cipher with a 256-bit key. #[derive(Default, Clone, Debug, PartialEq)] @@ -191,6 +229,22 @@ impl ParameterSet for MlKem1024Params { type Dv = U5; } +#[cfg(feature = "pkcs8")] +impl AssociatedOid for MlKem1024Params { + const OID: pkcs8::ObjectIdentifier = const_oid::db::fips203::ID_ALG_ML_KEM_1024; +} + +#[cfg(feature = "pkcs8")] +impl pkcs8::spki::AssociatedAlgorithmIdentifier for MlKem1024Params { + type Params = pkcs8::der::AnyRef<'static>; + + const ALGORITHM_IDENTIFIER: pkcs8::spki::AlgorithmIdentifier = + pkcs8::spki::AlgorithmIdentifier { + oid: Self::OID, + parameters: None, + }; +} + /// A shared key produced by the KEM `K` pub type SharedKey = Array::SharedKeySize>; @@ -232,4 +286,69 @@ mod test { round_trip_test::(); round_trip_test::(); } + + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + use pkcs8::{EncodePrivateKey, EncodePublicKey}; + + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + fn der_serialization_and_deserialization(expected_encaps_len: u16, expected_decaps_len: u16) + where + K: KemCore, + K::EncapsulationKey: EncodePublicKey, + K::DecapsulationKey: EncodePrivateKey, + { + use super::pkcs8::{EncodePrivateKey, PrivateKeyInfoOwned}; + use super::pkcs8::{EncodePublicKey, SubjectPublicKeyInfoRef}; + use pkcs8::der::{self, Decode}; + + let mut rng = rand::rng(); + let (decaps_key, encaps_key) = K::generate(&mut rng); + + // TEST: serialize encapsulation key into DER document + { + let der_document = encaps_key.to_public_key_der().unwrap(); + let serialized_document = der_document.as_bytes(); + + // deserialize encapsulation key from DER document + let parsed = der::Document::from_der(&serialized_document).unwrap(); + assert_eq!(parsed.len(), der::Length::new(expected_encaps_len)); + + // verify that original encapsulation key corresponds to deserialized encapsulation key + let pub_key = parsed.decode_msg::().unwrap(); + assert_eq!( + encaps_key.as_bytes().as_slice(), + pub_key.subject_public_key.as_bytes().unwrap() + ); + } + + // TEST: serialize decapsulation key into DER document + { + use pkcs8::DecodePrivateKey; + let der_document = decaps_key.to_pkcs8_der().unwrap(); + let serialized_document = der_document.as_bytes(); + + // deserialize decapsulation key from DER document + let parsed = der::SecretDocument::from_pkcs8_der(&serialized_document).unwrap(); + assert_eq!(parsed.len(), der::Length::new(expected_decaps_len)); + + // verify that original decapsulation key corresponds to deserialized decapsulation key + let priv_key = parsed.decode_msg::().unwrap(); + assert_eq!( + decaps_key.as_bytes().as_slice(), + priv_key.private_key.as_bytes() + ); + } + } + + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + #[test] + fn pkcs8_round_trip() { + // NOTE: standardized encapsulation key sizes for MlKem{512,768,1024} are {800,1184,1568} bytes respectively. + // DER serialization adds 22 bytes. Thus we expect a length of {822,1206,1590} respectively. + // NOTE: standardized decapsulation key sizes for MlKem{512,768,1024} are {1632,2400,3168} bytes respectively. + // DER serialization adds 24 bytes. Thus we expect a length of {1656,2424,3192} respectively. + der_serialization_and_deserialization::(822, 1656); + der_serialization_and_deserialization::(1206, 2424); + der_serialization_and_deserialization::(1590, 3192); + } } From cc9e1679ffa5df1d882fd1fbfcb427e03329791b Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Fri, 4 Jul 2025 17:25:58 +0200 Subject: [PATCH 2/9] clippy: dereference unnecessary references --- ml-kem/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index 480e0ed..3dcd0d3 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -310,7 +310,7 @@ mod test { let serialized_document = der_document.as_bytes(); // deserialize encapsulation key from DER document - let parsed = der::Document::from_der(&serialized_document).unwrap(); + let parsed = der::Document::from_der(serialized_document).unwrap(); assert_eq!(parsed.len(), der::Length::new(expected_encaps_len)); // verify that original encapsulation key corresponds to deserialized encapsulation key @@ -328,7 +328,7 @@ mod test { let serialized_document = der_document.as_bytes(); // deserialize decapsulation key from DER document - let parsed = der::SecretDocument::from_pkcs8_der(&serialized_document).unwrap(); + let parsed = der::SecretDocument::from_pkcs8_der(serialized_document).unwrap(); assert_eq!(parsed.len(), der::Length::new(expected_decaps_len)); // verify that original decapsulation key corresponds to deserialized decapsulation key From e099bc9ebf7dd5bfdea9e85f80eb0a062de8293d Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Tue, 8 Jul 2025 10:57:02 +0200 Subject: [PATCH 3/9] ml-kem: implement DecodePrivateKey through blanket implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • previously, the associated type Error was defined to be pkcs8::spki::Error instead of pkcs8::Error • As a result, the blanket implementation """ impl DecodePrivateKey for T where T: for<'a> TryFrom, Error = Error>, """ https://docs.rs/pkcs8/0.11.0-rc.4/pkcs8/trait.DecodePrivateKey.html#impl-DecodePrivateKey-for-T was not effective and DecodePrivateKey was not implemented for DecapsulationKey --- ml-kem/src/kem.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index 57ae98f..b4d02c7 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -352,15 +352,15 @@ impl

TryFrom> for DecapsulationKey

where P: KemParams + AssociatedAlgorithmIdentifier>, { - type Error = pkcs8::spki::Error; + type Error = pkcs8::Error; /// Deserialize the decapsulation key from DER format found in `spki.private_key`. /// Returns a `DecapsulationKey` containing `dk_{pke}`, `ek`, and `z` in case of success. fn try_from(private_key_info_ref: pkcs8::PrivateKeyInfoRef<'_>) -> Result { if private_key_info_ref.algorithm.oid != P::ALGORITHM_IDENTIFIER.oid { - return Err(pkcs8::spki::Error::OidUnknown { + return Err(pkcs8::Error::PublicKey(pkcs8::spki::Error::OidUnknown { oid: P::ALGORITHM_IDENTIFIER.oid, - }); + })); } let arr = Encoded::::try_from(private_key_info_ref.private_key.as_bytes()) From ac8befecdb763d49c98256bd59165dc188ddd27e Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Tue, 8 Jul 2025 10:59:44 +0200 Subject: [PATCH 4/9] ml-kem(tests): implement testcases for traits DecodePrivateKey/DecodePublicKey MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit • these traits are supported through blanket implementations and shall be tested as well --- ml-kem/src/lib.rs | 35 +++++++++++++++++++++++++++++------ 1 file changed, 29 insertions(+), 6 deletions(-) diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index 3dcd0d3..19f4d27 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -288,14 +288,14 @@ mod test { } #[cfg(all(feature = "pkcs8", feature = "alloc"))] - use pkcs8::{EncodePrivateKey, EncodePublicKey}; + use pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}; #[cfg(all(feature = "pkcs8", feature = "alloc"))] fn der_serialization_and_deserialization(expected_encaps_len: u16, expected_decaps_len: u16) where K: KemCore, - K::EncapsulationKey: EncodePublicKey, - K::DecapsulationKey: EncodePrivateKey, + K::EncapsulationKey: EncodePublicKey + DecodePublicKey, + K::DecapsulationKey: EncodePrivateKey + DecodePrivateKey, { use super::pkcs8::{EncodePrivateKey, PrivateKeyInfoOwned}; use super::pkcs8::{EncodePublicKey, SubjectPublicKeyInfoRef}; @@ -304,7 +304,7 @@ mod test { let mut rng = rand::rng(); let (decaps_key, encaps_key) = K::generate(&mut rng); - // TEST: serialize encapsulation key into DER document + // TEST: (de)serialize encapsulation key into DER document { let der_document = encaps_key.to_public_key_der().unwrap(); let serialized_document = der_document.as_bytes(); @@ -321,9 +321,20 @@ mod test { ); } - // TEST: serialize decapsulation key into DER document + // TEST: (de)serialize encapsulation key into DER document with the blanket implementation for DecodePublicKey + { + let der_document = encaps_key.to_public_key_der().unwrap(); + let serialized_document = der_document.as_bytes(); + + // deserialize encapsulation key from DER document + let parsed = K::EncapsulationKey::from_public_key_der(serialized_document).unwrap(); + + // verify that original encapsulation key corresponds to deserialized encapsulation key + assert_eq!(parsed, encaps_key); + } + + // TEST: (de)serialize decapsulation key into DER document { - use pkcs8::DecodePrivateKey; let der_document = decaps_key.to_pkcs8_der().unwrap(); let serialized_document = der_document.as_bytes(); @@ -338,6 +349,18 @@ mod test { priv_key.private_key.as_bytes() ); } + + // TEST: (de)serialize decapsulation key into DER document with the blanket implementation for DecodePrivateKey + { + let der_document = decaps_key.to_pkcs8_der().unwrap(); + let serialized_document = der_document.as_bytes(); + + // deserialize decapsulation key from DER document + let parsed = K::DecapsulationKey::from_pkcs8_der(serialized_document).unwrap(); + + // verify that original decapsulation key corresponds to deserialized decapsulation key + assert_eq!(parsed, decaps_key); + } } #[cfg(all(feature = "pkcs8", feature = "alloc"))] From ce3199b26b393aef7f1393cd80f890046ef70288 Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Thu, 14 Aug 2025 17:26:41 +0200 Subject: [PATCH 5/9] ml-kem: implement DER document structure suggested in IETF draft spec MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let us consider the test vectors from this repository: https://github.com/lamps-wg/kyber-certificates/tree/624bcaa4bd9ea9e72de5b51d81ce2d90cbd7e54a This implementation uses a seed of values [0, 1, …, 63]. Now let us generate the public and private keys. My previous implementation generated an OCTET STRING of length 1632 for ML-KEM 512: $ openssl asn1parse -in ml-kem-512-private-key-generated.pem -dump | head -n6 | tail -n2 20:d=1 hl=4 l=1632 prim: OCTET STRING 0000 - 70 55 4f d4 36 34 4f 27-85 b1 b3 b1 ba c1 84 b6 pUO.64O'........ The IETF draft specification suggests a structure involving ASN.1 CHOICE to distinguish between seed, expanded, and both formats: https://lamps-wg.github.io/kyber-certificates/draft-ietf-lamps-kyber-certificates.html#name-private-key-format This leads to four additional bytes: $ openssl asn1parse -in ML-KEM-512-expanded.priv -dump | head -n6 | tail -n2 20:d=1 hl=4 l=1636 prim: OCTET STRING 0000 - 04 82 06 60 70 55 4f d4-36 34 4f 27 85 b1 b3 b1 ...`pUO.64O'.... This implementation now introduces the CHOICE discriminator. Recognize that the draft suggests serialization to the seed format per default. Sadly we cannot implement this. The data stored in EncapsulationKey does not store the seed which lead to this encapsulation key. As a result, the expanded format is always used. The previous text talked about changes in the serialization. In terms of deserialization all three formats must be supported which also I implemented. --- Cargo.lock | 23 ++ ml-kem/Cargo.toml | 2 + ml-kem/src/kem.rs | 102 ++++++++- ml-kem/src/lib.rs | 213 ++++++++++++++++-- ml-kem/tests/examples/ML-KEM-1024-both.priv | 71 ++++++ .../tests/examples/ML-KEM-1024-expanded.priv | 69 ++++++ ml-kem/tests/examples/ML-KEM-1024-seed.priv | 4 + ml-kem/tests/examples/ML-KEM-1024.pub | 36 +++ ml-kem/tests/examples/ML-KEM-512-both.priv | 39 ++++ .../tests/examples/ML-KEM-512-expanded.priv | 37 +++ ml-kem/tests/examples/ML-KEM-512-seed.priv | 4 + ml-kem/tests/examples/ML-KEM-512.pub | 20 ++ ml-kem/tests/examples/ML-KEM-768-both.priv | 55 +++++ .../tests/examples/ML-KEM-768-expanded.priv | 53 +++++ ml-kem/tests/examples/ML-KEM-768-seed.priv | 4 + ml-kem/tests/examples/ML-KEM-768.pub | 28 +++ 16 files changed, 729 insertions(+), 31 deletions(-) create mode 100644 ml-kem/tests/examples/ML-KEM-1024-both.priv create mode 100644 ml-kem/tests/examples/ML-KEM-1024-expanded.priv create mode 100644 ml-kem/tests/examples/ML-KEM-1024-seed.priv create mode 100644 ml-kem/tests/examples/ML-KEM-1024.pub create mode 100644 ml-kem/tests/examples/ML-KEM-512-both.priv create mode 100644 ml-kem/tests/examples/ML-KEM-512-expanded.priv create mode 100644 ml-kem/tests/examples/ML-KEM-512-seed.priv create mode 100644 ml-kem/tests/examples/ML-KEM-512.pub create mode 100644 ml-kem/tests/examples/ML-KEM-768-both.priv create mode 100644 ml-kem/tests/examples/ML-KEM-768-expanded.priv create mode 100644 ml-kem/tests/examples/ML-KEM-768-seed.priv create mode 100644 ml-kem/tests/examples/ML-KEM-768.pub diff --git a/Cargo.lock b/Cargo.lock index 8743656..5005403 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -401,9 +401,22 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7050e8041c28720851f7db83183195b6acf375bb7bb28e3b86f0fe6cbd69459d" dependencies = [ "const-oid", + "der_derive", + "pem-rfc7468", "zeroize", ] +[[package]] +name = "der_derive" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "14bfffadecb79dfde429f5dcd7553780c2cea5f7d0e72ad7c37a74f1ef79230a" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + [[package]] name = "dhkem" version = "0.0.1-alpha" @@ -856,6 +869,7 @@ version = "0.3.0-pre" dependencies = [ "criterion", "crypto-common", + "der", "hex", "hex-literal", "hybrid-array", @@ -973,6 +987,15 @@ dependencies = [ "primeorder", ] +[[package]] +name = "pem-rfc7468" +version = "1.0.0-rc.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a8e58fab693c712c0d4e88f8eb3087b6521d060bcaf76aeb20cb192d809115ba" +dependencies = [ + "base64ct", +] + [[package]] name = "pin-project-lite" version = "0.2.16" diff --git a/ml-kem/Cargo.toml b/ml-kem/Cargo.toml index 9ddacc7..9b8dc9e 100644 --- a/ml-kem/Cargo.toml +++ b/ml-kem/Cargo.toml @@ -20,6 +20,7 @@ deterministic = [] # Expose deterministic generation and encapsulation functions alloc = ["pkcs8?/alloc"] zeroize = ["dep:zeroize"] pkcs8 = ["dep:const-oid", "dep:pkcs8"] +pem = ["pkcs8?/pem"] [dependencies] kem = "0.3.0-pre.0" @@ -29,6 +30,7 @@ sha3 = { version = "0.11.0-rc.0", default-features = false } zeroize = { version = "1.8.1", optional = true, default-features = false } pkcs8 = { version = "0.11.0-rc.4", optional = true, default-features = false } const-oid = { version = "0.10.1", optional = true, features = ["db"], default-features = false } +der = { version = "0.8.0-rc.0", features = ["derive"] } [dev-dependencies] criterion = "0.5.1" diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index b4d02c7..4805a19 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -19,10 +19,17 @@ pub use ::kem::{Decapsulate, Encapsulate}; pub(crate) type SharedKey = B32; #[cfg(feature = "pkcs8")] -use pkcs8::{der::AnyRef, spki::AssociatedAlgorithmIdentifier}; - +use hybrid_array::Array; +#[cfg(feature = "pkcs8")] +use pkcs8::der::AnyRef; #[cfg(all(feature = "pkcs8", feature = "alloc"))] -use pkcs8::der::asn1::{BitStringRef, OctetStringRef}; +use pkcs8::der::Encode; +#[cfg(all(feature = "pkcs8", feature = "alloc"))] +use pkcs8::der::asn1::BitStringRef; +#[cfg(feature = "pkcs8")] +use pkcs8::der::asn1::OctetStringRef; +#[cfg(feature = "pkcs8")] +use pkcs8::spki::AssociatedAlgorithmIdentifier; /// A `DecapsulationKey` provides the ability to generate a new key pair, and decapsulate an /// encapsulated shared key. @@ -259,6 +266,36 @@ where } } +/// The serialization of the private key is a choice between three different formats +/// [according to PKCS#8](https://lamps-wg.github.io/kyber-certificates/draft-ietf-lamps-kyber-certificates.html#name-private-key-format). +/// +/// “For ML-KEM private keys, the privateKey field in `OneAsymmetricKey` +/// contains one of the following DER-encoded `CHOICE` structures. +/// The seed format is a fixed 64-byte `OCTET STRING` (66 bytes total +/// with the 0x8040 tag and length) for all security levels, +/// while the expandedKey and both formats vary in size by security level” +#[cfg(feature = "pkcs8")] +#[derive(Clone, Debug, pkcs8::der::Choice)] +pub enum PrivateKeyChoice<'o> { + /// FIPS 203 format for an ML-KEM private key: a 64-octet seed + #[asn1(tag_mode = "IMPLICIT", context_specific = "0")] + Seed(OctetStringRef<'o>), + /// FIPS 203 format for an ML-KEM private key: the decapsulation key resulting from PKE's `KeyGen` operation + Expanded(OctetStringRef<'o>), + /// In this setting both key formats are provided in a `PrivateKeyBothChoice` `struct` + Both(PrivateKeyBothChoice<'o>), +} + +/// The private key's `Both` variant contains the seed as well as the expanded key. +#[cfg(feature = "pkcs8")] +#[derive(Clone, Debug, pkcs8::der::Sequence)] +pub struct PrivateKeyBothChoice<'o> { + /// FIPS 203 format for an ML-KEM private key: a 64-octet seed + pub seed: OctetStringRef<'o>, + /// FIPS 203 format for an ML-KEM private key: the decapsulation key resulting from PKE's `KeyGen` operation + pub expanded: OctetStringRef<'o>, +} + #[cfg(feature = "pkcs8")] impl

AssociatedAlgorithmIdentifier for EncapsulationKey

where @@ -308,7 +345,7 @@ where let bitstring_of_encapsulation_key = spki.subject_public_key; let enc_key = match bitstring_of_encapsulation_key.as_bytes() { Some(bytes) => { - let arr: hybrid_array::Array> = match bytes.try_into() { + let arr: Array> = match bytes.try_into() { Ok(array) => array, Err(_) => return Err(pkcs8::spki::Error::KeyMalformed), }; @@ -340,10 +377,18 @@ where /// Serialize the given `DecapsulationKey` into DER format. /// Returns a `SecretDocument` which wraps the DER document in case of success. fn to_pkcs8_der(&self) -> pkcs8::Result { - let decap_bytes = self.as_bytes(); - let decap_octets_ref = OctetStringRef::new(&decap_bytes)?; - let private_key = pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, decap_octets_ref); - pkcs8::SecretDocument::encode_msg(&private_key).map_err(pkcs8::Error::Asn1) + let decaps_key_bytes: Array::DecapsulationKeySize> = self.as_bytes(); + + // NOTE: “The seed format is RECOMMENDED as it efficiently stores both the private and public key”, + // but this is impossible with the definition of the type `DecapsulationKey`; see issue 53. + let pk_key_der = + PrivateKeyChoice::Expanded(OctetStringRef::new(decaps_key_bytes.as_slice())?) + .to_der()?; + let pk_key_octetstr: OctetStringRef<'_> = OctetStringRef::new(&pk_key_der)?; + + let private_key_info = + pkcs8::PrivateKeyInfoRef::new(P::ALGORITHM_IDENTIFIER, pk_key_octetstr); + pkcs8::SecretDocument::encode_msg(&private_key_info).map_err(pkcs8::Error::Asn1) } } @@ -363,10 +408,45 @@ where })); } - let arr = Encoded::::try_from(private_key_info_ref.private_key.as_bytes()) - .map_err(|_| pkcs8::Error::KeyMalformed)?; + let seed_to_key = |seed: OctetStringRef<'_>| -> Result, Self::Error> { + let (head, tail) = seed.as_bytes().split_at(32); + let d: &B32 = &B32::try_from_iter(head.iter().copied()).unwrap(); + let z: &B32 = &B32::try_from_iter(tail.iter().copied()).unwrap(); + Ok(DecapsulationKey::generate_deterministic(d, z)) + }; + + let expanded_to_key = + |expanded: OctetStringRef<'_>| -> Result, Self::Error> { + let bytes = expanded.as_bytes(); + let buffer = Array::::from_fn(|idx| bytes[idx]); + let array = + Encoded::::try_from(buffer).map_err(|_| pkcs8::Error::KeyMalformed)?; + Ok(Self::from_bytes(&array)) + }; + + let decaps_key = match private_key_info_ref + .private_key + .decode_into::() + { + Ok(PrivateKeyChoice::Seed(seed)) => seed_to_key(seed)?, + Ok(PrivateKeyChoice::Expanded(expanded)) => expanded_to_key(expanded)?, + Ok(PrivateKeyChoice::Both(PrivateKeyBothChoice { seed, expanded })) => { + let computed_decaps_key = seed_to_key(seed)?; + let given_decaps_key = expanded_to_key(expanded)?; + + // “When receiving a private key that contains both the seed and the expandedKey, + // the recipient SHOULD perform a seed consistency check to ensure + // that the sender properly generated the private key” + if computed_decaps_key != given_decaps_key { + return Err(pkcs8::Error::KeyMalformed); + } + + computed_decaps_key + } + Err(_) => return Err(pkcs8::Error::KeyMalformed), + }; - Ok(Self::from_bytes(&arr)) + Ok(decaps_key) } } diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index 19f4d27..af94d60 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -266,6 +266,17 @@ pub type MlKem1024 = kem::Kem; #[cfg(test)] mod test { use super::*; + #[cfg(all(feature = "pkcs8", feature = "alloc", feature = "pem"))] + use crate::kem::PrivateKeyBothChoice; + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + use crate::kem::PrivateKeyChoice; + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + use pkcs8::der::{self, Decode}; + #[cfg(all(feature = "pkcs8", feature = "alloc"))] + use pkcs8::{ + DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, PrivateKeyInfoRef, + SubjectPublicKeyInfoRef, + }; fn round_trip_test() where @@ -288,19 +299,12 @@ mod test { } #[cfg(all(feature = "pkcs8", feature = "alloc"))] - use pkcs8::{DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey}; - - #[cfg(all(feature = "pkcs8", feature = "alloc"))] - fn der_serialization_and_deserialization(expected_encaps_len: u16, expected_decaps_len: u16) + fn der_serialization_and_deserialization(expected_encaps_len: u32, expected_decaps_len: u32) where K: KemCore, K::EncapsulationKey: EncodePublicKey + DecodePublicKey, K::DecapsulationKey: EncodePrivateKey + DecodePrivateKey, { - use super::pkcs8::{EncodePrivateKey, PrivateKeyInfoOwned}; - use super::pkcs8::{EncodePublicKey, SubjectPublicKeyInfoRef}; - use pkcs8::der::{self, Decode}; - let mut rng = rand::rng(); let (decaps_key, encaps_key) = K::generate(&mut rng); @@ -339,15 +343,18 @@ mod test { let serialized_document = der_document.as_bytes(); // deserialize decapsulation key from DER document - let parsed = der::SecretDocument::from_pkcs8_der(serialized_document).unwrap(); - assert_eq!(parsed.len(), der::Length::new(expected_decaps_len)); + let secret_document = der::SecretDocument::from_pkcs8_der(serialized_document).unwrap(); + assert_eq!(secret_document.len(), der::Length::new(expected_decaps_len)); + assert_eq!(secret_document.as_bytes(), der_document.as_bytes()); // verify that original decapsulation key corresponds to deserialized decapsulation key - let priv_key = parsed.decode_msg::().unwrap(); - assert_eq!( - decaps_key.as_bytes().as_slice(), - priv_key.private_key.as_bytes() - ); + let priv_key = secret_document.decode_msg::().unwrap(); + + if let Ok(PrivateKeyChoice::Expanded(expanded)) = priv_key.private_key.decode_into() { + assert_eq!(decaps_key.as_bytes().as_slice(), expanded.as_bytes()); + } else { + panic!("unexpected PrivateKey serialization"); + } } // TEST: (de)serialize decapsulation key into DER document with the blanket implementation for DecodePrivateKey @@ -365,13 +372,179 @@ mod test { #[cfg(all(feature = "pkcs8", feature = "alloc"))] #[test] - fn pkcs8_round_trip() { + fn pkcs8_serialize_and_deserialize_round_trip() { // NOTE: standardized encapsulation key sizes for MlKem{512,768,1024} are {800,1184,1568} bytes respectively. // DER serialization adds 22 bytes. Thus we expect a length of {822,1206,1590} respectively. // NOTE: standardized decapsulation key sizes for MlKem{512,768,1024} are {1632,2400,3168} bytes respectively. - // DER serialization adds 24 bytes. Thus we expect a length of {1656,2424,3192} respectively. - der_serialization_and_deserialization::(822, 1656); - der_serialization_and_deserialization::(1206, 2424); - der_serialization_and_deserialization::(1590, 3192); + // DER serialization adds 28 bytes. Thus we expect a length of {1660,2428,3196} respectively. + der_serialization_and_deserialization::(822, 1660); + der_serialization_and_deserialization::(1206, 2428); + der_serialization_and_deserialization::(1590, 3196); + } + + #[cfg(all(feature = "pkcs8", feature = "alloc", feature = "pem"))] + fn compare_with_reference_keys(variant: usize, ref_pub_key_pem: &str, ref_priv_key_pem: &str) + where + K: KemCore, + K::EncapsulationKey: EncodePublicKey, + K::DecapsulationKey: EncodePrivateKey, + { + // auxiliary RNG implementation for a static seed + struct SeedBasedRng { + index: usize, + seed: [u8; SEED_LEN], + } + + impl rand_core::RngCore for SeedBasedRng { + fn next_u32(&mut self) -> u32 { + let mut buf = [0u8; 4]; + self.fill_bytes(&mut buf); + u32::from_be_bytes(buf) + } + + fn next_u64(&mut self) -> u64 { + let mut buf = [0u8; 8]; + self.fill_bytes(&mut buf); + u64::from_be_bytes(buf) + } + + fn fill_bytes(&mut self, dst: &mut [u8]) { + for item in dst { + *item = self.seed[self.index]; + self.index = self.index.wrapping_add(1) & ((1 << SEED_LEN.ilog2()) - 1); + } + } + } + + impl CryptoRng for SeedBasedRng {} + + const SEED_LEN: usize = 64; + assert_eq!(SEED_LEN & (SEED_LEN - 1), 0); + + let seed: [u8; SEED_LEN] = core::array::from_fn(|i| u8::try_from(i).unwrap()); + let mut rng = SeedBasedRng { seed, index: 0 }; + let (decaps_key, encaps_key) = K::generate(&mut rng); + + let gen_pub_key_pem = encaps_key + .to_public_key_pem(pkcs8::LineEnding::LF) + .expect("serialization works"); + let gen_priv_key_pem = decaps_key + .to_pkcs8_pem(pkcs8::LineEnding::LF) + .expect("serialization works"); + + { + // TEST: DER document of public key must match + let gen_pub_key_der = encaps_key.to_public_key_der().expect("serialization works"); + let ref_pub_key_der = der::Document::from_pem(ref_pub_key_pem) + .expect("can read pubkey PEM document") + .1; + assert_eq!(gen_pub_key_der, ref_pub_key_der); + } + + // TEST: PEM document of public key must match + assert_eq!( + gen_pub_key_pem, ref_pub_key_pem, + "key generated from static seed and reference public key for ML-KEM-{variant} do not match" + ); + // TEST: PEM document of private key must match + assert_eq!( + gen_priv_key_pem.as_str(), + ref_priv_key_pem, + "key generated from static seed and reference private key for ML-KEM-{variant} do not match" + ); + } + + #[cfg(all(feature = "pkcs8", feature = "alloc", feature = "pem"))] + #[test] + fn pkcs8_generate_same_keys_like_golang_for_static_seed() { + // NOTE: test vector files come from https://github.com/lamps-wg/kyber-certificates/tree/624bcaa4bd9ea9e72de5b51d81ce2d90cbd7e54a + const PEM_512_PUB: &str = include_str!("../tests/examples/ML-KEM-512.pub"); + const PEM_768_PUB: &str = include_str!("../tests/examples/ML-KEM-768.pub"); + const PEM_1024_PUB: &str = include_str!("../tests/examples/ML-KEM-1024.pub"); + const PEM_512_PRIV: &str = include_str!("../tests/examples/ML-KEM-512-expanded.priv"); + const PEM_768_PRIV: &str = include_str!("../tests/examples/ML-KEM-768-expanded.priv"); + const PEM_1024_PRIV: &str = include_str!("../tests/examples/ML-KEM-1024-expanded.priv"); + + compare_with_reference_keys::(512, PEM_512_PUB, PEM_512_PRIV); + compare_with_reference_keys::(768, PEM_768_PUB, PEM_768_PRIV); + compare_with_reference_keys::(1024, PEM_1024_PUB, PEM_1024_PRIV); + } + + #[cfg(all(feature = "pkcs8", feature = "alloc", feature = "pem"))] + #[test] + fn pkcs8_can_read_reference_private_keys() { + // NOTE: test vector files come from https://github.com/lamps-wg/kyber-certificates/tree/624bcaa4bd9ea9e72de5b51d81ce2d90cbd7e54a + const PEM_512_SEED: &str = include_str!("../tests/examples/ML-KEM-512-seed.priv"); + const PEM_512_EXPANDED: &str = include_str!("../tests/examples/ML-KEM-512-expanded.priv"); + const PEM_512_BOTH: &str = include_str!("../tests/examples/ML-KEM-512-both.priv"); + const PEM_768_SEED: &str = include_str!("../tests/examples/ML-KEM-768-seed.priv"); + const PEM_768_EXPANDED: &str = include_str!("../tests/examples/ML-KEM-768-expanded.priv"); + const PEM_768_BOTH: &str = include_str!("../tests/examples/ML-KEM-768-both.priv"); + const PEM_1024_SEED: &str = include_str!("../tests/examples/ML-KEM-1024-seed.priv"); + const PEM_1024_EXPANDED: &str = include_str!("../tests/examples/ML-KEM-1024-expanded.priv"); + const PEM_1024_BOTH: &str = include_str!("../tests/examples/ML-KEM-1024-both.priv"); + + fn expect_seed_bytes(ref_pem: &str, expected_seed_prefix: &[u8]) { + let length = expected_seed_prefix.len(); + let secret_document = der::SecretDocument::from_pkcs8_pem(ref_pem) + .expect("can read reference PEM private key file"); + let priv_key = secret_document.decode_msg::().unwrap(); + + let given_prefix = match priv_key + .private_key + .decode_into() + .expect("could not read internal structure of PEM private key") + { + PrivateKeyChoice::Seed(seed) + | PrivateKeyChoice::Both(PrivateKeyBothChoice { seed, .. }) => { + &seed.as_bytes()[0..length] + } + PrivateKeyChoice::Expanded(_) => return, + }; + + assert_eq!(given_prefix, expected_seed_prefix); + } + + fn expect_expanded_bytes(ref_pem: &str, expected_expanded_prefix: &[u8]) { + let length = expected_expanded_prefix.len(); + let secret_document = der::SecretDocument::from_pkcs8_pem(ref_pem) + .expect("can read reference PEM private key file"); + let priv_key = secret_document.decode_msg::().unwrap(); + + let given_prefix = match priv_key + .private_key + .decode_into() + .expect("could not read internal expanded structure of PEM private key") + { + PrivateKeyChoice::Seed(_) => return, + PrivateKeyChoice::Expanded(expanded) + | PrivateKeyChoice::Both(PrivateKeyBothChoice { expanded, .. }) => { + &expanded.as_bytes()[0..length] + } + }; + + assert_eq!(given_prefix, expected_expanded_prefix); + } + + const STATIC_SEED_PREFIX: &[u8] = + &[0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17]; + const EXPANDED_512_KEY_PREFIX: &[u8] = &[0x70, 0x55, 0x4f, 0xd4, 0x36, 0x34, 0x4f, 0x27]; + const EXPANDED_768_KEY_PREFIX: &[u8] = &[0x27, 0xd2, 0xa7, 0x7f, 0x33, 0x75, 0x6f, 0x61]; + const EXPANDED_1024_KEY_PREFIX: &[u8] = &[0xf7, 0x7b, 0x7f, 0x6b, 0x15, 0xc7, 0x3f, 0xe2]; + + expect_seed_bytes(PEM_512_SEED, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_512_EXPANDED, EXPANDED_512_KEY_PREFIX); + expect_seed_bytes(PEM_512_BOTH, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_512_BOTH, EXPANDED_512_KEY_PREFIX); + + expect_seed_bytes(PEM_768_SEED, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_768_EXPANDED, EXPANDED_768_KEY_PREFIX); + expect_seed_bytes(PEM_768_BOTH, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_768_BOTH, EXPANDED_768_KEY_PREFIX); + + expect_seed_bytes(PEM_1024_SEED, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_1024_EXPANDED, EXPANDED_1024_KEY_PREFIX); + expect_seed_bytes(PEM_1024_BOTH, STATIC_SEED_PREFIX); + expect_expanded_bytes(PEM_1024_BOTH, EXPANDED_1024_KEY_PREFIX); } } diff --git a/ml-kem/tests/examples/ML-KEM-1024-both.priv b/ml-kem/tests/examples/ML-KEM-1024-both.priv new file mode 100644 index 0000000..ff3dcbe --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-1024-both.priv @@ -0,0 +1,71 @@ +-----BEGIN PRIVATE KEY----- +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 +Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-1024-expanded.priv b/ml-kem/tests/examples/ML-KEM-1024-expanded.priv new file mode 100644 index 0000000..88ab933 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-1024-expanded.priv @@ -0,0 +1,69 @@ +-----BEGIN PRIVATE KEY----- +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 +JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw== +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-1024-seed.priv b/ml-kem/tests/examples/ML-KEM-1024-seed.priv new file mode 100644 index 0000000..e9d8768 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-1024-seed.priv @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFQCAQAwCwYJYIZIAWUDBAQDBEKAQAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ +GhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-1024.pub b/ml-kem/tests/examples/ML-KEM-1024.pub new file mode 100644 index 0000000..d44e8f0 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-1024.pub @@ -0,0 +1,36 @@ +-----BEGIN PUBLIC KEY----- +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 +uYOILhF1 +-----END PUBLIC KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-512-both.priv b/ml-kem/tests/examples/ML-KEM-512-both.priv new file mode 100644 index 0000000..51d4be4 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-512-both.priv @@ -0,0 +1,39 @@ +-----BEGIN PRIVATE KEY----- +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 +Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-512-expanded.priv b/ml-kem/tests/examples/ML-KEM-512-expanded.priv new file mode 100644 index 0000000..418dfa0 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-512-expanded.priv @@ -0,0 +1,37 @@ +-----BEGIN PRIVATE KEY----- +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 +JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw== +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-512-seed.priv b/ml-kem/tests/examples/ML-KEM-512-seed.priv new file mode 100644 index 0000000..8222519 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-512-seed.priv @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFQCAQAwCwYJYIZIAWUDBAQBBEKAQAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ +GhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-512.pub b/ml-kem/tests/examples/ML-KEM-512.pub new file mode 100644 index 0000000..da30af4 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-512.pub @@ -0,0 +1,20 @@ +-----BEGIN PUBLIC KEY----- +MIIDMjALBglghkgBZQMEBAEDggMhADmVgV5ZfRBDVc8pqlMzyTJRhp1bzb5IcST2 +Ari2pmwWxHYWSK12XPXYAGtRXpBafwrAdrDGLvoygVPnylcBaZ8TBfHmvG+QsOSb +aTUSts6ZKouAFt38GmYsfj+WGcvYad13GvMIlszVkYrGy3dGbF53mZbWf/mqvJdQ +Pyx7fi0ADYZFD7GAfKTKvaRlgloxx4mht6SRqzhydl0yDQtxkg+iE8lAk0Frg7gS +Tmn2XmLLUADcw3qpoP/3OXDEdy81fSQYnKb1MFVowOI3ajdipoxgXlY8XSCVcuD8 +dTLKKUcpU1VntfxBPF6HktJGRTbMgI+YrddGZPFBVm+QFqkKVBgpqYoEZM5BqLtE +wtT6PCwglGByjvFKGnxMm5jRIgO0zDUpFgqasteDj3/2tTrgWqMafWRrevpsRZMl +JqPDdVYZvplMIRwqMcBbNEeDbLIVC+GCna5rBMVTXP9Ubjkrp5dBFyD5JPSQpaxU +lfITVtVQt4KmTBaItrZVvMeEIZekNML2Vjtbfwmni8xIgjJ4NWHRb0y6tnVUAAUH +gVcMZmBLgXrRJSKUc26LAYYaS1p0UZuLb+UUiaUHI5Llh2JscTd2V10zgGocjicy +r5fCaA9RZmMxxOuLvAQxxPloMtrxs8RVKPuhU/bHixwZhwKUfM0zdyekb7U7oR3l +y0GRNGhZUWy2rXJADzzyCbI2rvNaWArIfrPjD6/WaXPKin3SZ1r0H3oXthQzzRr4 +D3cIhp9mVIhJeYCxrBCgzctjagDthoGzXkKRJMqANQcluF+DperDpKPMFgCQPmUp +NWC5szblrw1SnawaBIEZMCy3qbzBELlIUb8CEX8ZncSFqFK3Rz8JuDGmgx1bVMC3 +kNIlz2u5LZRiomzbM92lEjx6rw4moLg2Ve6ii/OoB0clAY/WuuS2Ac9huqtxp6PT +UZejQ+dLSicsEl1UCJZCbYW3lY07OKa6mH7DciXHtEzbEt3kU5tKsII2NoPwS/eg +nMXEHf6DChsWLgsyQzQ2LwhKFEZ3IzRLrdAA+NjFN8SPmY8FMHzr0e3guBw7xZoG +WhttY7Js +-----END PUBLIC KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-768-both.priv b/ml-kem/tests/examples/ML-KEM-768-both.priv new file mode 100644 index 0000000..0c603db --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-768-both.priv @@ -0,0 +1,55 @@ +-----BEGIN PRIVATE KEY----- +MIIJvgIBADALBglghkgBZQMEBAIEggmqMIIJpgRAAAECAwQFBgcICQoLDA0ODxAR +EhMUFRYXGBkaGxwdHh8gISIjJCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+PwSC +CWAn0qd/M3VvYSCO8ROr6CWVhz1KvHMOW11nlSm/akzrY4NCcjGoYS9BVQUVrLpS +5I6ti5QoM7vmhl0T0Up50sXD4H8KBW2N56rfyroFjEk8gLN8q4xWJ1O7O6a27IKX ++IXqp1QNUwAVqEQG5VsTZrV34jbOWKJtih61pE1UIyPCFn2b9KR/mFaZygW65DuN +7GF/AjgKOJCv1LjH7H7eJlU6Al885bxdemITAwQjXLGtSDa1ZrW4Y72b20WihEpw +R7bI04PkSFJeBAtNyKK0jGw3yW1i1D8/2I4ogcQKIFyeJI9lK1kngad5+GiA8qFH +tnhj85HMGlqQjACV4HISKR4u+KNuuanAxgcyJbNHA6SvBJOCxHVz2mj96SRa1ETj +Gx+9tSHx9h83vAzvKSBn5nDSih/9kE9vEZCplpGKEwN6bKvzw3O/gpbNN6szundG +gJzD+K3hs2Ob1Xv8xpZQqq8d4Zj8TARjKZ5SxGF4DMQo/F0EpcUYUMumwqUnQ0Bn +V5PdoJvkTCnmOVxl+F0qCnxt9BHmkRsfLLbDUc0uh19Rtji+d2CX6T4vKy+D2gvu +9KqFup52OrZFAqDKUiLp6rWztwiO1SBg6Mgmm5Q6casK4cWxtofS4BnPgDa8+b9u +e6w6qjbkFmD6pFQPJkjNk6GJ7Fwt6nC6yqpP/JBvkIEOobZ78k8seM9rqIGq6mHA +ZSv/lbG65EJtF3O5zCyoLCHjjGNuOxxSMkSYawvoqD9d1c8tVHYvs8Xr9ZuOiFMC +sc5HAz7fdg9OApvkC21WaxnddYrNXHQSh4ExJE+QFyxT8mZjwh2QUwHUi6+RyRfM +d3np2IAswQ2Jo3BQmaKtOjqIlnQ8EURpgJO+JX2stm3HhSKLkSyNll0Uqig0LDrE +qT/vpTKyCUXdwQIBOcFNY4uQjE3d6aBkW5Wy5EFNQLt58EQTgw8VqHPCi7cFnCdB +ACAV8gQI8FjnFbC/mVtTgLfdMloFarl+ZZor4M32wzcxxoOmNLdx6MkqE5ruS7Dk +nHB3Mh1C/BmffB8pjKYl0iOlwmOgPMSBWbeBJmW3hjfk4YcgssKaa5n0J2aky8Tc +UIupS6g7icOlx4+Lsmu9m3m+uMgYJJD1eT7luWATt0t+Fp4p0WLxMVRk6n1yQ22J +t1UWEZLIHMLdHIuLunle9CbuHMAcN6qjeyz/iwo3i0fL0LTUk5jPwnEpWWmfoL2M +2EZmrMYfVBuE+pa5yFTk516RRK3bRLhWalffu1Rc5CPAM0byssGpF4DRUqjeGk1M +nKzec5LJloiMwjmcAsOLM1Ot+KyrKDkk2gCgW3bnOMcskw1sugmuFomQ+qH+8iJu +eAhh1Bbv9AL091n8ZIqx+XEAEJCH+W5LFI0ssx5IBTFOoM2V+wI+rA2YlHS6QgHX +tB0m9TlLIX7qWzS3Gos3kxwOWUJx4LfHMyVyQCM+e6c1YD5CWofe53B543yyiiF2 +RZTOU1DY2itioHF0lDAy7InJiAnHO2Qj0wwdKDp2amTYlwPD1im0l4KNSDIMNGIQ +eXopiqENQjyN2gadArxZ5s3wOglriz2kyrm4DKShSQdnLM7x7E+vI0oLxbfp1HPy +sxM7Oyah0XXLZ6eAWRlpnAL3ZTG5nF+JGAcEu0ykU1xbiXJnnGYKB8XlFLhwCchi +649RV2le+z/ECp3va4HBzAKiSa5PCUrQ2b00hcHBxoCAUgp8jGMgMs7nOBVOXFF2 +wH2lYCR3akMP526s9mWj97gyECIVvILxCTnINVcEM2qPrB2B5LsEhapdfHTWtZu+ +XF6XKg2LrEEbVbXVVXzWgKGo9xtOuGvEjJoFCXMaVL2dcpCyeWPkNy3JsZnP3KwL +AazSimI5URLkxDZI1iLEjII00BRA6Mw3bJJ/I6WvyawEdMZiJ05CRSXIVS7OOz/i +ZRbekBvH1RW96JVY5ibJXIC5M0L4AQAE855sbJSHHF40TKs5Zsg1+alqWa/THEAo +azixwaeEcLq5R1GJNEU86Gc2qRnx9abVEKhvVFT8OYDLXHZb0r1fezaxQQ1mNcjO +tHxN2g12oo6sk5xxwwJIBIZscWJmWEQhY8LCIRflCs785jeKmFZSMCpO8MLODMcW +t3luK2suN3ffoaw9olmjG1qbUw+MtjioGmKsMBhJq6+VpzAb2jAGiQm/235n28y7 +OKVVGiWxo6D2hXSK1XU9iIDwAWxidIYWY4TFVx/iNlkANk0DgxHi2HXbNmaGkyte +xgJDCjaeh6bvXDOHhmV4Jb1MBXrOuSPrCTXmkF5jtM7X+AhXp3PdZLFQ0mYS6prB +IFLbIBe/GEPMtLMoG2kNxyit+oXAAoG448CShzNfhWtPwokvaaL1eSGtoBkUxAmI +Zi1XdpZip4Y1G5tmST2reVlNmG3iEA1lug/06li4FTjSSkQ1olj6wlQEqn9B9lix +OFBl4VjctgEVcycg9ARZqqwV5AaVOpCsUpl9HM0HAGDvxl255lM1RGf61W7HE8hu +dUDEI6zyZp9S+m9KxoiNhx7z6EfAKaiq+7kuF7JKoHmx9Bm6YXW0Qq+xGQnUpWtw +oDNbKHOSGKp8k0jiw8Lz6z0VpB5kF8DdlL/rIUGbMRp7sToYC76DMhipprF0R8yF +8iWFlYenMHcEmsvP1E0PAlQ44V0VOCcNWG4b+DGSqUWc9jwOly+FKXZ5gx7PEhUJ +hRy4NA9vEHsPoaDv0bNqgYm8CFxPXLeE5VP0G5GPgDl84ZVveFvuN3ypqovmmYra +MMJrfD2Ma1UlTMliA7IMQq7grE4eu0COSanj+HnQqweF63AlQl0TBaIpnAFeEg0W +Ow4ZSUzlclPQJG0YJ0XLgZerdDizwbt5cr7Fowbro1Z4VcAUaZ/vZa5Ux3Cg2FwY +QAz2Qq7cZgd3uksThQK9WngS9iH4Skgpa5jdQyK28VgouKjw4AqLpEpTw6ixQ1cb +B0Cr1Wfa8c3px5wgS21eJZ0XZqMbu8tOagXPRQIXazAcHC9BJHdQFXvOyF6AmzCk +1g13R83Q9bmaqMgmmHUXeTqqgICgsSSoVY33K743t19O27a+ghbWxjP7KyKA4lET +2GleQ0gcPus5frGSUFIptnogHqiTw+LLMtqLw0L6TeoFeKJOFtj4+Tg6lbdwUPTZ +/S9XM+7B1j7zwj6/mRgXNmmnICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9 +Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-768-expanded.priv b/ml-kem/tests/examples/ML-KEM-768-expanded.priv new file mode 100644 index 0000000..9b2f13c --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-768-expanded.priv @@ -0,0 +1,53 @@ +-----BEGIN PRIVATE KEY----- +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 +JCUmJygpKissLS4vMDEyMzQ1Njc4OTo7PD0+Pw== +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-768-seed.priv b/ml-kem/tests/examples/ML-KEM-768-seed.priv new file mode 100644 index 0000000..4e7d5a0 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-768-seed.priv @@ -0,0 +1,4 @@ +-----BEGIN PRIVATE KEY----- +MFQCAQAwCwYJYIZIAWUDBAQCBEKAQAABAgMEBQYHCAkKCwwNDg8QERITFBUWFxgZ +GhscHR4fICEiIyQlJicoKSorLC0uLzAxMjM0NTY3ODk6Ozw9Pj8= +-----END PRIVATE KEY----- diff --git a/ml-kem/tests/examples/ML-KEM-768.pub b/ml-kem/tests/examples/ML-KEM-768.pub new file mode 100644 index 0000000..36dddd6 --- /dev/null +++ b/ml-kem/tests/examples/ML-KEM-768.pub @@ -0,0 +1,28 @@ +-----BEGIN PUBLIC KEY----- +MIIEsjALBglghkgBZQMEBAIDggShACmKoQ1CPI3aBp0CvFnmzfA6CWuLPaTKubgM +pKFJB2cszvHsT68jSgvFt+nUc/KzEzs7JqHRdctnp4BZGWmcAvdlMbmcX4kYBwS7 +TKRTXFuJcmecZgoHxeUUuHAJyGLrj1FXaV77P8QKne9rgcHMAqJJrk8JStDZvTSF +wcHGgIBSCnyMYyAyzuc4FU5cUXbAfaVgJHdqQw/nbqz2ZaP3uDIQIhW8gvEJOcg1 +VwQzao+sHYHkuwSFql18dNa1m75cXpcqDYusQRtVtdVVfNaAoaj3G064a8SMmgUJ +cxpUvZ1ykLJ5Y+Q3Lcmxmc/crAsBrNKKYjlREuTENkjWIsSMgjTQFEDozDdskn8j +pa/JrAR0xmInTkJFJchVLs47P+JlFt6QG8fVFb3olVjmJslcgLkzQvgBAATznmxs +lIccXjRMqzlmyDX5qWpZr9McQChrOLHBp4RwurlHUYk0RTzoZzapGfH1ptUQqG9U +VPw5gMtcdlvSvV97NrFBDWY1yM60fE3aDXaijqyTnHHDAkgEhmxxYmZYRCFjwsIh +F+UKzvzmN4qYVlIwKk7wws4Mxxa3eW4ray43d9+hrD2iWaMbWptTD4y2OKgaYqww +GEmrr5WnMBvaMAaJCb/bfmfbzLs4pVUaJbGjoPaFdIrVdT2IgPABbGJ0hhZjhMVX +H+I2WQA2TQODEeLYdds2ZoaTK17GAkMKNp6Hpu9cM4eGZXglvUwFes65I+sJNeaQ +XmO0ztf4CFenc91ksVDSZhLqmsEgUtsgF78YQ8y0sygbaQ3HKK36hcACgbjjwJKH +M1+Fa0/CiS9povV5Ia2gGRTECYhmLVd2lmKnhjUbm2ZJPat5WU2YbeIQDWW6D/Tq +WLgVONJKRDWiWPrCVASqf0H2WLE4UGXhWNy2ARVzJyD0BFmqrBXkBpU6kKxSmX0c +zQcAYO/GXbnmUzVEZ/rVbscTyG51QMQjrPJmn1L6b0rGiI2HHvPoR8ApqKr7uS4X +skqgebH0GbphdbRCr7EZCdSla3CgM1soc5IYqnyTSOLDwvPrPRWkHmQXwN2Uv+sh +QZsxGnuxOhgLvoMyGKmmsXRHzIXyJYWVh6cwdwSay8/UTQ8CVDjhXRU4Jw1Ybhv4 +MZKpRZz2PA6XL4UpdnmDHs8SFQmFHLg0D28Qew+hoO/Rs2qBibwIXE9ct4TlU/Qb +kY+AOXzhlW94W+43fKmqi+aZitowwmt8PYxrVSVMyWIDsgxCruCsTh67QI5JqeP4 +edCrB4XrcCVCXRMFoimcAV4SDRY7DhlJTOVyU9AkbRgnRcuBl6t0OLPBu3lyvsWj +BuujVnhVwBRpn+9lrlTHcKDYXBhADPZCrtxmB3e6SxOFAr1aeBL2IfhKSClrmN1D +IrbxWCi4qPDgCoukSlPDqLFDVxsHQKvVZ9rxzenHnCBLbV4lnRdmoxu7y05qBc9F +AhdrMBwcL0Ekd1AVe87IXoCbMKTWDXdHzdD1uZqoyCaYdRd5OqqAgKCxJKhVjfcr +vje3X07btr6CFtbGM/srIoDiURPYaV5DSBw+6zl+sZJQUim2eiAeqJPD4ssy2ovD +QvpN6gV4 +-----END PUBLIC KEY----- From e8296e33017a17bb75dca532cb3bcdf1a75a4e2a Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Mon, 15 Sep 2025 11:33:57 +0200 Subject: [PATCH 6/9] ml-kem(API): PrivateKey* struct should only be internal APIs --- ml-kem/src/kem.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index 4805a19..a2f85ae 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -276,7 +276,7 @@ where /// while the expandedKey and both formats vary in size by security level” #[cfg(feature = "pkcs8")] #[derive(Clone, Debug, pkcs8::der::Choice)] -pub enum PrivateKeyChoice<'o> { +pub(crate) enum PrivateKeyChoice<'o> { /// FIPS 203 format for an ML-KEM private key: a 64-octet seed #[asn1(tag_mode = "IMPLICIT", context_specific = "0")] Seed(OctetStringRef<'o>), @@ -289,7 +289,7 @@ pub enum PrivateKeyChoice<'o> { /// The private key's `Both` variant contains the seed as well as the expanded key. #[cfg(feature = "pkcs8")] #[derive(Clone, Debug, pkcs8::der::Sequence)] -pub struct PrivateKeyBothChoice<'o> { +pub(crate) struct PrivateKeyBothChoice<'o> { /// FIPS 203 format for an ML-KEM private key: a 64-octet seed pub seed: OctetStringRef<'o>, /// FIPS 203 format for an ML-KEM private key: the decapsulation key resulting from PKE's `KeyGen` operation From f051eeef4ef4a72560f725bb092ee0072bf4da12 Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Mon, 15 Sep 2025 11:37:05 +0200 Subject: [PATCH 7/9] ml-kem: group use statements by one unique cfg condition --- ml-kem/src/kem.rs | 18 ++++++++---------- ml-kem/src/lib.rs | 21 ++++++++++----------- 2 files changed, 18 insertions(+), 21 deletions(-) diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index a2f85ae..758c094 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -18,18 +18,16 @@ pub use ::kem::{Decapsulate, Encapsulate}; /// A shared key resulting from an ML-KEM transaction pub(crate) type SharedKey = B32; -#[cfg(feature = "pkcs8")] -use hybrid_array::Array; -#[cfg(feature = "pkcs8")] -use pkcs8::der::AnyRef; #[cfg(all(feature = "pkcs8", feature = "alloc"))] -use pkcs8::der::Encode; -#[cfg(all(feature = "pkcs8", feature = "alloc"))] -use pkcs8::der::asn1::BitStringRef; -#[cfg(feature = "pkcs8")] -use pkcs8::der::asn1::OctetStringRef; +use pkcs8::der::{Encode, asn1::BitStringRef}; #[cfg(feature = "pkcs8")] -use pkcs8::spki::AssociatedAlgorithmIdentifier; +use { + hybrid_array::Array, + pkcs8::{ + der::{AnyRef, asn1::OctetStringRef}, + spki::AssociatedAlgorithmIdentifier, + }, +}; /// A `DecapsulationKey` provides the ability to generate a new key pair, and decapsulate an /// encapsulated shared key. diff --git a/ml-kem/src/lib.rs b/ml-kem/src/lib.rs index af94d60..484010a 100644 --- a/ml-kem/src/lib.rs +++ b/ml-kem/src/lib.rs @@ -81,10 +81,7 @@ pub use util::B32; pub use param::{ArraySize, ParameterSet}; #[cfg(feature = "pkcs8")] -pub use pkcs8; - -#[cfg(feature = "pkcs8")] -use pkcs8::AssociatedOid; +pub use pkcs8::{self, AssociatedOid}; /// An object that knows what size it is pub trait EncodedSizeUser { @@ -269,13 +266,15 @@ mod test { #[cfg(all(feature = "pkcs8", feature = "alloc", feature = "pem"))] use crate::kem::PrivateKeyBothChoice; #[cfg(all(feature = "pkcs8", feature = "alloc"))] - use crate::kem::PrivateKeyChoice; - #[cfg(all(feature = "pkcs8", feature = "alloc"))] - use pkcs8::der::{self, Decode}; - #[cfg(all(feature = "pkcs8", feature = "alloc"))] - use pkcs8::{ - DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, PrivateKeyInfoRef, - SubjectPublicKeyInfoRef, + use { + crate::kem::PrivateKeyChoice, + pkcs8::{ + der::{self, Decode}, + { + DecodePrivateKey, DecodePublicKey, EncodePrivateKey, EncodePublicKey, + PrivateKeyInfoRef, SubjectPublicKeyInfoRef, + }, + }, }; fn round_trip_test() From c105ec9b632dcfb38209d20d5f52832f44a8f01d Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Mon, 15 Sep 2025 11:59:12 +0200 Subject: [PATCH 8/9] ml-kem: cargo update for PKCS#8-specific crates --- Cargo.lock | 32 ++++++++++++++++++++++++++++++-- 1 file changed, 30 insertions(+), 2 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 5005403..2b7b342 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -55,6 +55,12 @@ version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d8b59d472eab27ade8d770dcb11da7201c11234bef9f82ce7aa517be028d462b" +[[package]] +name = "base64ct" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "55248b47b0caf0546f7988906588779981c43bb1bc9d0c44087278f80cdb44ba" + [[package]] name = "bincode" version = "1.3.3" @@ -243,9 +249,9 @@ checksum = "67ba02a97a2bd10f4b59b25c7973101c79642302776489e030cd13cdab09ed15" [[package]] name = "const-oid" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1cb3c4a0d3776f7535c32793be81d6d5fec0d48ac70955d9834e643aa249a52f" +checksum = "0dabb6555f92fb9ee4140454eb5dcd14c7960e1225c6d1a6cc361f032947713e" [[package]] name = "cpufeatures" @@ -867,6 +873,7 @@ checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" name = "ml-kem" version = "0.3.0-pre" dependencies = [ + "const-oid", "criterion", "crypto-common", "der", @@ -875,6 +882,7 @@ dependencies = [ "hybrid-array", "kem", "num-rational", + "pkcs8", "rand", "rand_core", "serde", @@ -1008,6 +1016,16 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "pkcs8" +version = "0.11.0-rc.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c53e5d0804fa4070b1b2a5b320102f2c1c094920a7533d5d87c2630609bcbd34" +dependencies = [ + "der", + "spki", +] + [[package]] name = "pkg-config" version = "0.3.32" @@ -1463,6 +1481,16 @@ dependencies = [ "lock_api", ] +[[package]] +name = "spki" +version = "0.8.0-rc.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8baeff88f34ed0691978ec34440140e1572b68c7dd4a495fd14a3dc1944daa80" +dependencies = [ + "base64ct", + "der", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" From 93dbb57a4a418653ddbcd411516912aaccb17141 Mon Sep 17 00:00:00 2001 From: Lukas PROKOP Date: Mon, 15 Sep 2025 12:12:47 +0200 Subject: [PATCH 9/9] ml-kem: implementation simplifications --- ml-kem/src/kem.rs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ml-kem/src/kem.rs b/ml-kem/src/kem.rs index 758c094..3551f1f 100644 --- a/ml-kem/src/kem.rs +++ b/ml-kem/src/kem.rs @@ -408,17 +408,16 @@ where let seed_to_key = |seed: OctetStringRef<'_>| -> Result, Self::Error> { let (head, tail) = seed.as_bytes().split_at(32); - let d: &B32 = &B32::try_from_iter(head.iter().copied()).unwrap(); - let z: &B32 = &B32::try_from_iter(tail.iter().copied()).unwrap(); - Ok(DecapsulationKey::generate_deterministic(d, z)) + let d: &B32 = head.try_into().map_err(|_| pkcs8::Error::KeyMalformed)?; + let z: &B32 = tail.try_into().map_err(|_| pkcs8::Error::KeyMalformed)?; + Ok(Self::generate_deterministic(d, z)) }; let expanded_to_key = |expanded: OctetStringRef<'_>| -> Result, Self::Error> { let bytes = expanded.as_bytes(); - let buffer = Array::::from_fn(|idx| bytes[idx]); let array = - Encoded::::try_from(buffer).map_err(|_| pkcs8::Error::KeyMalformed)?; + Encoded::::try_from(bytes).map_err(|_| pkcs8::Error::KeyMalformed)?; Ok(Self::from_bytes(&array)) };