From e0bc169c3dedbc45f457503223a496d364317c0c Mon Sep 17 00:00:00 2001 From: Sean McGrail Date: Thu, 13 Mar 2025 21:00:25 +0000 Subject: [PATCH] Randomness for cryptographic values should come from the cryptographic provider --- .../hazmat/bindings/_rust/openssl/rand.pyi | 5 ++++ src/rust/src/backend/aead.rs | 29 ++++++++++--------- src/rust/src/backend/mod.rs | 1 + src/rust/src/backend/rand.rs | 19 ++++++++++++ src/rust/src/lib.rs | 2 ++ src/rust/src/pkcs12.rs | 22 ++++---------- src/rust/src/pkcs7.rs | 6 ++-- src/rust/src/types.rs | 1 - 8 files changed, 50 insertions(+), 35 deletions(-) create mode 100644 src/cryptography/hazmat/bindings/_rust/openssl/rand.pyi create mode 100644 src/rust/src/backend/rand.rs diff --git a/src/cryptography/hazmat/bindings/_rust/openssl/rand.pyi b/src/cryptography/hazmat/bindings/_rust/openssl/rand.pyi new file mode 100644 index 000000000000..38c76e623f6e --- /dev/null +++ b/src/cryptography/hazmat/bindings/_rust/openssl/rand.pyi @@ -0,0 +1,5 @@ +# This file is dual licensed under the terms of the Apache License, Version +# 2.0, and the BSD License. See the LICENSE file in the root of this repository +# for complete details. + +def get_rand_bytes(size: int) -> bytes: ... diff --git a/src/rust/src/backend/aead.rs b/src/rust/src/backend/aead.rs index 10f9a0813972..2d279c779d14 100644 --- a/src/rust/src/backend/aead.rs +++ b/src/rust/src/backend/aead.rs @@ -6,7 +6,7 @@ use pyo3::types::{PyAnyMethods, PyListMethods}; use crate::buf::CffiBuf; use crate::error::{CryptographyError, CryptographyResult}; -use crate::{exceptions, types}; +use crate::exceptions; fn check_length(data: &[u8]) -> CryptographyResult<()> { if data.len() > (i32::MAX as usize) { @@ -533,8 +533,10 @@ impl ChaCha20Poly1305 { } #[staticmethod] - fn generate_key(py: pyo3::Python<'_>) -> CryptographyResult> { - Ok(types::OS_URANDOM.get(py)?.call1((32,))?) + fn generate_key( + py: pyo3::Python<'_>, + ) -> CryptographyResult> { + crate::backend::rand::get_rand_bytes(py, 32) } #[pyo3(signature = (nonce, data, associated_data))] @@ -644,14 +646,14 @@ impl AesGcm { fn generate_key( py: pyo3::Python<'_>, bit_length: usize, - ) -> CryptographyResult> { + ) -> CryptographyResult> { if bit_length != 128 && bit_length != 192 && bit_length != 256 { return Err(CryptographyError::from( pyo3::exceptions::PyValueError::new_err("bit_length must be 128, 192, or 256"), )); } - Ok(types::OS_URANDOM.get(py)?.call1((bit_length / 8,))?) + crate::backend::rand::get_rand_bytes(py, bit_length / 8) } #[pyo3(signature = (nonce, data, associated_data))] @@ -760,14 +762,13 @@ impl AesCcm { fn generate_key( py: pyo3::Python<'_>, bit_length: usize, - ) -> CryptographyResult> { + ) -> CryptographyResult> { if bit_length != 128 && bit_length != 192 && bit_length != 256 { return Err(CryptographyError::from( pyo3::exceptions::PyValueError::new_err("bit_length must be 128, 192, or 256"), )); } - - Ok(types::OS_URANDOM.get(py)?.call1((bit_length / 8,))?) + crate::backend::rand::get_rand_bytes(py, bit_length / 8) } #[pyo3(signature = (nonce, data, associated_data))] @@ -896,14 +897,14 @@ impl AesSiv { fn generate_key( py: pyo3::Python<'_>, bit_length: usize, - ) -> CryptographyResult> { + ) -> CryptographyResult> { if bit_length != 256 && bit_length != 384 && bit_length != 512 { return Err(CryptographyError::from( pyo3::exceptions::PyValueError::new_err("bit_length must be 256, 384, or 512"), )); } - Ok(types::OS_URANDOM.get(py)?.call1((bit_length / 8,))?) + crate::backend::rand::get_rand_bytes(py, bit_length / 8) } #[pyo3(signature = (data, associated_data))] @@ -994,14 +995,14 @@ impl AesOcb3 { fn generate_key( py: pyo3::Python<'_>, bit_length: usize, - ) -> CryptographyResult> { + ) -> CryptographyResult> { if bit_length != 128 && bit_length != 192 && bit_length != 256 { return Err(CryptographyError::from( pyo3::exceptions::PyValueError::new_err("bit_length must be 128, 192, or 256"), )); } - Ok(types::OS_URANDOM.get(py)?.call1((bit_length / 8,))?) + crate::backend::rand::get_rand_bytes(py, bit_length / 8) } #[pyo3(signature = (nonce, data, associated_data))] @@ -1121,14 +1122,14 @@ impl AesGcmSiv { fn generate_key( py: pyo3::Python<'_>, bit_length: usize, - ) -> CryptographyResult> { + ) -> CryptographyResult> { if bit_length != 128 && bit_length != 192 && bit_length != 256 { return Err(CryptographyError::from( pyo3::exceptions::PyValueError::new_err("bit_length must be 128, 192, or 256"), )); } - Ok(types::OS_URANDOM.get(py)?.call1((bit_length / 8,))?) + crate::backend::rand::get_rand_bytes(py, bit_length / 8) } #[pyo3(signature = (nonce, data, associated_data))] diff --git a/src/rust/src/backend/mod.rs b/src/rust/src/backend/mod.rs index a447565d7229..c58a6301560c 100644 --- a/src/rust/src/backend/mod.rs +++ b/src/rust/src/backend/mod.rs @@ -17,6 +17,7 @@ pub(crate) mod hmac; pub(crate) mod kdf; pub(crate) mod keys; pub(crate) mod poly1305; +pub(crate) mod rand; pub(crate) mod rsa; pub(crate) mod utils; pub(crate) mod x25519; diff --git a/src/rust/src/backend/rand.rs b/src/rust/src/backend/rand.rs new file mode 100644 index 000000000000..3159fd54cd6a --- /dev/null +++ b/src/rust/src/backend/rand.rs @@ -0,0 +1,19 @@ +// This file is dual licensed under the terms of the Apache License, Version +// 2.0, and the BSD License. See the LICENSE file in the root of this repository +// for complete details. + +use crate::error::{CryptographyError, CryptographyResult}; + +#[pyo3::pyfunction] +pub(crate) fn get_rand_bytes( + py: pyo3::Python<'_>, + size: usize, +) -> CryptographyResult> { + Ok(pyo3::types::PyBytes::new_with(py, size, |b| { + #[cfg(any(CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL))] + openssl::rand::rand_bytes(b).map_err(CryptographyError::from)?; + #[cfg(not(any(CRYPTOGRAPHY_IS_LIBRESSL, CRYPTOGRAPHY_IS_BORINGSSL)))] + openssl::rand::rand_priv_bytes(b).map_err(CryptographyError::from)?; + Ok(()) + })?) +} diff --git a/src/rust/src/lib.rs b/src/rust/src/lib.rs index 989178ccec7b..8687b6e24866 100644 --- a/src/rust/src/lib.rs +++ b/src/rust/src/lib.rs @@ -191,6 +191,8 @@ mod _rust { #[pymodule_export] use crate::backend::poly1305::poly1305; #[pymodule_export] + use crate::backend::rand::get_rand_bytes; + #[pymodule_export] use crate::backend::rsa::rsa; #[pymodule_export] use crate::backend::x25519::x25519; diff --git a/src/rust/src/pkcs12.rs b/src/rust/src/pkcs12.rs index 00e6a759e2a2..d90d07cea76d 100644 --- a/src/rust/src/pkcs12.rs +++ b/src/rust/src/pkcs12.rs @@ -418,13 +418,9 @@ fn serialize_bags<'p>( if !plain_safebags.is_empty() { plain_safebag_contents = asn1::write_single(&asn1::SequenceOfWriter::new(plain_safebags))?; - auth_safe_salt = types::OS_URANDOM - .get(py)? - .call1((e.salt_length(),))? + auth_safe_salt = crate::backend::rand::get_rand_bytes(py, e.salt_length())? .extract::()?; - auth_safe_iv = types::OS_URANDOM - .get(py)? - .call1((16,))? + auth_safe_iv = crate::backend::rand::get_rand_bytes(py, 16)? .extract::()?; auth_safe_ciphertext = e.encrypt( py, @@ -475,10 +471,8 @@ fn serialize_bags<'p>( let auth_safe_content = asn1::write_single(&asn1::SequenceOfWriter::new(auth_safe_contents))?; - let salt = types::OS_URANDOM - .get(py)? - .call1((8,))? - .extract::()?; + let salt = + crate::backend::rand::get_rand_bytes(py, 8)?.extract::()?; let mac_algorithm_md = hashes::message_digest_from_algorithm(py, &encryption_details.mac_algorithm)?; let mac_key = cryptography_crypto::pkcs12::kdf( @@ -596,13 +590,9 @@ fn serialize_key_and_certificates<'p>( .extract::()?; let key_bag = if let Some(ref e) = encryption_details.encryption_algorithm { - key_salt = types::OS_URANDOM - .get(py)? - .call1((e.salt_length(),))? + key_salt = crate::backend::rand::get_rand_bytes(py, e.salt_length())? .extract::()?; - key_iv = types::OS_URANDOM - .get(py)? - .call1((16,))? + key_iv = crate::backend::rand::get_rand_bytes(py, 16)? .extract::()?; key_ciphertext = e.encrypt( py, diff --git a/src/rust/src/pkcs7.rs b/src/rust/src/pkcs7.rs index f2f41b95135d..1d90006d5a73 100644 --- a/src/rust/src/pkcs7.rs +++ b/src/rust/src/pkcs7.rs @@ -103,13 +103,11 @@ fn encrypt_and_serialize<'p>( // Get the content encryption algorithm let content_encryption_algorithm_type = content_encryption_algorithm; let key_size = content_encryption_algorithm_type.getattr(pyo3::intern!(py, "key_size"))?; - let key = types::OS_URANDOM - .get(py)? - .call1((key_size.floor_div(8)?,))?; + let key = crate::backend::rand::get_rand_bytes(py, key_size.floor_div(8)?.extract()?)?; let content_encryption_algorithm = content_encryption_algorithm_type.call1((&key,))?; // Get the mode - let iv = types::OS_URANDOM.get(py)?.call1((16,))?; + let iv = crate::backend::rand::get_rand_bytes(py, 16)?; let cbc_mode = types::CBC.get(py)?.call1((&iv,))?; let encrypted_content = symmetric_encrypt( diff --git a/src/rust/src/types.rs b/src/rust/src/types.rs index 3226e890e0fa..46b44ef312c3 100644 --- a/src/rust/src/types.rs +++ b/src/rust/src/types.rs @@ -37,7 +37,6 @@ pub static DATETIME_TIMEZONE_UTC: LazyPyImport = LazyPyImport::new("datetime", &["timezone", "utc"]); pub static IPADDRESS_IPADDRESS: LazyPyImport = LazyPyImport::new("ipaddress", &["ip_address"]); pub static IPADDRESS_IPNETWORK: LazyPyImport = LazyPyImport::new("ipaddress", &["ip_network"]); -pub static OS_URANDOM: LazyPyImport = LazyPyImport::new("os", &["urandom"]); pub static DEPRECATED_IN_36: LazyPyImport = LazyPyImport::new("cryptography.utils", &["DeprecatedIn36"]);