1 | #[cfg (feature = "logging" )] |
2 | use crate::log::trace; |
3 | use alloc::sync::Arc; |
4 | use alloc::vec::Vec; |
5 | |
6 | use pki_types::{CertificateDer, CertificateRevocationListDer, ServerName, UnixTime}; |
7 | use webpki::{CertRevocationList, RevocationCheckDepth, UnknownStatusPolicy}; |
8 | |
9 | use crate::crypto::{CryptoProvider, WebPkiSupportedAlgorithms}; |
10 | use crate::verify::{ |
11 | DigitallySignedStruct, HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier, |
12 | }; |
13 | use crate::webpki::verify::{ |
14 | verify_server_cert_signed_by_trust_anchor_impl, verify_tls12_signature, verify_tls13_signature, |
15 | ParsedCertificate, |
16 | }; |
17 | use crate::webpki::{parse_crls, verify_server_name, VerifierBuilderError}; |
18 | use crate::{Error, RootCertStore, SignatureScheme}; |
19 | |
20 | #[cfg (doc)] |
21 | use crate::{crypto, ConfigBuilder, ServerConfig}; |
22 | |
23 | /// A builder for configuring a `webpki` server certificate verifier. |
24 | /// |
25 | /// For more information, see the [`WebPkiServerVerifier`] documentation. |
26 | #[derive (Debug, Clone)] |
27 | pub struct ServerCertVerifierBuilder { |
28 | roots: Arc<RootCertStore>, |
29 | crls: Vec<CertificateRevocationListDer<'static>>, |
30 | revocation_check_depth: RevocationCheckDepth, |
31 | unknown_revocation_policy: UnknownStatusPolicy, |
32 | supported_algs: WebPkiSupportedAlgorithms, |
33 | } |
34 | |
35 | impl ServerCertVerifierBuilder { |
36 | pub(crate) fn new( |
37 | roots: Arc<RootCertStore>, |
38 | supported_algs: WebPkiSupportedAlgorithms, |
39 | ) -> Self { |
40 | Self { |
41 | roots, |
42 | crls: Vec::new(), |
43 | revocation_check_depth: RevocationCheckDepth::Chain, |
44 | unknown_revocation_policy: UnknownStatusPolicy::Deny, |
45 | supported_algs, |
46 | } |
47 | } |
48 | |
49 | /// Verify the revocation state of presented client certificates against the provided |
50 | /// certificate revocation lists (CRLs). Calling `with_crls` multiple times appends the |
51 | /// given CRLs to the existing collection. |
52 | pub fn with_crls( |
53 | mut self, |
54 | crls: impl IntoIterator<Item = CertificateRevocationListDer<'static>>, |
55 | ) -> Self { |
56 | self.crls.extend(crls); |
57 | self |
58 | } |
59 | |
60 | /// Only check the end entity certificate revocation status when using CRLs. |
61 | /// |
62 | /// If CRLs are provided using [`with_crls`][Self::with_crls] only check the end entity |
63 | /// certificate's revocation status. Overrides the default behavior of checking revocation |
64 | /// status for each certificate in the verified chain built to a trust anchor |
65 | /// (excluding the trust anchor itself). |
66 | /// |
67 | /// If no CRLs are provided then this setting has no effect. Neither the end entity certificate |
68 | /// or any intermediates will have revocation status checked. |
69 | pub fn only_check_end_entity_revocation(mut self) -> Self { |
70 | self.revocation_check_depth = RevocationCheckDepth::EndEntity; |
71 | self |
72 | } |
73 | |
74 | /// Allow unknown certificate revocation status when using CRLs. |
75 | /// |
76 | /// If CRLs are provided with [`with_crls`][Self::with_crls] and it isn't possible to |
77 | /// determine the revocation status of a certificate, do not treat it as an error condition. |
78 | /// Overrides the default behavior where unknown revocation status is considered an error. |
79 | /// |
80 | /// If no CRLs are provided then this setting has no effect as revocation status checks |
81 | /// are not performed. |
82 | pub fn allow_unknown_revocation_status(mut self) -> Self { |
83 | self.unknown_revocation_policy = UnknownStatusPolicy::Allow; |
84 | self |
85 | } |
86 | |
87 | /// Build a server certificate verifier, allowing control over the root certificates to use as |
88 | /// trust anchors, and to control how server certificate revocation checking is performed. |
89 | /// |
90 | /// If `with_signature_verification_algorithms` was not called on the builder, a default set of |
91 | /// signature verification algorithms is used, controlled by the selected [`crypto::CryptoProvider`]. |
92 | /// |
93 | /// Once built, the provided `Arc<dyn ServerCertVerifier>` can be used with a Rustls |
94 | /// [`ServerConfig`] to configure client certificate validation using |
95 | /// [`with_client_cert_verifier`][ConfigBuilder<ClientConfig, WantsVerifier>::with_client_cert_verifier]. |
96 | /// |
97 | /// # Errors |
98 | /// This function will return a `CertVerifierBuilderError` if: |
99 | /// 1. No trust anchors have been provided. |
100 | /// 2. DER encoded CRLs have been provided that can not be parsed successfully. |
101 | pub fn build(self) -> Result<Arc<WebPkiServerVerifier>, VerifierBuilderError> { |
102 | if self.roots.is_empty() { |
103 | return Err(VerifierBuilderError::NoRootAnchors); |
104 | } |
105 | |
106 | Ok(WebPkiServerVerifier::new( |
107 | self.roots, |
108 | parse_crls(self.crls)?, |
109 | self.revocation_check_depth, |
110 | self.unknown_revocation_policy, |
111 | self.supported_algs, |
112 | ) |
113 | .into()) |
114 | } |
115 | } |
116 | |
117 | /// Default `ServerCertVerifier`, see the trait impl for more information. |
118 | #[allow (unreachable_pub)] |
119 | #[derive (Debug)] |
120 | pub struct WebPkiServerVerifier { |
121 | roots: Arc<RootCertStore>, |
122 | crls: Vec<CertRevocationList<'static>>, |
123 | revocation_check_depth: RevocationCheckDepth, |
124 | unknown_revocation_policy: UnknownStatusPolicy, |
125 | supported: WebPkiSupportedAlgorithms, |
126 | } |
127 | |
128 | #[allow (unreachable_pub)] |
129 | impl WebPkiServerVerifier { |
130 | /// Create a builder for the `webpki` server certificate verifier configuration using |
131 | /// the default [`CryptoProvider`]. |
132 | /// |
133 | /// Server certificates will be verified using the trust anchors found in the provided `roots`. |
134 | /// |
135 | /// The cryptography used comes from the default [`CryptoProvider`]: [`crypto::ring::default_provider`]. |
136 | /// Use [`Self::builder_with_provider`] if you wish to customize this. |
137 | /// |
138 | /// For more information, see the [`ServerCertVerifierBuilder`] documentation. |
139 | #[cfg (feature = "ring" )] |
140 | pub fn builder(roots: Arc<RootCertStore>) -> ServerCertVerifierBuilder { |
141 | Self::builder_with_provider(roots, crate::crypto::ring::default_provider().into()) |
142 | } |
143 | |
144 | /// Create a builder for the `webpki` server certificate verifier configuration using |
145 | /// a specified [`CryptoProvider`]. |
146 | /// |
147 | /// Server certificates will be verified using the trust anchors found in the provided `roots`. |
148 | /// |
149 | /// The cryptography used comes from the specified [`CryptoProvider`]. |
150 | /// |
151 | /// For more information, see the [`ServerCertVerifierBuilder`] documentation. |
152 | pub fn builder_with_provider( |
153 | roots: Arc<RootCertStore>, |
154 | provider: Arc<CryptoProvider>, |
155 | ) -> ServerCertVerifierBuilder { |
156 | ServerCertVerifierBuilder::new(roots, provider.signature_verification_algorithms) |
157 | } |
158 | |
159 | /// Short-cut for creating a `WebPkiServerVerifier` that does not perform certificate revocation |
160 | /// checking, avoiding the need to use a builder. |
161 | pub(crate) fn new_without_revocation( |
162 | roots: impl Into<Arc<RootCertStore>>, |
163 | supported_algs: WebPkiSupportedAlgorithms, |
164 | ) -> Self { |
165 | Self::new( |
166 | roots, |
167 | Vec::default(), |
168 | RevocationCheckDepth::Chain, |
169 | UnknownStatusPolicy::Allow, |
170 | supported_algs, |
171 | ) |
172 | } |
173 | |
174 | /// Constructs a new `WebPkiServerVerifier`. |
175 | /// |
176 | /// * `roots` is the set of trust anchors to trust for issuing server certs. |
177 | /// * `crls` are a vec of owned certificate revocation lists (CRLs) to use for |
178 | /// client certificate validation. |
179 | /// * `revocation_check_depth` controls which certificates have their revocation status checked |
180 | /// when `crls` are provided. |
181 | /// * `unknown_revocation_policy` controls how certificates with an unknown revocation status |
182 | /// are handled when `crls` are provided. |
183 | /// * `supported` is the set of supported algorithms that will be used for |
184 | /// certificate verification and TLS handshake signature verification. |
185 | pub(crate) fn new( |
186 | roots: impl Into<Arc<RootCertStore>>, |
187 | crls: Vec<CertRevocationList<'static>>, |
188 | revocation_check_depth: RevocationCheckDepth, |
189 | unknown_revocation_policy: UnknownStatusPolicy, |
190 | supported: WebPkiSupportedAlgorithms, |
191 | ) -> Self { |
192 | Self { |
193 | roots: roots.into(), |
194 | crls, |
195 | revocation_check_depth, |
196 | unknown_revocation_policy, |
197 | supported, |
198 | } |
199 | } |
200 | } |
201 | |
202 | impl ServerCertVerifier for WebPkiServerVerifier { |
203 | /// Will verify the certificate is valid in the following ways: |
204 | /// - Signed by a trusted `RootCertStore` CA |
205 | /// - Not Expired |
206 | /// - Valid for DNS entry |
207 | /// - Valid revocation status (if applicable). |
208 | /// |
209 | /// Depending on the verifier's configuration revocation status checking may be performed for |
210 | /// each certificate in the chain to a root CA (excluding the root itself), or only the |
211 | /// end entity certificate. Similarly, unknown revocation status may be treated as an error |
212 | /// or allowed based on configuration. |
213 | fn verify_server_cert( |
214 | &self, |
215 | end_entity: &CertificateDer<'_>, |
216 | intermediates: &[CertificateDer<'_>], |
217 | server_name: &ServerName<'_>, |
218 | ocsp_response: &[u8], |
219 | now: UnixTime, |
220 | ) -> Result<ServerCertVerified, Error> { |
221 | let cert = ParsedCertificate::try_from(end_entity)?; |
222 | |
223 | let crl_refs = self.crls.iter().collect::<Vec<_>>(); |
224 | |
225 | let revocation = if self.crls.is_empty() { |
226 | None |
227 | } else { |
228 | // Note: unwrap here is safe because RevocationOptionsBuilder only errors when given |
229 | // empty CRLs. |
230 | Some( |
231 | webpki::RevocationOptionsBuilder::new(crl_refs.as_slice()) |
232 | // Note: safe to unwrap here - new is only fallible if no CRLs are provided |
233 | // and we verify this above. |
234 | .unwrap() |
235 | .with_depth(self.revocation_check_depth) |
236 | .with_status_policy(self.unknown_revocation_policy) |
237 | .build(), |
238 | ) |
239 | }; |
240 | |
241 | // Note: we use the crate-internal `_impl` fn here in order to provide revocation |
242 | // checking information, if applicable. |
243 | verify_server_cert_signed_by_trust_anchor_impl( |
244 | &cert, |
245 | &self.roots, |
246 | intermediates, |
247 | revocation, |
248 | now, |
249 | self.supported.all, |
250 | )?; |
251 | |
252 | if !ocsp_response.is_empty() { |
253 | trace!("Unvalidated OCSP response: {:?}" , ocsp_response.to_vec()); |
254 | } |
255 | |
256 | verify_server_name(&cert, server_name)?; |
257 | Ok(ServerCertVerified::assertion()) |
258 | } |
259 | |
260 | fn verify_tls12_signature( |
261 | &self, |
262 | message: &[u8], |
263 | cert: &CertificateDer<'_>, |
264 | dss: &DigitallySignedStruct, |
265 | ) -> Result<HandshakeSignatureValid, Error> { |
266 | verify_tls12_signature(message, cert, dss, &self.supported) |
267 | } |
268 | |
269 | fn verify_tls13_signature( |
270 | &self, |
271 | message: &[u8], |
272 | cert: &CertificateDer<'_>, |
273 | dss: &DigitallySignedStruct, |
274 | ) -> Result<HandshakeSignatureValid, Error> { |
275 | verify_tls13_signature(message, cert, dss, &self.supported) |
276 | } |
277 | |
278 | fn supported_verify_schemes(&self) -> Vec<SignatureScheme> { |
279 | self.supported.supported_schemes() |
280 | } |
281 | } |
282 | |
283 | #[cfg (all(test, any(feature = "ring" , feature = "aws_lc_rs" )))] |
284 | mod tests { |
285 | use std::sync::Arc; |
286 | |
287 | use pki_types::{CertificateDer, CertificateRevocationListDer}; |
288 | |
289 | use super::{VerifierBuilderError, WebPkiServerVerifier}; |
290 | use crate::{test_provider, RootCertStore}; |
291 | |
292 | fn load_crls(crls_der: &[&[u8]]) -> Vec<CertificateRevocationListDer<'static>> { |
293 | crls_der |
294 | .iter() |
295 | .map(|pem_bytes| { |
296 | rustls_pemfile::crls(&mut &pem_bytes[..]) |
297 | .next() |
298 | .unwrap() |
299 | .unwrap() |
300 | }) |
301 | .collect() |
302 | } |
303 | |
304 | fn test_crls() -> Vec<CertificateRevocationListDer<'static>> { |
305 | load_crls(&[ |
306 | include_bytes!("../../../test-ca/ecdsa-p256/client.revoked.crl.pem" ).as_slice(), |
307 | include_bytes!("../../../test-ca/rsa/client.revoked.crl.pem" ).as_slice(), |
308 | ]) |
309 | } |
310 | |
311 | fn load_roots(roots_der: &[&[u8]]) -> Arc<RootCertStore> { |
312 | let mut roots = RootCertStore::empty(); |
313 | roots_der.iter().for_each(|der| { |
314 | roots |
315 | .add(CertificateDer::from(der.to_vec())) |
316 | .unwrap() |
317 | }); |
318 | roots.into() |
319 | } |
320 | |
321 | fn test_roots() -> Arc<RootCertStore> { |
322 | load_roots(&[ |
323 | include_bytes!("../../../test-ca/ecdsa-p256/ca.der" ).as_slice(), |
324 | include_bytes!("../../../test-ca/rsa/ca.der" ).as_slice(), |
325 | ]) |
326 | } |
327 | |
328 | #[test ] |
329 | fn test_with_invalid_crls() { |
330 | // Trying to build a server verifier with invalid CRLs should error at build time. |
331 | let result = WebPkiServerVerifier::builder_with_provider( |
332 | test_roots(), |
333 | test_provider::default_provider().into(), |
334 | ) |
335 | .with_crls(vec![CertificateRevocationListDer::from(vec![0xFF])]) |
336 | .build(); |
337 | assert!(matches!(result, Err(VerifierBuilderError::InvalidCrl(_)))); |
338 | } |
339 | |
340 | #[test ] |
341 | fn test_with_crls_multiple_calls() { |
342 | // We should be able to call `with_crls` on a server verifier multiple times. |
343 | let initial_crls = test_crls(); |
344 | let extra_crls = |
345 | load_crls(&[ |
346 | include_bytes!("../../../test-ca/eddsa/client.revoked.crl.pem" ).as_slice(), |
347 | ]); |
348 | |
349 | let builder = WebPkiServerVerifier::builder_with_provider( |
350 | test_roots(), |
351 | test_provider::default_provider().into(), |
352 | ) |
353 | .with_crls(initial_crls.clone()) |
354 | .with_crls(extra_crls.clone()); |
355 | |
356 | // There should be the expected number of crls. |
357 | assert_eq!(builder.crls.len(), initial_crls.len() + extra_crls.len()); |
358 | // The builder should be Debug. |
359 | println!("{:?}" , builder); |
360 | builder.build().unwrap(); |
361 | } |
362 | |
363 | #[test ] |
364 | fn test_builder_no_roots() { |
365 | // Trying to create a server verifier builder with no trust anchors should fail at build time |
366 | let result = WebPkiServerVerifier::builder_with_provider( |
367 | RootCertStore::empty().into(), |
368 | test_provider::default_provider().into(), |
369 | ) |
370 | .build(); |
371 | assert!(matches!(result, Err(VerifierBuilderError::NoRootAnchors))); |
372 | } |
373 | |
374 | #[test ] |
375 | fn test_server_verifier_ee_only() { |
376 | // We should be able to build a server cert. verifier that only checks the EE cert. |
377 | let builder = WebPkiServerVerifier::builder_with_provider( |
378 | test_roots(), |
379 | test_provider::default_provider().into(), |
380 | ) |
381 | .only_check_end_entity_revocation(); |
382 | // The builder should be Debug. |
383 | println!("{:?}" , builder); |
384 | builder.build().unwrap(); |
385 | } |
386 | |
387 | #[test ] |
388 | fn test_server_verifier_allow_unknown() { |
389 | // We should be able to build a server cert. verifier that allows unknown revocation |
390 | // status. |
391 | let builder = WebPkiServerVerifier::builder_with_provider( |
392 | test_roots(), |
393 | test_provider::default_provider().into(), |
394 | ) |
395 | .allow_unknown_revocation_status(); |
396 | // The builder should be Debug. |
397 | println!("{:?}" , builder); |
398 | builder.build().unwrap(); |
399 | } |
400 | |
401 | #[test ] |
402 | fn test_server_verifier_allow_unknown_ee_only() { |
403 | // We should be able to build a server cert. verifier that allows unknown revocation |
404 | // status and only checks the EE cert. |
405 | let builder = WebPkiServerVerifier::builder_with_provider( |
406 | test_roots(), |
407 | test_provider::default_provider().into(), |
408 | ) |
409 | .allow_unknown_revocation_status() |
410 | .only_check_end_entity_revocation(); |
411 | // The builder should be Debug. |
412 | println!("{:?}" , builder); |
413 | builder.build().unwrap(); |
414 | } |
415 | } |
416 | |