diff --git a/Cargo.lock b/Cargo.lock index bb86c0f..bf6dc2a 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -435,7 +435,7 @@ checksum = "f87165f0995f63a9fbeea62b64d10b4d9d8e78ec6d7d51fb2125fda7bb36788f" [[package]] name = "rustls-symcrypt" -version = "0.2.2" +version = "0.2.3" dependencies = [ "der 0.8.0", "once_cell", diff --git a/Cargo.toml b/Cargo.toml index 181e269..1775af9 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -7,4 +7,4 @@ resolver = "2" [workspace.package] edition = "2021" -rust-version = "1.64.0" +rust-version = "1.70.0" diff --git a/rustls-symcrypt/Cargo.toml b/rustls-symcrypt/Cargo.toml index 442a205..d42e827 100644 --- a/rustls-symcrypt/Cargo.toml +++ b/rustls-symcrypt/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "rustls-symcrypt" authors = ["Microsoft"] -version = "0.2.2" +version = "0.2.3" license = "Apache-2.0 OR ISC OR MIT" description = "Implementation of rustls crypto provider model for SymCrypt" edition.workspace = true diff --git a/rustls-symcrypt/src/lib.rs b/rustls-symcrypt/src/lib.rs index 938c4e8..4e03e2b 100644 --- a/rustls-symcrypt/src/lib.rs +++ b/rustls-symcrypt/src/lib.rs @@ -2,6 +2,7 @@ use rustls::crypto::{CryptoProvider, GetRandomFailed, SecureRandom, SupportedKxGroup}; use rustls::SupportedCipherSuite; +use std::sync::{Arc, OnceLock}; use symcrypt::symcrypt_random; mod cipher_suites; @@ -70,6 +71,41 @@ pub fn default_symcrypt_provider() -> CryptoProvider { } } +/// Returns a process-cached `Arc` for callers that want +/// session-/connection-/test-scoped sharing without paying for repeated +/// `Vec` and `Vec<&dyn SupportedKxGroup>` allocations +/// on every call to [`default_symcrypt_provider`]. +/// +/// Initialized lazily on the first call; subsequent calls are an +/// `Arc::clone` (atomic refcount bump, no heap allocation). +/// +/// Prefer this over `Arc::new(default_symcrypt_provider())` in any code +/// path that may run more than once per process — TLS integration tests, +/// per-connection setup, multi-config harnesses, etc. +/// +/// ```rust +/// use rustls::{ClientConfig, RootCertStore}; +/// use rustls_symcrypt::default_symcrypt_provider_arc; +/// use webpki_roots; +/// +/// let mut root_store = RootCertStore { +/// roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(), +/// }; +/// +/// let provider = default_symcrypt_provider_arc(); +/// let mut config = ClientConfig::builder_with_provider(provider) +/// .with_safe_default_protocol_versions() +/// .unwrap() +/// .with_root_certificates(root_store) +/// .with_no_client_auth(); +/// ``` +pub fn default_symcrypt_provider_arc() -> Arc { + static PROVIDER: OnceLock> = OnceLock::new(); + PROVIDER + .get_or_init(|| Arc::new(default_symcrypt_provider())) + .clone() +} + /// `custom_symcrypt_provider` provides a way to set up an custom config using a `symcrypt` crypto backend. /// /// `provided_cipher_suites` takes in an optional `Vec<>` of `SupportedCipherSuites`. @@ -190,4 +226,16 @@ mod test { assert_ne!(buff_1, buff_2); } + + #[test] + fn test_default_symcrypt_provider_arc_is_cached() { + let a = default_symcrypt_provider_arc(); + let b = default_symcrypt_provider_arc(); + assert!( + Arc::ptr_eq(&a, &b), + "default_symcrypt_provider_arc must return the same Arc on repeat calls", + ); + assert_eq!(a.cipher_suites.len(), DEFAULT_CIPHER_SUITES.len()); + assert!(!a.kx_groups.is_empty()); + } }