Skip to content

Randomness for cryptographic values should come from the cryptographic provider #12688

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions src/cryptography/hazmat/bindings/_rust/openssl/rand.pyi
Original file line number Diff line number Diff line change
@@ -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: ...
29 changes: 15 additions & 14 deletions src/rust/src/backend/aead.rs
Original file line number Diff line number Diff line change
@@ -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<pyo3::Bound<'_, pyo3::PyAny>> {
Ok(types::OS_URANDOM.get(py)?.call1((32,))?)
fn generate_key(
py: pyo3::Python<'_>,
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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<pyo3::Bound<'_, pyo3::PyAny>> {
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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<pyo3::Bound<'_, pyo3::PyAny>> {
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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<pyo3::Bound<'_, pyo3::PyAny>> {
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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<pyo3::Bound<'_, pyo3::PyAny>> {
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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<pyo3::Bound<'_, pyo3::PyAny>> {
) -> CryptographyResult<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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))]
1 change: 1 addition & 0 deletions src/rust/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -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;
19 changes: 19 additions & 0 deletions src/rust/src/backend/rand.rs
Original file line number Diff line number Diff line change
@@ -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<pyo3::Bound<'_, pyo3::types::PyBytes>> {
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)?;
Comment on lines +15 to +16
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I hate OpenSSL so much.

Ok(())
})?)
}
2 changes: 2 additions & 0 deletions src/rust/src/lib.rs
Original file line number Diff line number Diff line change
@@ -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;
22 changes: 6 additions & 16 deletions src/rust/src/pkcs12.rs
Original file line number Diff line number Diff line change
@@ -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::<pyo3::pybacked::PyBackedBytes>()?;
auth_safe_iv = types::OS_URANDOM
.get(py)?
.call1((16,))?
auth_safe_iv = crate::backend::rand::get_rand_bytes(py, 16)?
.extract::<pyo3::pybacked::PyBackedBytes>()?;
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::<pyo3::pybacked::PyBackedBytes>()?;
let salt =
crate::backend::rand::get_rand_bytes(py, 8)?.extract::<pyo3::pybacked::PyBackedBytes>()?;
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::<pyo3::pybacked::PyBackedBytes>()?;

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::<pyo3::pybacked::PyBackedBytes>()?;
key_iv = types::OS_URANDOM
.get(py)?
.call1((16,))?
key_iv = crate::backend::rand::get_rand_bytes(py, 16)?
.extract::<pyo3::pybacked::PyBackedBytes>()?;
key_ciphertext = e.encrypt(
py,
6 changes: 2 additions & 4 deletions src/rust/src/pkcs7.rs
Original file line number Diff line number Diff line change
@@ -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(
1 change: 0 additions & 1 deletion src/rust/src/types.rs
Original file line number Diff line number Diff line change
@@ -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"]);