diff --git a/src/rust/cryptography-key-parsing/src/pkcs8.rs b/src/rust/cryptography-key-parsing/src/pkcs8.rs index 821a7dfa3733..412d7c0ce52b 100644 --- a/src/rust/cryptography-key-parsing/src/pkcs8.rs +++ b/src/rust/cryptography-key-parsing/src/pkcs8.rs @@ -48,14 +48,13 @@ pub enum MlDsaPrivateKey { /// Extract the ML-KEM seed from a private key. /// -/// For BoringSSL/AWS-LC, round-trips through PKCS#8 encoding to extract the -/// seed. AWS-LC's `raw_private_key()` returns the 2400-byte expanded key, not -/// the seed; since AWS-LC 1.72.0, `private_key_to_pkcs8()` produces RFC 9935 -/// seed-format PKCS#8 when the key was created from a seed. BoringSSL's -/// private key serialization also emits RFC 9935 seed-format PKCS#8. +/// For BoringSSL and OpenSSL 3.5+, calls the library's seed extraction API +/// directly (`EVP_PKEY_get_private_seed` / `PKey::seed_into`). /// -/// For vanilla OpenSSL 3.5+, calls `PKey::seed_into` to read the seed -/// directly, avoiding the PKCS#8 round-trip. +/// For AWS-LC, round-trips through PKCS#8 encoding because +/// `raw_private_key()` returns the 2400-byte expanded key. Since AWS-LC +/// 1.72.0, `private_key_to_pkcs8()` produces RFC 9935 seed-format PKCS#8 +/// when the key was created from a seed. #[cfg(any( CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC, @@ -65,11 +64,11 @@ pub fn mlkem_seed_from_pkey( pkey: &openssl::pkey::PKeyRef, ) -> Result { cfg_if::cfg_if! { - if #[cfg(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC))] { + if #[cfg(CRYPTOGRAPHY_IS_AWSLC)] { let pkcs8_der = pkey.private_key_to_pkcs8()?; let pki = asn1::parse_single::>(&pkcs8_der).unwrap(); Ok(asn1::parse_single::(pki.private_key).unwrap()) - } else if #[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] { + } else { let seed = cryptography_openssl::mlkem::mlkem_seed_raw(pkey)?; Ok(MlKemPrivateKey::Seed(seed)) } @@ -78,13 +77,12 @@ pub fn mlkem_seed_from_pkey( /// Extract the 32-byte ML-DSA seed from a private key. /// -/// For BoringSSL/AWS-LC, round-trips through PKCS#8 encoding to extract the -/// seed (AWS-LC's `raw_private_key()` returns the expanded key, not the seed: -/// https://github.com/aws/aws-lc/issues/3072). +/// For BoringSSL and OpenSSL 3.5+, calls the library's seed extraction API +/// directly (`EVP_PKEY_get_private_seed` / `PKey::seed_into`). /// -/// For vanilla OpenSSL 3.5+, calls `PKey::seed_into` to read the seed -/// directly, since OpenSSL 3.5's PKCS#8 inner encoding differs from -/// BoringSSL/AWS-LC. +/// For AWS-LC, round-trips through PKCS#8 encoding because +/// `raw_private_key()` returns the expanded key, not the seed +/// (https://github.com/aws/aws-lc/issues/3072). #[cfg(any( CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC, @@ -94,11 +92,11 @@ pub fn mldsa_seed_from_pkey( pkey: &openssl::pkey::PKeyRef, ) -> Result { cfg_if::cfg_if! { - if #[cfg(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_IS_AWSLC))] { + if #[cfg(CRYPTOGRAPHY_IS_AWSLC)] { let pkcs8_der = pkey.private_key_to_pkcs8()?; let pki = asn1::parse_single::>(&pkcs8_der).unwrap(); Ok(asn1::parse_single::(pki.private_key).unwrap()) - } else if #[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] { + } else { let seed = cryptography_openssl::mldsa::mldsa_seed_raw(pkey)?; Ok(MlDsaPrivateKey::Seed(seed)) } diff --git a/src/rust/cryptography-openssl/src/mldsa.rs b/src/rust/cryptography-openssl/src/mldsa.rs index 6c9d9c79c4e0..e2a3c9df9e27 100644 --- a/src/rust/cryptography-openssl/src/mldsa.rs +++ b/src/rust/cryptography-openssl/src/mldsa.rs @@ -206,12 +206,27 @@ extern "C" { /// /// Avoids the PKCS#8 round-trip that vanilla OpenSSL 3.5 encodes /// differently from BoringSSL/AWS-LC. -#[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] +#[cfg(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_OPENSSL_350_OR_GREATER))] pub fn mldsa_seed_raw( pkey: &openssl::pkey::PKeyRef, ) -> OpenSSLResult<[u8; 32]> { let mut seed = [0u8; 32]; - pkey.seed_into(&mut seed)?; + cfg_if::cfg_if! { + if #[cfg(CRYPTOGRAPHY_IS_BORINGSSL)] { + let mut seed_len = seed.len(); + // SAFETY: pkey is a valid EVP_PKEY and seed is a 32-byte buffer. + unsafe { + cvt(ffi::EVP_PKEY_get_private_seed( + pkey.as_ptr(), + seed.as_mut_ptr(), + &mut seed_len, + ))?; + } + assert_eq!(seed_len, 32); + } else if #[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] { + pkey.seed_into(&mut seed)?; + } + } Ok(seed) } diff --git a/src/rust/cryptography-openssl/src/mlkem.rs b/src/rust/cryptography-openssl/src/mlkem.rs index 6bb107c61599..f886997573f8 100644 --- a/src/rust/cryptography-openssl/src/mlkem.rs +++ b/src/rust/cryptography-openssl/src/mlkem.rs @@ -3,6 +3,8 @@ // for complete details. use foreign_types_shared::ForeignType; +#[cfg(CRYPTOGRAPHY_IS_BORINGSSL)] +use foreign_types_shared::ForeignTypeRef; use openssl_sys as ffi; #[cfg(CRYPTOGRAPHY_IS_AWSLC)] use std::os::raw::c_int; @@ -121,12 +123,27 @@ extern "C" { /// /// Avoids the PKCS#8 round-trip that vanilla OpenSSL 3.5 encodes /// differently from BoringSSL/AWS-LC. -#[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] +#[cfg(any(CRYPTOGRAPHY_IS_BORINGSSL, CRYPTOGRAPHY_OPENSSL_350_OR_GREATER))] pub fn mlkem_seed_raw( pkey: &openssl::pkey::PKeyRef, ) -> OpenSSLResult<[u8; 64]> { let mut seed = [0u8; 64]; - pkey.seed_into(&mut seed)?; + cfg_if::cfg_if! { + if #[cfg(CRYPTOGRAPHY_IS_BORINGSSL)] { + let mut seed_len = seed.len(); + // SAFETY: pkey is a valid EVP_PKEY and seed is a 64-byte buffer. + unsafe { + cvt(ffi::EVP_PKEY_get_private_seed( + pkey.as_ptr(), + seed.as_mut_ptr(), + &mut seed_len, + ))?; + } + assert_eq!(seed_len, 64); + } else if #[cfg(CRYPTOGRAPHY_OPENSSL_350_OR_GREATER)] { + pkey.seed_into(&mut seed)?; + } + } Ok(seed) }