Skip to content

Commit c7de20f

Browse files
Modularize rustls as work towards providers
`rustls`'s architecture is very clean and trait-driven. There are many providers for `rustls` including the built-in `aws-lc-rs` and `ring` as well as backends for `boringssl`, `graviola`, `openssl`, `mbedtls`, etc. This commit removes the hard dependency on `aws-lc-rs` and adds support for `ring`. It works towards RustPython#7059 as well.
1 parent 11e991f commit c7de20f

6 files changed

Lines changed: 62 additions & 30 deletions

File tree

‎crates/stdlib/Cargo.toml‎

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,15 @@ threading = ["rustpython-common/threading", "rustpython-vm/threading"]
1818
sqlite = ["dep:libsqlite3-sys"]
1919
# SSL backends - default to rustls
2020
ssl = []
21-
ssl-rustls = ["ssl", "rustls", "rustls-native-certs", "rustls-pemfile", "rustls-platform-verifier", "x509-cert", "x509-parser", "der", "pem-rfc7468", "webpki-roots", "aws-lc-rs", "oid-registry", "pkcs8"]
22-
ssl-rustls-fips = ["ssl-rustls", "aws-lc-rs/fips"]
21+
ssl-rustls = ["__ssl-rustls", "rustls/aws_lc_rs", "aws-lc-rs"]
22+
ssl-rustls-fips = ["__ssl-rustls", "rustls/aws_lc_rs", "aws-lc-rs", "aws-lc-rs/fips"]
2323
ssl-openssl = ["ssl", "openssl", "openssl-sys", "foreign-types-shared", "openssl-probe"]
2424
ssl-vendor = ["ssl-openssl", "openssl/vendored"]
2525
tkinter = ["dep:tk-sys", "dep:tcl-sys", "dep:widestring"]
2626
flame-it = ["flame"]
2727

28+
__ssl-rustls = ["ssl", "rustls", "rustls-native-certs", "rustls-pemfile", "rustls-platform-verifier", "x509-cert", "x509-parser", "der", "pem-rfc7468", "webpki-roots", "oid-registry", "pkcs8"]
29+
2830
[dependencies]
2931
# rustpython crates
3032
rustpython-derive = { workspace = true }
@@ -122,7 +124,7 @@ openssl-probe = { workspace = true, optional = true }
122124
foreign-types-shared = { workspace = true, optional = true }
123125

124126
# Rustls dependencies (optional, for ssl-rustls feature)
125-
rustls = { workspace = true, default-features = false, features = ["std", "tls12", "aws_lc_rs"], optional = true }
127+
rustls = { workspace = true, default-features = false, features = ["std", "tls12", ], optional = true }
126128
rustls-native-certs = { workspace = true, optional = true }
127129
rustls-pemfile = { workspace = true, optional = true }
128130
rustls-platform-verifier = { workspace = true, optional = true }

‎crates/stdlib/src/lib.rs‎

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -128,12 +128,16 @@ mod openssl;
128128
#[cfg(all(
129129
feature = "host_env",
130130
not(target_arch = "wasm32"),
131-
feature = "ssl-rustls"
131+
feature = "__ssl-rustls"
132132
))]
133133
mod ssl;
134134

135135
#[cfg(all(feature = "ssl-openssl", feature = "ssl-rustls", not(clippy)))]
136136
compile_error!(r#"features "ssl-openssl" and "ssl-rustls" are mutually exclusive"#);
137+
#[cfg(all(feature = "ssl-rustls-aws-lc-rs", feature = "ssl-rustls-ring"))]
138+
compile_error!(
139+
"features \"ssl-rustls-aws-lc-rs\" (also enabled transitively by \"ssl-rustls-fips\") and \"ssl-rustls-ring\" are mutually exclusive; pick exactly one rustls crypto backend"
140+
);
137141

138142
#[cfg(all(
139143
feature = "host_env",
@@ -246,7 +250,7 @@ pub fn stdlib_module_defs(ctx: &Context) -> Vec<&'static builtins::PyModuleDef>
246250
#[cfg(all(
247251
feature = "host_env",
248252
not(target_arch = "wasm32"),
249-
feature = "ssl-rustls"
253+
feature = "__ssl-rustls"
250254
))]
251255
ssl::module_def(ctx),
252256
statistics::module_def(ctx),

‎crates/stdlib/src/ssl.rs‎

