crypto: CryptoProvider is-a SecureRandom -> has-a

In preparation for moving to a struct based model where
a `CryptoProvider` has a `&'static dyn SecureRandom` field, this commit
splits the `SecureRandom` trait from the `CryptoProvider` trait. In its
place `CryptoProvider` gets a `secure_random(&self)` fn that acts as
a stand-in for what will be a field in the struct based approach.
This commit is contained in:
Daniel McCarney 2023-11-28 14:57:31 -05:00
parent 53ed597fa1
commit 3b5cf17ade
11 changed files with 86 additions and 40 deletions

View File

@ -38,6 +38,10 @@ impl rustls::crypto::CryptoProvider for Provider {
fn signature_verification_algorithms(&self) -> rustls::crypto::WebPkiSupportedAlgorithms {
verify::ALGORITHMS
}
fn secure_random(&self) -> &'static dyn rustls::crypto::SecureRandom {
&Self
}
}
impl rustls::crypto::SecureRandom for Provider {

View File

@ -121,7 +121,7 @@ pub(super) fn start_handshake(
// we're doing an abbreviated handshake. See section 3.4 in
// RFC5077.
if !inner.ticket().is_empty() {
inner.session_id = SessionId::random(config.provider)?;
inner.session_id = SessionId::random(config.provider.secure_random())?;
}
session_id = Some(inner.session_id);
}
@ -137,10 +137,10 @@ pub(super) fn start_handshake(
Some(session_id) => session_id,
None if cx.common.is_quic() => SessionId::empty(),
None if !config.supports_version(ProtocolVersion::TLSv1_3) => SessionId::empty(),
None => SessionId::random(config.provider)?,
None => SessionId::random(config.provider.secure_random())?,
};
let random = Random::new(config.provider)?;
let random = Random::new(config.provider.secure_random())?;
Ok(emit_client_hello_for_retry(
transcript_buffer,

View File

@ -60,6 +60,10 @@ impl CryptoProvider for AwsLcRs {
fn signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms {
SUPPORTED_SIG_ALGS
}
fn secure_random(&self) -> &'static dyn SecureRandom {
&Self
}
}
impl SecureRandom for AwsLcRs {

View File

@ -100,11 +100,7 @@ pub use crate::msgs::handshake::KeyExchangeAlgorithm;
/// #[derive(Debug)]
/// struct HsmKeyLoader;
///
/// impl rustls::crypto::CryptoProvider for HsmKeyLoader {
/// fn fill_random(&self, buf: &mut [u8]) -> Result<(), rustls::crypto::GetRandomFailed> {
/// RING.fill(buf)
/// }
///
/// impl rustls::crypto::CryptoProvider for HsmKeyLoader {///
/// fn default_cipher_suites(&self) -> &'static [rustls::SupportedCipherSuite] {
/// RING.default_cipher_suites()
/// }
@ -120,6 +116,16 @@ pub use crate::msgs::handshake::KeyExchangeAlgorithm;
/// fn load_private_key(&self, key_der: pki_types::PrivateKeyDer<'static>) -> Result<Arc<dyn rustls::sign::SigningKey>, rustls::Error> {
/// fictious_hsm_api::load_private_key(key_der)
/// }
///
/// fn secure_random(&self) -> &'static dyn rustls::crypto::SecureRandom {
/// &HsmKeyLoader
/// }
/// }
///
/// impl rustls::crypto::SecureRandom for HsmKeyLoader {
/// fn fill(&self, buf: &mut [u8]) -> Result<(), rustls::crypto::GetRandomFailed> {
/// RING.secure_random().fill(buf)
/// }
/// }
/// # }
/// ```
@ -152,7 +158,7 @@ pub use crate::msgs::handshake::KeyExchangeAlgorithm;
/// [provider-example/]: https://github.com/rustls/rustls/tree/main/provider-example/
/// [rust-crypto]: https://github.com/rustcrypto
/// [dalek-cryptography]: https://github.com/dalek-cryptography
pub trait CryptoProvider: SecureRandom + Send + Sync + Debug + 'static {
pub trait CryptoProvider: Send + Sync + Debug + 'static {
/// Provide a safe set of cipher suites that can be used as the defaults.
///
/// This is used by [`crate::ConfigBuilder::with_safe_defaults()`] and
@ -197,6 +203,9 @@ pub trait CryptoProvider: SecureRandom + Send + Sync + Debug + 'static {
/// [`crate::server::WebPkiClientVerifier::builder_with_provider()`] and
/// [`crate::client::WebPkiServerVerifier::builder_with_provider()`].
fn signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms;
/// Return a source of cryptographically secure randomness.
fn secure_random(&self) -> &'static dyn SecureRandom;
}
/// A source of cryptographically secure randomness.

View File

@ -55,6 +55,10 @@ impl CryptoProvider for Ring {
fn signature_verification_algorithms(&self) -> WebPkiSupportedAlgorithms {
SUPPORTED_SIG_ALGS
}
fn secure_random(&self) -> &'static dyn SecureRandom {
&Self
}
}
impl SecureRandom for Ring {

View File

@ -2,7 +2,7 @@
#[cfg(feature = "tls12")]
use crate::crypto::ActiveKeyExchange;
use crate::crypto::CryptoProvider;
use crate::crypto::SecureRandom;
use crate::enums::{CipherSuite, HandshakeType, ProtocolVersion, SignatureScheme};
use crate::error::InvalidMessage;
#[cfg(feature = "logging")]
@ -97,11 +97,9 @@ impl Codec for Random {
}
impl Random {
pub(crate) fn new(
provider: &'static dyn CryptoProvider,
) -> Result<Self, rand::GetRandomFailed> {
pub(crate) fn new(secure_random: &dyn SecureRandom) -> Result<Self, rand::GetRandomFailed> {
let mut data = [0u8; 32];
provider.fill(&mut data)?;
secure_random.fill(&mut data)?;
Ok(Self(data))
}
}
@ -165,9 +163,9 @@ impl Codec for SessionId {
}
impl SessionId {
pub fn random(provider: &'static dyn CryptoProvider) -> Result<Self, rand::GetRandomFailed> {
pub fn random(secure_random: &dyn SecureRandom) -> Result<Self, rand::GetRandomFailed> {
let mut data = [0u8; 32];
provider.fill(&mut data)?;
secure_random.fill(&mut data)?;
Ok(Self { data, len: 32 })
}

View File

@ -1,24 +1,24 @@
//! The single place where we generate random material for our own use.
use crate::crypto::CryptoProvider;
use crate::crypto::SecureRandom;
use alloc::vec;
use alloc::vec::Vec;
/// Make a [`Vec<u8>`] of the given size containing random material.
pub(crate) fn random_vec(
provider: &dyn CryptoProvider,
secure_random: &dyn SecureRandom,
len: usize,
) -> Result<Vec<u8>, GetRandomFailed> {
let mut v = vec![0; len];
provider.fill(&mut v)?;
secure_random.fill(&mut v)?;
Ok(v)
}
/// Return a uniformly random [`u32`].
pub(crate) fn random_u32(provider: &dyn CryptoProvider) -> Result<u32, GetRandomFailed> {
pub(crate) fn random_u32(secure_random: &dyn SecureRandom) -> Result<u32, GetRandomFailed> {
let mut buf = [0u8; 4];
provider.fill(&mut buf)?;
secure_random.fill(&mut buf)?;
Ok(u32::from_be_bytes(buf))
}

View File

@ -384,8 +384,10 @@ impl ExpectClientHello {
};
// Save their Random.
let randoms =
ConnectionRandoms::new(client_hello.random, Random::new(self.config.provider)?);
let randoms = ConnectionRandoms::new(
client_hello.random,
Random::new(self.config.provider.secure_random())?,
);
match suite {
SupportedCipherSuite::Tls13(suite) => tls13::CompleteClientHelloHandling {
config: self.config,

View File

@ -209,7 +209,7 @@ mod client_hello {
if !self.config.session_storage.can_cache() {
self.session_id = SessionId::empty();
} else if self.session_id.is_empty() && !ticket_received {
self.session_id = SessionId::random(self.config.provider)?;
self.session_id = SessionId::random(self.config.provider.secure_random())?;
}
self.send_ticket = emit_server_hello(

View File

@ -1085,8 +1085,9 @@ impl ExpectFinished {
key_schedule: &KeyScheduleTraffic,
config: &ServerConfig,
) -> Result<(), Error> {
let nonce = rand::random_vec(config.provider, 32)?;
let age_add = rand::random_u32(config.provider)?;
let secure_random = config.provider.secure_random();
let nonce = rand::random_vec(secure_random, 32)?;
let age_add = rand::random_u32(secure_random)?;
let plain = get_server_session_value(
transcript,
suite,
@ -1106,7 +1107,7 @@ impl ExpectFinished {
};
(ticket, config.ticketer.lifetime())
} else {
let id = rand::random_vec(config.provider, 32)?;
let id = rand::random_vec(secure_random, 32)?;
let stored = config
.session_storage
.put(id.clone(), plain);

View File

@ -4104,7 +4104,10 @@ mod test_quic {
use rustls::{CipherSuite, HandshakeType, SignatureScheme};
let mut random = [0; 32];
PROVIDER.fill(&mut random).unwrap();
PROVIDER
.secure_random()
.fill(&mut random)
.unwrap();
let random = Random::from(random);
let rng = ring::rand::SystemRandom::new();
@ -4118,7 +4121,7 @@ mod test_quic {
payload: HandshakePayload::ClientHello(ClientHelloPayload {
client_version: ProtocolVersion::TLSv1_3,
random,
session_id: SessionId::random(PROVIDER).unwrap(),
session_id: SessionId::random(PROVIDER.secure_random()).unwrap(),
cipher_suites: vec![CipherSuite::TLS13_AES_128_GCM_SHA256],
compression_methods: vec![Compression::Null],
extensions: vec![
@ -4158,7 +4161,10 @@ mod test_quic {
use rustls::{CipherSuite, HandshakeType, SignatureScheme};
let mut random = [0; 32];
PROVIDER.fill(&mut random).unwrap();
PROVIDER
.secure_random()
.fill(&mut random)
.unwrap();
let random = Random::from(random);
let rng = ring::rand::SystemRandom::new();
@ -4179,7 +4185,7 @@ mod test_quic {
payload: HandshakePayload::ClientHello(ClientHelloPayload {
client_version: ProtocolVersion::TLSv1_2,
random,
session_id: SessionId::random(PROVIDER).unwrap(),
session_id: SessionId::random(PROVIDER.secure_random()).unwrap(),
cipher_suites: vec![CipherSuite::TLS13_AES_128_GCM_SHA256],
compression_methods: vec![Compression::Null],
extensions: vec![
@ -4575,7 +4581,7 @@ fn test_client_sends_helloretryrequest() {
#[test]
fn test_client_rejects_hrr_with_varied_session_id() {
use rustls::internal::msgs::handshake::SessionId;
let different_session_id = SessionId::random(PROVIDER).unwrap();
let different_session_id = SessionId::random(PROVIDER.secure_random()).unwrap();
let assert_client_sends_hello_with_secp384 = |msg: &mut Message| -> Altered {
if let MessagePayload::Handshake { parsed, encoded } = &mut msg.payload {
@ -5503,8 +5509,7 @@ fn test_explicit_provider_selection() {
struct FaultyRandomProvider {
parent: &'static dyn rustls::crypto::CryptoProvider,
// when empty, `fill_random` requests return `GetRandomFailed`
rand_queue: Mutex<&'static [u8]>,
random: &'static dyn rustls::crypto::SecureRandom,
}
impl rustls::crypto::CryptoProvider for FaultyRandomProvider {
@ -5527,9 +5532,19 @@ impl rustls::crypto::CryptoProvider for FaultyRandomProvider {
self.parent
.signature_verification_algorithms()
}
fn secure_random(&self) -> &'static dyn rustls::crypto::SecureRandom {
self.random
}
}
impl rustls::crypto::SecureRandom for FaultyRandomProvider {
#[derive(Debug)]
struct FaultyRandom {
// when empty, `fill_random` requests return `GetRandomFailed`
rand_queue: Mutex<&'static [u8]>,
}
impl rustls::crypto::SecureRandom for FaultyRandom {
fn fill(&self, output: &mut [u8]) -> Result<(), rustls::crypto::GetRandomFailed> {
let mut queue = self.rand_queue.lock().unwrap();
@ -5552,9 +5567,12 @@ impl rustls::crypto::SecureRandom for FaultyRandomProvider {
#[test]
fn test_client_construction_fails_if_random_source_fails_in_first_request() {
static FAULTY_RANDOM: FaultyRandom = FaultyRandom {
rand_queue: Mutex::new(b""),
};
static TEST_PROVIDER: FaultyRandomProvider = FaultyRandomProvider {
parent: PROVIDER,
rand_queue: Mutex::new(b""),
random: &FAULTY_RANDOM,
};
let client_config = finish_client_config(
@ -5570,9 +5588,12 @@ fn test_client_construction_fails_if_random_source_fails_in_first_request() {
#[test]
fn test_client_construction_fails_if_random_source_fails_in_second_request() {
static FAULTY_RANDOM: FaultyRandom = FaultyRandom {
rand_queue: Mutex::new(b"nice random number generator huh"),
};
static TEST_PROVIDER: FaultyRandomProvider = FaultyRandomProvider {
parent: PROVIDER,
rand_queue: Mutex::new(b"nice random number generator huh"),
random: &FAULTY_RANDOM,
};
let client_config = finish_client_config(
@ -5588,13 +5609,16 @@ fn test_client_construction_fails_if_random_source_fails_in_second_request() {
#[test]
fn test_client_construction_requires_64_bytes_of_random_material() {
static TEST_PROVIDER: FaultyRandomProvider = FaultyRandomProvider {
parent: PROVIDER,
static FAULTY_RANDOM: FaultyRandom = FaultyRandom {
rand_queue: Mutex::new(
b"nice random number generator !!!\
it's really not very good is it?",
it's really not very good is it?",
),
};
static TEST_PROVIDER: FaultyRandomProvider = FaultyRandomProvider {
parent: PROVIDER,
random: &FAULTY_RANDOM,
};
let client_config = finish_client_config(
KeyType::Rsa,