Skip to content

Commit 9f0c916

Browse files
authored
feat: enhance verifiers and expose util functions (#10)
* feat: enhance verifiers and expose util functions - Change verifiers to be able to ignore expired cert. - Change verifiers to be able to have a custom verify callback. - Make some util functions public. * test: add tests * refactor * refactor * code improve * fmt * fix rustdoc
1 parent 3225e03 commit 9f0c916

File tree

5 files changed

+366
-62
lines changed

5 files changed

+366
-62
lines changed

.github/workflows/build.yml

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -193,8 +193,8 @@ jobs:
193193
uses: dtolnay/rust-toolchain@stable
194194
with:
195195
components: clippy
196-
- run: cargo clippy --locked --package rustls-mbedcrypto-provider --all-features --all-targets -- --deny warnings
197-
- run: cargo clippy --locked --package rustls-mbedcrypto-provider --no-default-features --all-targets -- --deny warnings
196+
- run: cargo clippy --locked --all-features --all-targets -- --deny warnings
197+
- run: cargo clippy --locked --no-default-features --all-targets -- --deny warnings
198198

199199
clippy-nightly:
200200
name: Clippy (Nightly)
@@ -212,5 +212,5 @@ jobs:
212212
uses: dtolnay/rust-toolchain@nightly
213213
with:
214214
components: clippy
215-
- run: cargo clippy --locked --package rustls-mbedcrypto-provider --all-features --all-targets
216-
- run: cargo clippy --locked --package rustls-mbedcrypto-provider --no-default-features --all-targets
215+
- run: cargo clippy --locked --all-features --all-targets
216+
- run: cargo clippy --locked --no-default-features --all-targets

rustls-mbedpki-provider/src/client_cert_verifier.rs

Lines changed: 125 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8+
use std::sync::Arc;
9+
810
use chrono::NaiveDateTime;
911
use pki_types::{CertificateDer, UnixTime};
1012
use rustls::{
@@ -14,19 +16,21 @@ use rustls::{
1416

1517
use crate::{
1618
mbedtls_err_into_rustls_err, mbedtls_err_into_rustls_err_with_error_msg, rustls_cert_to_mbedtls_cert,
17-
verify_certificates_active, verify_tls_signature,
19+
verify_certificates_active, verify_tls_signature, CertActiveCheck,
1820
};
1921

20-
/// A `rustls` `ClientCertVerifier` implemented using the PKI functionality of
22+
/// A [`rustls`] [`ClientCertVerifier`] implemented using the PKI functionality of
2123
/// `mbedtls`
2224
#[derive(Clone)]
2325
pub struct MbedTlsClientCertVerifier {
2426
trusted_cas: mbedtls::alloc::List<mbedtls::x509::Certificate>,
25-
root_subjects: Vec<rustls::DistinguishedName>,
27+
root_subjects: Vec<DistinguishedName>,
28+
verify_callback: Option<Arc<dyn mbedtls::x509::VerifyCallback + 'static>>,
29+
cert_active_check: CertActiveCheck,
2630
}
2731

2832
impl MbedTlsClientCertVerifier {
29-
/// Constructs a new `MbedTlsClientCertVerifier` object given the provided trusted certificate authority
33+
/// Constructs a new [`MbedTlsClientCertVerifier`] object given the provided trusted certificate authority
3034
/// certificates.
3135
///
3236
/// Returns an error if any of the certificates are invalid.
@@ -40,7 +44,7 @@ impl MbedTlsClientCertVerifier {
4044
Self::new_from_mbedtls_trusted_cas(trusted_cas)
4145
}
4246

43-
/// Constructs a new `MbedTlsClientCertVerifier` object given the provided trusted certificate authority
47+
/// Constructs a new [`MbedTlsClientCertVerifier`] object given the provided trusted certificate authority
4448
/// certificates.
4549
pub fn new_from_mbedtls_trusted_cas(
4650
trusted_cas: mbedtls::alloc::List<mbedtls::x509::Certificate>,
@@ -49,7 +53,12 @@ impl MbedTlsClientCertVerifier {
4953
for ca in trusted_cas.iter() {
5054
root_subjects.push(DistinguishedName::from(ca.subject_raw()?));
5155
}
52-
Ok(Self { trusted_cas, root_subjects })
56+
Ok(Self {
57+
trusted_cas,
58+
root_subjects,
59+
verify_callback: None,
60+
cert_active_check: CertActiveCheck::default(),
61+
})
5362
}
5463

5564
/// The certificate authority certificates used to construct this object
@@ -62,6 +71,28 @@ impl MbedTlsClientCertVerifier {
6271
pub fn root_subjects(&self) -> &[DistinguishedName] {
6372
self.root_subjects.as_ref()
6473
}
74+
75+
/// Retrieves the verification callback function set for the certificate verification process.
76+
pub fn verify_callback(&self) -> Option<Arc<dyn mbedtls::x509::VerifyCallback + 'static>> {
77+
self.verify_callback.clone()
78+
}
79+
80+
/// Sets the verification callback for mbedtls certificate verification process,
81+
///
82+
/// This callback function allows you to add logic at end of mbedtls verification before returning.
83+
pub fn set_verify_callback(&mut self, callback: Option<Arc<dyn mbedtls::x509::VerifyCallback + 'static>>) {
84+
self.verify_callback = callback;
85+
}
86+
87+
/// Getter for [`CertActiveCheck`]
88+
pub fn cert_active_check(&self) -> &CertActiveCheck {
89+
&self.cert_active_check
90+
}
91+
92+
/// Setter for [`CertActiveCheck`]
93+
pub fn set_cert_active_check(&mut self, check: CertActiveCheck) {
94+
self.cert_active_check = check;
95+
}
6596
}
6697

6798
impl ClientCertVerifier for MbedTlsClientCertVerifier {
@@ -74,7 +105,7 @@ impl ClientCertVerifier for MbedTlsClientCertVerifier {
74105
end_entity: &CertificateDer,
75106
intermediates: &[CertificateDer],
76107
now: UnixTime,
77-
) -> Result<rustls::server::danger::ClientCertVerified, rustls::Error> {
108+
) -> Result<ClientCertVerified, rustls::Error> {
78109
let now = NaiveDateTime::from_timestamp_opt(
79110
now.as_secs()
80111
.try_into()
@@ -92,11 +123,25 @@ impl ClientCertVerifier for MbedTlsClientCertVerifier {
92123
.into_iter()
93124
.collect();
94125

95-
verify_certificates_active(chain.iter().map(|c| &**c), now)?;
126+
verify_certificates_active(chain.iter().map(|c| &**c), now, &self.cert_active_check)?;
96127

97128
let mut error_msg = String::default();
98-
mbedtls::x509::Certificate::verify(&chain, &self.trusted_cas, None, Some(&mut error_msg))
99-
.map_err(|e| mbedtls_err_into_rustls_err_with_error_msg(e, &error_msg))?;
129+
match &self.verify_callback {
130+
Some(callback) => {
131+
let callback = Arc::clone(callback);
132+
mbedtls::x509::Certificate::verify_with_callback(
133+
&chain,
134+
&self.trusted_cas,
135+
None,
136+
Some(&mut error_msg),
137+
move |cert: &mbedtls::x509::Certificate, depth: i32, flags: &mut mbedtls::x509::VerifyError| {
138+
callback(cert, depth, flags)
139+
},
140+
)
141+
}
142+
None => mbedtls::x509::Certificate::verify(&chain, &self.trusted_cas, None, Some(&mut error_msg)),
143+
}
144+
.map_err(|e| mbedtls_err_into_rustls_err_with_error_msg(e, &error_msg))?;
100145

101146
Ok(ClientCertVerified::assertion())
102147
}
@@ -272,4 +317,74 @@ mod tests {
272317
rustls::Error::InvalidCertificate(CertificateError::Expired)
273318
);
274319
}
320+
321+
#[test]
322+
fn client_cert_verifier_active_check() {
323+
let cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
324+
let trusted_cas = [CertificateDer::from(include_bytes!("../test-data/rsa/ca.der").to_vec())];
325+
326+
let mut verifier = MbedTlsClientCertVerifier::new(trusted_cas.iter()).unwrap();
327+
let now = SystemTime::from(DateTime::parse_from_rfc3339("2052-11-26T12:00:00+00:00").unwrap());
328+
let now = UnixTime::since_unix_epoch(
329+
now.duration_since(SystemTime::UNIX_EPOCH)
330+
.unwrap(),
331+
);
332+
333+
assert_eq!(
334+
verifier
335+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
336+
.unwrap_err(),
337+
rustls::Error::InvalidCertificate(CertificateError::Expired)
338+
);
339+
verifier.set_cert_active_check(crate::CertActiveCheck { ignore_expired: true, ignore_not_active_yet: false });
340+
341+
assert!(verifier
342+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
343+
.is_ok());
344+
345+
let now = SystemTime::from(DateTime::parse_from_rfc3339("2002-11-26T12:00:00+00:00").unwrap());
346+
let now = UnixTime::since_unix_epoch(
347+
now.duration_since(SystemTime::UNIX_EPOCH)
348+
.unwrap(),
349+
);
350+
assert_eq!(
351+
verifier
352+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
353+
.unwrap_err(),
354+
rustls::Error::InvalidCertificate(CertificateError::NotValidYet)
355+
);
356+
verifier.set_cert_active_check(crate::CertActiveCheck { ignore_expired: false, ignore_not_active_yet: true });
357+
358+
assert!(verifier
359+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
360+
.is_ok());
361+
}
362+
363+
#[test]
364+
fn client_cert_verifier_callback() {
365+
let mut cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
366+
cert_chain.remove(1);
367+
let trusted_cas = [CertificateDer::from(include_bytes!("../test-data/rsa/ca.der").to_vec())];
368+
369+
let mut verifier = MbedTlsClientCertVerifier::new(trusted_cas.iter()).unwrap();
370+
assert!(verifier.verify_callback().is_none());
371+
let now = SystemTime::from(DateTime::parse_from_rfc3339("2023-11-26T12:00:00+00:00").unwrap());
372+
let now = UnixTime::since_unix_epoch(
373+
now.duration_since(SystemTime::UNIX_EPOCH)
374+
.unwrap(),
375+
);
376+
377+
let verify_res = verifier.verify_client_cert(&cert_chain[0], &cert_chain[1..], now);
378+
assert!(matches!(verify_res, Err(rustls::Error::InvalidCertificate(_))));
379+
380+
verifier.set_verify_callback(Some(Arc::new(
381+
move |_cert: &mbedtls::x509::Certificate, _depth: i32, flags: &mut mbedtls::x509::VerifyError| {
382+
flags.remove(mbedtls::x509::VerifyError::CERT_NOT_TRUSTED);
383+
Ok(())
384+
},
385+
)));
386+
assert!(verifier.verify_callback().is_some());
387+
let verify_res = verifier.verify_client_cert(&cert_chain[0], &cert_chain[1..], now);
388+
assert!(verify_res.is_ok(), "{:?}", verify_res);
389+
}
275390
}

rustls-mbedpki-provider/src/lib.rs

Lines changed: 76 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,33 @@
55
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
66
*/
77

8+
//! rustls-mbedpki-provider
9+
//!
10+
//! rustls-mbedpki-provider is a pki provider for rustls based on [mbedtls].
11+
//!
12+
//! [mbedtls]: https://github.com/fortanix/rust-mbedtls
13+
14+
// Require docs for public APIs, deny unsafe code, etc.
15+
#![forbid(unsafe_code, unused_must_use)]
16+
#![cfg_attr(not(bench), forbid(unstable_features))]
17+
#![deny(
18+
clippy::alloc_instead_of_core,
19+
clippy::clone_on_ref_ptr,
20+
clippy::std_instead_of_core,
21+
clippy::use_self,
22+
clippy::upper_case_acronyms,
23+
trivial_casts,
24+
trivial_numeric_casts,
25+
missing_docs,
26+
unreachable_pub,
27+
unused_import_braces,
28+
unused_extern_crates,
29+
unused_qualifications
30+
)]
31+
// Enable documentation for all features on docs.rs
32+
#![cfg_attr(docsrs, feature(doc_cfg, doc_auto_cfg))]
33+
#![cfg_attr(bench, feature(test))]
34+
835
use chrono::NaiveDateTime;
936
use mbedtls::hash::Type;
1037
use pki_types::CertificateDer;
@@ -14,12 +41,28 @@ use std::sync::Arc;
1441
#[cfg(test)]
1542
mod tests_common;
1643

44+
/// module for implementation of [`ClientCertVerifier`]
45+
///
46+
/// [`ClientCertVerifier`]: rustls::server::danger::ClientCertVerifier
1747
pub mod client_cert_verifier;
48+
/// module for implementation of [`ServerCertVerifier`]
49+
///
50+
/// [`ServerCertVerifier`]: rustls::client::danger::ServerCertVerifier
1851
pub mod server_cert_verifier;
1952

2053
pub use client_cert_verifier::MbedTlsClientCertVerifier;
2154
pub use server_cert_verifier::MbedTlsServerCertVerifier;
2255

56+
/// A config about whether to check certificate validity period
57+
#[derive(Debug, PartialEq, Eq, Clone, Default)]
58+
pub struct CertActiveCheck {
59+
/// Accept expired certificates
60+
pub ignore_expired: bool,
61+
/// Accept certificates that are not yet active
62+
pub ignore_not_active_yet: bool,
63+
}
64+
65+
/// Helper function to convert a [`CertificateDer`] to [`mbedtls::x509::Certificate`]
2366
pub fn rustls_cert_to_mbedtls_cert(cert: &CertificateDer) -> mbedtls::Result<mbedtls::alloc::Box<mbedtls::x509::Certificate>> {
2467
let cert = mbedtls::x509::Certificate::from_der(cert)?;
2568
Ok(cert)
@@ -30,6 +73,7 @@ pub fn mbedtls_err_into_rustls_err(err: mbedtls::Error) -> rustls::Error {
3073
mbedtls_err_into_rustls_err_with_error_msg(err, "")
3174
}
3275

76+
/// All supported signature schemas
3377
pub const SUPPORTED_SIGNATURE_SCHEMA: [SignatureScheme; 9] = [
3478
rustls::SignatureScheme::RSA_PSS_SHA512,
3579
rustls::SignatureScheme::RSA_PSS_SHA384,
@@ -75,7 +119,8 @@ pub fn mbedtls_err_into_rustls_err_with_error_msg(err: mbedtls::Error, msg: &str
75119
}
76120
}
77121

78-
fn rustls_signature_scheme_to_mbedtls_hash_type(signature_scheme: SignatureScheme) -> mbedtls::hash::Type {
122+
/// Helper function to convert rustls [`SignatureScheme`] to mbedtls [`Type`]
123+
pub fn rustls_signature_scheme_to_mbedtls_hash_type(signature_scheme: SignatureScheme) -> Type {
79124
match signature_scheme {
80125
SignatureScheme::RSA_PKCS1_SHA1 => Type::Sha1,
81126
SignatureScheme::ECDSA_SHA1_Legacy => Type::Sha1,
@@ -95,7 +140,8 @@ fn rustls_signature_scheme_to_mbedtls_hash_type(signature_scheme: SignatureSchem
95140
}
96141
}
97142

98-
fn rustls_signature_scheme_to_mbedtls_pk_options(signature_scheme: SignatureScheme) -> Option<mbedtls::pk::Options> {
143+
/// Helper function to convert rustls [`SignatureScheme`] to mbedtls [`mbedtls::pk::Options`]
144+
pub fn rustls_signature_scheme_to_mbedtls_pk_options(signature_scheme: SignatureScheme) -> Option<mbedtls::pk::Options> {
99145
use mbedtls::pk::Options;
100146
use mbedtls::pk::RsaPadding;
101147
// reference: https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3
@@ -118,7 +164,8 @@ fn rustls_signature_scheme_to_mbedtls_pk_options(signature_scheme: SignatureSche
118164
}
119165
}
120166

121-
fn rustls_signature_scheme_to_mbedtls_curve_id(signature_scheme: SignatureScheme) -> mbedtls::pk::EcGroupId {
167+
/// Helper function to convert rustls [`SignatureScheme`] to mbedtls [`mbedtls::pk::EcGroupId`]
168+
pub fn rustls_signature_scheme_to_mbedtls_curve_id(signature_scheme: SignatureScheme) -> mbedtls::pk::EcGroupId {
122169
// reference: https://www.rfc-editor.org/rfc/rfc8446.html#section-4.2.3
123170
use mbedtls::pk::EcGroupId;
124171
match signature_scheme {
@@ -141,7 +188,7 @@ fn rustls_signature_scheme_to_mbedtls_curve_id(signature_scheme: SignatureScheme
141188
}
142189

143190
/// Returns the size of the message digest given the hash type.
144-
fn hash_size_bytes(hash_type: mbedtls::hash::Type) -> Option<usize> {
191+
fn hash_size_bytes(hash_type: Type) -> Option<usize> {
145192
match hash_type {
146193
mbedtls::hash::Type::None => None,
147194
mbedtls::hash::Type::Md2 => Some(16),
@@ -156,7 +203,8 @@ fn hash_size_bytes(hash_type: mbedtls::hash::Type) -> Option<usize> {
156203
}
157204
}
158205

159-
fn buffer_for_hash_type(hash_type: mbedtls::hash::Type) -> Option<Vec<u8>> {
206+
/// Returns the a ready to use empty [`Vec<u8>`] for the message digest with given hash type.
207+
pub fn buffer_for_hash_type(hash_type: Type) -> Option<Vec<u8>> {
160208
let size = hash_size_bytes(hash_type)?;
161209
Some(vec![0; size])
162210
}
@@ -166,27 +214,36 @@ fn buffer_for_hash_type(hash_type: mbedtls::hash::Type) -> Option<Vec<u8>> {
166214
fn verify_certificates_active<'a>(
167215
chain: impl IntoIterator<Item = &'a mbedtls::x509::Certificate>,
168216
now: NaiveDateTime,
217+
active_check: &CertActiveCheck,
169218
) -> Result<(), rustls::Error> {
219+
if active_check.ignore_expired && active_check.ignore_not_active_yet {
220+
return Ok(());
221+
}
222+
170223
fn time_err_to_err(_time_err: mbedtls::x509::InvalidTimeError) -> rustls::Error {
171224
rustls::Error::InvalidCertificate(rustls::CertificateError::BadEncoding)
172225
}
173226

174227
for cert in chain.into_iter() {
175-
let not_after = cert
176-
.not_after()
177-
.map_err(mbedtls_err_into_rustls_err)?
178-
.try_into()
179-
.map_err(time_err_to_err)?;
180-
if now > not_after {
181-
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::Expired));
228+
if !active_check.ignore_expired {
229+
let not_after = cert
230+
.not_after()
231+
.map_err(mbedtls_err_into_rustls_err)?
232+
.try_into()
233+
.map_err(time_err_to_err)?;
234+
if now > not_after {
235+
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::Expired));
236+
}
182237
}
183-
let not_before = cert
184-
.not_before()
185-
.map_err(mbedtls_err_into_rustls_err)?
186-
.try_into()
187-
.map_err(time_err_to_err)?;
188-
if now < not_before {
189-
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidYet));
238+
if !active_check.ignore_not_active_yet {
239+
let not_before = cert
240+
.not_before()
241+
.map_err(mbedtls_err_into_rustls_err)?
242+
.try_into()
243+
.map_err(time_err_to_err)?;
244+
if now < not_before {
245+
return Err(rustls::Error::InvalidCertificate(rustls::CertificateError::NotValidYet));
246+
}
190247
}
191248
}
192249
Ok(())

0 commit comments

Comments
 (0)