Skip to main content

rustls_symcrypt/
lib.rs

1#![doc = include_str!("../README.md")]
2use rustls::crypto::{CryptoProvider, GetRandomFailed, SecureRandom, SupportedKxGroup};
3
4use rustls::SupportedCipherSuite;
5use std::sync::{Arc, OnceLock};
6use symcrypt::symcrypt_random;
7
8mod cipher_suites;
9mod ecdh;
10mod hash;
11mod hmac;
12mod signer;
13mod tls12;
14mod tls13;
15mod verify;
16use crate::verify::SUPPORTED_SIG_ALGS;
17
18/// Exporting default cipher suites for TLS 1.3
19pub use cipher_suites::{TLS13_AES_128_GCM_SHA256, TLS13_AES_256_GCM_SHA384};
20
21/// Exporting default cipher suites for TLS 1.2
22pub use cipher_suites::{
23    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
24    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
25};
26
27/// Exporting ChaCha suites for TLS 1.2 and TLS 1.3
28#[cfg(feature = "chacha")]
29pub use cipher_suites::{
30    TLS13_CHACHA20_POLY1305_SHA256, TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
31    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
32};
33
34/// Exporting default key exchange groups
35pub use ecdh::{SECP256R1, SECP384R1};
36
37/// Exporting X25519 key exchange group
38#[cfg(feature = "x25519")]
39pub use ecdh::X25519;
40
41/// `default_symcrypt_provider` returns a `CryptoProvider` using the default `SymCrypt` configuration and cipher suites.
42/// To see the default cipher suites, please take a look at [`DEFAULT_CIPHER_SUITES`].
43///
44/// Sample usage:
45/// ```rust
46/// use rustls::{ClientConfig, RootCertStore};
47/// use rustls_symcrypt::default_symcrypt_provider;
48/// use std::sync::Arc;
49/// use webpki_roots;
50///
51/// let mut root_store = RootCertStore {
52///     roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(),
53/// };
54///
55/// let mut config =
56/// ClientConfig::builder_with_provider(Arc::new(default_symcrypt_provider()))
57///     .with_safe_default_protocol_versions()
58///     .unwrap()
59///     .with_root_certificates(root_store)
60///     .with_no_client_auth();
61/// // Rest of the connection setup
62///
63/// ```
64pub fn default_symcrypt_provider() -> CryptoProvider {
65    CryptoProvider {
66        cipher_suites: DEFAULT_CIPHER_SUITES.to_vec(),
67        kx_groups: ecdh::ALL_KX_GROUPS.to_vec(),
68        signature_verification_algorithms: SUPPORTED_SIG_ALGS,
69        secure_random: &SymCrypt,
70        key_provider: &signer::SymCryptProvider,
71    }
72}
73
74/// Returns a process-cached `Arc<CryptoProvider>` for callers that want
75/// session-/connection-/test-scoped sharing without paying for repeated
76/// `Vec<SupportedCipherSuite>` and `Vec<&dyn SupportedKxGroup>` allocations
77/// on every call to [`default_symcrypt_provider`].
78///
79/// Initialized lazily on the first call; subsequent calls are an
80/// `Arc::clone` (atomic refcount bump, no heap allocation).
81///
82/// Prefer this over `Arc::new(default_symcrypt_provider())` in any code
83/// path that may run more than once per process — TLS integration tests,
84/// per-connection setup, multi-config harnesses, etc.
85///
86/// ```rust
87/// use rustls::{ClientConfig, RootCertStore};
88/// use rustls_symcrypt::default_symcrypt_provider_arc;
89/// use webpki_roots;
90///
91/// let mut root_store = RootCertStore {
92///     roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(),
93/// };
94///
95/// let provider = default_symcrypt_provider_arc();
96/// let mut config = ClientConfig::builder_with_provider(provider)
97///     .with_safe_default_protocol_versions()
98///     .unwrap()
99///     .with_root_certificates(root_store)
100///     .with_no_client_auth();
101/// ```
102pub fn default_symcrypt_provider_arc() -> Arc<CryptoProvider> {
103    static PROVIDER: OnceLock<Arc<CryptoProvider>> = OnceLock::new();
104    PROVIDER
105        .get_or_init(|| Arc::new(default_symcrypt_provider()))
106        .clone()
107}
108
109/// `custom_symcrypt_provider` provides a way to set up an custom config using a `symcrypt` crypto backend.
110///
111/// `provided_cipher_suites` takes in an optional `Vec<>` of `SupportedCipherSuites`.
112/// The supplied arguments for `provided_cipher_suite` will be used when when negotiating the TLS cipher suite;
113/// and should be placed in preference order, where the first element has highest priority.
114/// If `None` or an empty `Vec<>` is provided the [`DEFAULT_CIPHER_SUITES`] will be used instead.
115///
116/// `provided_kx_group` takes in an optional `Vec<>` of `SupportedKxGroup`
117/// The supplied arguments for `provided_kx_group` will be used when when negotiating the TLS key exchange;
118/// and should be placed in preference order, where the first element has highest priority.
119/// If `None` or an empty `Vec<>` is provided the default will be used instead.
120///
121/// This call cannot fail.
122///
123/// Sample usage:
124/// ```rust
125/// use rustls::{ClientConfig, RootCertStore};
126/// use rustls_symcrypt::{custom_symcrypt_provider, TLS13_AES_128_GCM_SHA256, SECP256R1};
127/// use std::sync::Arc;
128/// use webpki_roots;
129///
130/// let mut root_store = RootCertStore {
131///     roots: webpki_roots::TLS_SERVER_ROOTS.iter().cloned().collect(),
132/// };
133///
134/// // Set custom config of cipher suites that have been imported from rustls_symcrypt.
135/// let cipher_suites = vec![TLS13_AES_128_GCM_SHA256];
136/// let kx_group = vec![SECP256R1];
137///
138/// let mut config =
139///     ClientConfig::builder_with_provider(Arc::new(custom_symcrypt_provider(
140///         Some(cipher_suites), Some(kx_group))))
141///             .with_safe_default_protocol_versions()
142///             .unwrap()
143///             .with_root_certificates(root_store)
144///             .with_no_client_auth();
145///     // Rest of the connection setup
146/// ```
147pub fn custom_symcrypt_provider(
148    provided_cipher_suites: Option<Vec<SupportedCipherSuite>>,
149    provided_kx_group: Option<Vec<&'static dyn SupportedKxGroup>>,
150) -> CryptoProvider {
151    let cipher_suites = match provided_cipher_suites {
152        Some(suites) if !suites.is_empty() => suites, // Use provided non-empty suites
153        _ => DEFAULT_CIPHER_SUITES.to_vec(),          // Use default suites if None or empty
154    };
155
156    let kx_group = match provided_kx_group {
157        Some(groups) if !groups.is_empty() => groups, // Use provided non-empty groups
158        _ => ecdh::ALL_KX_GROUPS.to_vec(),            // Use default groups if None or empty
159    };
160
161    CryptoProvider {
162        cipher_suites,
163        kx_groups: kx_group,
164        signature_verification_algorithms: SUPPORTED_SIG_ALGS,
165        secure_random: &SymCrypt,
166        key_provider: &signer::SymCryptProvider,
167    }
168}
169
170/// List of SymCrypt supported cipher suites in a preference order.
171/// The first element has highest priority when negotiating cipher suites.
172/// ```ignore
173/// // TLS 1.3 suites
174/// TLS13_AES_256_GCM_SHA384
175/// TLS13_AES_128_GCM_SHA256
176/// TLS13_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature
177/// // TLS 1.2 suites
178/// TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384
179/// TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256
180/// TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature
181/// TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256
182/// TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384
183/// TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 // Enabled with the `chacha` feature
184/// ```
185pub static DEFAULT_CIPHER_SUITES: &[SupportedCipherSuite] = ALL_CIPHER_SUITES;
186
187static ALL_CIPHER_SUITES: &[SupportedCipherSuite] = &[
188    // TLS 1.3 suites
189    TLS13_AES_256_GCM_SHA384,
190    TLS13_AES_128_GCM_SHA256,
191    #[cfg(feature = "chacha")]
192    TLS13_CHACHA20_POLY1305_SHA256,
193    // TLS 1.2 suites
194    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384,
195    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256,
196    #[cfg(feature = "chacha")]
197    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256,
198    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384,
199    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256,
200    #[cfg(feature = "chacha")]
201    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256,
202];
203
204#[derive(Debug)]
205struct SymCrypt;
206
207impl SecureRandom for SymCrypt {
208    fn fill(&self, buf: &mut [u8]) -> Result<(), GetRandomFailed> {
209        symcrypt_random(buf);
210        Ok(())
211    }
212}
213
214#[cfg(test)]
215mod test {
216    use super::*;
217
218    #[test]
219    fn test_secure_random() {
220        let random = SymCrypt;
221        let mut buff_1 = [0u8; 10];
222        let mut buff_2 = [0u8; 10];
223
224        let _ = random.fill(&mut buff_1);
225        let _ = random.fill(&mut buff_2);
226
227        assert_ne!(buff_1, buff_2);
228    }
229
230    #[test]
231    fn test_default_symcrypt_provider_arc_is_cached() {
232        let a = default_symcrypt_provider_arc();
233        let b = default_symcrypt_provider_arc();
234        assert!(
235            Arc::ptr_eq(&a, &b),
236            "default_symcrypt_provider_arc must return the same Arc on repeat calls",
237        );
238        assert_eq!(a.cipher_suites.len(), DEFAULT_CIPHER_SUITES.len());
239        assert!(!a.kx_groups.is_empty());
240    }
241}