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}