1use crate::{
5 certificate::{IntoCertificate, IntoPrivateKey},
6 keylog::KeyLogHandle,
7 params::Params,
8 session::Session,
9 ConfigLoader,
10};
11use s2n_codec::EncoderValue;
12use s2n_quic_core::{application::ServerName, crypto::tls, endpoint};
13use s2n_tls::{
14 callbacks::VerifyHostNameCallback,
15 config::{self, Config},
16 enums::ClientAuthType,
17 error::Error,
18};
19use std::sync::Arc;
20
21pub struct Client<L: ConfigLoader = Config> {
22 loader: L,
23 #[allow(dead_code)] keylog: Option<KeyLogHandle>,
25 params: Params,
26}
27
28impl Client {
29 pub fn builder() -> Builder {
30 Builder::default()
31 }
32}
33
34impl<L: ConfigLoader> Client<L> {
35 pub fn from_loader(loader: L) -> Self {
43 Self {
44 loader,
45 keylog: None,
46 params: Default::default(),
47 }
48 }
49}
50
51impl Default for Client {
52 fn default() -> Self {
53 Self::builder()
54 .build()
55 .expect("could not create a default client")
56 }
57}
58
59impl<L: ConfigLoader> ConfigLoader for Client<L> {
60 #[inline]
61 fn load(&mut self, cx: crate::ConnectionContext) -> s2n_tls::config::Config {
62 self.loader.load(cx)
63 }
64}
65
66pub struct Builder {
67 config: config::Builder,
68 keylog: Option<KeyLogHandle>,
69}
70
71impl Default for Builder {
72 fn default() -> Self {
73 let mut config = config::Builder::default();
74 config.enable_quic().unwrap();
75 config.set_security_policy(crate::DEFAULT_POLICY).unwrap();
77 config.set_application_protocol_preference([b"h3"]).unwrap();
78
79 Self {
80 config,
81 keylog: None,
82 }
83 }
84}
85
86impl Builder {
87 pub fn config_mut(&mut self) -> &mut s2n_tls::config::Builder {
88 &mut self.config
89 }
90
91 pub fn with_application_protocols<P: IntoIterator<Item = I>, I: AsRef<[u8]>>(
92 mut self,
93 protocols: P,
94 ) -> Result<Self, Error> {
95 self.config.set_application_protocol_preference(protocols)?;
96 Ok(self)
97 }
98
99 pub fn with_certificate<C: IntoCertificate>(mut self, certificate: C) -> Result<Self, Error> {
100 let certificate = certificate.into_certificate()?;
101 let certificate = certificate
102 .0
103 .as_pem()
104 .expect("pem is currently the only certificate format supported");
105 self.config.trust_pem(certificate)?;
106 Ok(self)
107 }
108
109 pub fn with_empty_trust_store(mut self) -> Result<Self, Error> {
119 self.config.wipe_trust_store()?;
120 Ok(self)
121 }
122
123 pub fn with_client_identity<C: IntoCertificate, PK: IntoPrivateKey>(
129 mut self,
130 certificate: C,
131 private_key: PK,
132 ) -> Result<Self, Error> {
133 let certificate = certificate.into_certificate()?;
134 let private_key = private_key.into_private_key()?;
135 self.config.load_pem(
136 certificate
137 .0
138 .as_pem()
139 .expect("pem is currently the only certificate format supported"),
140 private_key
141 .0
142 .as_pem()
143 .expect("pem is currently the only certificate format supported"),
144 )?;
145 self.config.set_client_auth_type(ClientAuthType::Required)?;
146 Ok(self)
147 }
148
149 pub fn with_verify_host_name_callback<T: 'static + VerifyHostNameCallback>(
156 mut self,
157 handler: T,
158 ) -> Result<Self, Error> {
159 self.config.set_verify_host_callback(handler)?;
160 Ok(self)
161 }
162
163 pub fn with_max_cert_chain_depth(mut self, len: u16) -> Result<Self, Error> {
164 self.config.set_max_cert_chain_depth(len)?;
165 Ok(self)
166 }
167
168 pub fn with_key_logging(mut self) -> Result<Self, Error> {
169 use crate::keylog::KeyLog;
170
171 self.keylog = KeyLog::try_open();
172
173 unsafe {
174 if let Some(keylog) = self.keylog.as_ref() {
176 self.config
177 .set_key_log_callback(Some(KeyLog::callback), Arc::as_ptr(keylog) as *mut _)?;
178 } else {
179 self.config
181 .set_key_log_callback(None, core::ptr::null_mut())?;
182 }
183 }
184
185 Ok(self)
186 }
187
188 pub fn build(self) -> Result<Client, Error> {
189 #[cfg(feature = "fips")]
190 assert!(s2n_tls::init::fips_mode()?.is_enabled());
191
192 Ok(Client {
193 loader: self.config.build()?,
194 keylog: self.keylog,
195 params: Default::default(),
196 })
197 }
198}
199
200impl<L: ConfigLoader> tls::Endpoint for Client<L> {
201 type Session = Session;
202
203 fn new_server_session<Params: EncoderValue>(&mut self, _params: &Params) -> Self::Session {
204 panic!("cannot create a server session from a client config");
205 }
206
207 fn new_client_session<Params: EncoderValue>(
208 &mut self,
209 params: &Params,
210 server_name: ServerName,
211 ) -> Self::Session {
212 let config = self.loader.load(crate::ConnectionContext {
213 server_name: Some(&server_name),
214 });
215 self.params.with(params, |params| {
216 Session::new(endpoint::Type::Client, config, params, Some(server_name)).unwrap()
217 })
218 }
219
220 fn max_tag_length(&self) -> usize {
221 s2n_quic_crypto::MAX_TAG_LEN
222 }
223}