Lines changed: 26 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ mod _ssl {
3737
lock::{PyMutex, PyRwLock},
3838
},
3939
socket::{PySocket, SelectKind, sock_select, timeout_error_msg},
40+
ssl::compat,
4041
vm::{
4142
AsObject, Py, PyObject, PyObjectRef, PyPayload, PyRef, PyResult, TryFromObject,
4243
VirtualMachine,
@@ -64,7 +65,6 @@ mod _ssl {
6465
sync::atomic::{AtomicUsize, Ordering},
6566
time::Duration,
6667
};
67-
use rustls::crypto::aws_lc_rs::ALL_CIPHER_SUITES;
6868
use std::{
6969
collections::{HashMap, hash_map::DefaultHasher},
7070
io::BufRead,
@@ -77,14 +77,23 @@ mod _ssl {
7777
use rustls::{
7878
ClientConfig, ClientConnection, RootCertStore, ServerConfig, ServerConnection,
7979
client::{ClientSessionMemoryCache, ClientSessionStore},
80-
crypto::SupportedKxGroup,
80+
crypto::{CryptoProvider, SupportedKxGroup},
8181
pki_types::{CertificateDer, CertificateRevocationListDer, PrivateKeyDer, ServerName},
8282
server::{ClientHello, ResolvesServerCert},
8383
sign::CertifiedKey,
8484
version::{TLS12, TLS13},
8585
};
8686
use sha2::{Digest, Sha256};
8787

88+
#[cfg(all(feature = "ssl-rustls-aws-lc-rs", feature = "ssl-rustls-ring"))]
89+
compile_error!("Enable only one rustls provider: ssl-rustls-aws-lc-rs or ssl-rustls-ring");
90+
91+
#[cfg(feature = "ssl-rustls-aws-lc-rs")]
92+
use rustls::crypto::aws_lc_rs::{ALL_CIPHER_SUITES, Ticketer, sign};
93+
94+
#[cfg(feature = "ssl-rustls-ring")]
95+
use rustls::crypto::ring::{ALL_CIPHER_SUITES, Ticketer, sign};
96+
8897
// Import certificate operations module
8998
use super::cert;
9099

@@ -1189,15 +1198,14 @@ mod _ssl {
11891198
}
11901199

11911200
// Additional validation: Create CertifiedKey to ensure rustls accepts it
1192-
let signing_key =
1193-
rustls::crypto::aws_lc_rs::sign::any_supported_type(&key).map_err(|_| {
1194-
vm.new_os_subtype_error(
1195-
PySSLError::class(&vm.ctx).to_owned(),
1196-
None,
1197-
"[SSL: KEY_VALUES_MISMATCH] key values mismatch",
1198-
)
1199-
.upcast()
1200-
})?;
1201+
let signing_key = sign::any_supported_type(&key).map_err(|_| {
1202+
vm.new_os_subtype_error(
1203+
PySSLError::class(&vm.ctx).to_owned(),
1204+
None,
1205+
"[SSL: KEY_VALUES_MISMATCH] key values mismatch",
1206+
)
1207+
.upcast()
1208+
})?;
12011209

12021210
let certified_key = CertifiedKey::new(full_chain.clone(), signing_key);
12031211
if certified_key.keys_match().is_err() {
@@ -2295,7 +2303,7 @@ mod _ssl {
22952303
rustls_server_session_store: rustls::server::ServerSessionMemoryCache::new(
22962304
SSL_SESSION_CACHE_SIZE,
22972305
),
2298-
server_ticketer: rustls::crypto::aws_lc_rs::Ticketer::new()
2306+
server_ticketer: Ticketer::new()
22992307
.expect("Failed to create shared ticketer for TLS 1.2 session resumption"),
23002308
accept_count: AtomicUsize::new(0),
23012309
session_hits: AtomicUsize::new(0),
@@ -4846,17 +4854,20 @@ mod _ssl {
48464854

48474855
#[pyfunction]
48484856
fn RAND_bytes(n: i64, vm: &VirtualMachine) -> PyResult<PyBytesRef> {
4849-
use aws_lc_rs::rand::{SecureRandom, SystemRandom};
4857+
compat::ensure_default_provider();
4858+
let default_provider =
4859+
CryptoProvider::get_default().expect("A CryptoProvider should have been set earlier");
48504860

48514861
// Validate n is not negative
48524862
if n < 0 {
48534863
return Err(vm.new_value_error("num must be positive"));
48544864
}
48554865

48564866
let n_usize = n as usize;
4857-
let rng = SystemRandom::new();
48584867
let mut buf = vec![0u8; n_usize];
4859-
rng.fill(&mut buf)
4868+
default_provider
4869+
.secure_random
4870+
.fill(&mut buf)
48604871
.map_err(|_| vm.new_os_error("Failed to generate random bytes"))?;
48614872
Ok(PyBytesRef::from(vm.ctx.new_bytes(buf)))
48624873
}

‎crates/stdlib/src/ssl/cert.rs‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ use x509_parser::prelude::*;
2424

2525
use super::compat::{VERIFY_X509_PARTIAL_CHAIN, VERIFY_X509_STRICT};
2626

27+
#[cfg(feature = "ssl-rustls-aws-lc-rs")]
28+
use rustls::crypto::aws_lc_rs::sign;
29+
30+
#[cfg(feature = "ssl-rustls-ring")]
31+
use rustls::crypto::ring::sign;
32+
2733
// Certificate Verification Constants
2834

2935
/// All supported signature schemes for certificate verification
@@ -1268,9 +1274,7 @@ pub(super) fn validate_cert_key_match(
12681274

12691275
// For rustls, the actual validation happens when creating CertifiedKey
12701276
// We can attempt to create a signing key to verify the key is valid
1271-
use rustls::crypto::aws_lc_rs::sign::any_supported_type;
1272-
1273-
match any_supported_type(private_key) {
1277+
match sign::any_supported_type(private_key) {
12741278
Ok(_signing_key) => {
12751279
// If we can create a signing key, the private key is valid
12761280
// Rustls will validate the cert-key match when building config

‎crates/stdlib/src/ssl/compat.rs‎

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -63,11 +63,17 @@ pub(super) const VERIFY_X509_PARTIAL_CHAIN: i32 = 0x80000;
6363
/// happens exactly once, even if called from multiple threads.
6464
static INIT_PROVIDER: Once = Once::new();
6565

66-
fn ensure_default_provider() {
66+
pub(super) fn ensure_default_provider() {
6767
INIT_PROVIDER.call_once(|| {
68+
#[cfg(feature = "ssl-rustls-aws-lc-rs")]
6869
let _ = rustls::crypto::CryptoProvider::install_default(
6970
rustls::crypto::aws_lc_rs::default_provider(),
7071
);
72+
73+
#[cfg(feature = "ssl-rustls-ring")]
74+
let _ = rustls::crypto::CryptoProvider::install_default(
75+
rustls::crypto::ring::default_provider(),
76+
);
7177
});
7278
}
7379

@@ -669,12 +675,12 @@ fn create_custom_crypto_provider(
669675
cipher_suites: Option<Vec<rustls::SupportedCipherSuite>>,
670676
kx_groups: Option<Vec<&'static dyn rustls::crypto::SupportedKxGroup>>,
671677
) -> Arc<rustls::crypto::CryptoProvider> {
672-
use rustls::crypto::aws_lc_rs::{ALL_CIPHER_SUITES, ALL_KX_GROUPS};
673-
let default_provider = rustls::crypto::aws_lc_rs::default_provider();
678+
let default_provider = rustls::crypto::CryptoProvider::get_default()
679+
.expect("A CryptoProvider should have been set earlier");
674680

675681
Arc::new(rustls::crypto::CryptoProvider {
676-
cipher_suites: cipher_suites.unwrap_or_else(|| ALL_CIPHER_SUITES.to_vec()),
677-
kx_groups: kx_groups.unwrap_or_else(|| ALL_KX_GROUPS.to_vec()),
682+
cipher_suites: cipher_suites.unwrap_or_else(|| default_provider.cipher_suites.clone()),
683+
kx_groups: kx_groups.unwrap_or_else(|| default_provider.kx_groups.clone()),
678684
signature_verification_algorithms: default_provider.signature_verification_algorithms,
679685
secure_random: default_provider.secure_random,
680686
key_provider: default_provider.key_provider,
@@ -2367,7 +2373,8 @@ pub(super) fn curve_name_to_kx_group(
23672373
curve: &str,
23682374
) -> Result<Vec<&'static dyn SupportedKxGroup>, String> {
23692375
// Get the default crypto provider's key exchange groups
2370-
let provider = rustls::crypto::aws_lc_rs::default_provider();
2376+
let provider = rustls::crypto::CryptoProvider::get_default()
2377+
.expect("A CryptoProvider should have been set earlier");
23712378
let all_groups = &provider.kx_groups;
23722379

23732380
match curve {

‎src/lib.rs‎

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,11 @@ pub use shell::run_shell;
7171

7272
#[cfg(all(
7373
feature = "ssl",
74-
not(any(feature = "ssl-rustls", feature = "ssl-openssl"))
74+
not(any(
75+
feature = "ssl-rustls",
76+
feature = "ssl-rustls-ring",
77+
feature = "ssl-openssl"
78+
))
7579
))]
7680
compile_error!(
7781
"Feature \"ssl\" is now enabled by either \"ssl-rustls\" or \"ssl-openssl\" to be enabled. Do not manually pass \"ssl\" feature. To enable ssl-openssl, use --no-default-features to disable ssl-rustls"

0 commit comments

Comments
 (0)