Skip to content

Commit 9931669

Browse files
s-arashArash Sahebolamri
andauthored
Add rustls-mbedpki-provider crate (#1)
* Create ci.yml * Add rustls-mbedpki-provider crate * Formatting changes * Check for mbedpki-provider warnings in ci.yml * Implement signature verification methods of `ClientCertVerifier` and `ServerSertVerifier` * Address review comments * Add doc comments to public items --------- Co-authored-by: Arash Sahebolamri <[email protected]>
1 parent aaf0d87 commit 9931669

File tree

13 files changed

+1150
-0
lines changed

13 files changed

+1150
-0
lines changed

.github/workflows/ci.yml

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: Rust
2+
3+
on:
4+
push:
5+
branches: [ "master" ]
6+
pull_request:
7+
branches: [ "master" ]
8+
9+
env:
10+
CARGO_TERM_COLOR: always
11+
12+
jobs:
13+
build:
14+
15+
runs-on: ubuntu-latest
16+
17+
steps:
18+
- uses: actions/checkout@v3
19+
20+
- name: check mbedpki-provider
21+
working-directory: rustls-mbedpki-provider
22+
env:
23+
RUSTFLAGS: -D warnings
24+
run: cargo check
25+
26+
- name: Run mbedpki-provider tests
27+
working-directory: rustls-mbedpki-provider
28+
run: cargo test

.rustfmt.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
chain_width=40
2+
max_width = 128
3+
struct_lit_width = 80

README.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# rustls-mbedtls-provider
22

3+
This repository will contain code to allow [mbedtls](https://github.com/fortanix/rust-mbedtls) to be used
4+
as the crypto and PKI provider for [rustls](https://github.com/rustls/rustls).
5+
6+
## PKI provider
7+
Implements [`ClientCertVerifier`](https://docs.rs/rustls/latest/rustls/server/trait.ClientCertVerifier.html) and [`ClientCertVerifier`](https://docs.rs/rustls/latest/rustls/client/trait.ServerCertVerifier.html) traits from `rustls` using mbedtls.
8+
39

410
# Contributing
511

rustls-mbedpki-provider/Cargo.toml

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
[package]
2+
name = "rustls-mbedpki-provider"
3+
version = "0.1.0"
4+
edition = "2021"
5+
description = "Implements rustls PKI traits using mbedtls"
6+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
7+
8+
[dependencies]
9+
rustls = {version = "0.21", features = ["dangerous_configuration"]}
10+
mbedtls = {version = "0.12.0-alpha.1", features = ["x509", "chrono", "std"], default_features = false}
11+
12+
x509-parser = "0.15"
13+
chrono = "0.4"
14+
15+
[dev-dependencies]
16+
rustls-pemfile = "1.0"
Lines changed: 244 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,244 @@
1+
use std::time::SystemTime;
2+
3+
use rustls::{
4+
server::{ClientCertVerified, ClientCertVerifier},
5+
DistinguishedName,
6+
};
7+
8+
use crate::{
9+
mbedtls_err_into_rustls_err, mbedtls_err_into_rustls_err_with_error_msg, rustls_cert_to_mbedtls_cert,
10+
verify_certificates_active, verify_tls_signature,
11+
};
12+
13+
/// A `rustls` `ClientCertVerifier` implemented using the PKI functionality of
14+
/// `mbedtls`
15+
#[derive(Clone)]
16+
pub struct MbedTlsClientCertVerifier {
17+
trusted_cas: mbedtls::alloc::List<mbedtls::x509::Certificate>,
18+
root_subjects: Vec<rustls::DistinguishedName>,
19+
}
20+
21+
impl MbedTlsClientCertVerifier {
22+
/// Constructs a new `MbedTlsClientCertVerifier` object given the provided trusted certificate authority
23+
/// certificates.
24+
///
25+
/// Returns an error if any of the certificates are invalid.
26+
pub fn new<'a>(trusted_cas: impl IntoIterator<Item = &'a rustls::Certificate>) -> mbedtls::Result<Self> {
27+
let trusted_cas = trusted_cas
28+
.into_iter()
29+
.map(rustls_cert_to_mbedtls_cert)
30+
.collect::<mbedtls::Result<Vec<_>>>()?
31+
.into_iter()
32+
.collect();
33+
Self::new_from_mbedtls_trusted_cas(trusted_cas)
34+
}
35+
36+
/// Constructs a new `MbedTlsClientCertVerifier` object given the provided trusted certificate authority
37+
/// certificates.
38+
pub fn new_from_mbedtls_trusted_cas(
39+
trusted_cas: mbedtls::alloc::List<mbedtls::x509::Certificate>,
40+
) -> mbedtls::Result<Self> {
41+
let mut root_subjects = vec![];
42+
for ca in trusted_cas.iter() {
43+
root_subjects.push(DistinguishedName::from(ca.subject_raw()?));
44+
}
45+
Ok(Self { trusted_cas, root_subjects })
46+
}
47+
48+
/// The certificate authority certificates used to construct this object
49+
pub fn trusted_cas(&self) -> &mbedtls::alloc::List<mbedtls::x509::Certificate> {
50+
&self.trusted_cas
51+
}
52+
53+
/// the Subjects of the client authentication trust anchors to share with the client when
54+
/// requesting client authentication, extractd from CA certificates.
55+
pub fn root_subjects(&self) -> &[DistinguishedName] {
56+
self.root_subjects.as_ref()
57+
}
58+
}
59+
60+
impl ClientCertVerifier for MbedTlsClientCertVerifier {
61+
fn client_auth_root_subjects(&self) -> &[DistinguishedName] {
62+
&self.root_subjects
63+
}
64+
65+
fn verify_client_cert(
66+
&self,
67+
end_entity: &rustls::Certificate,
68+
intermediates: &[rustls::Certificate],
69+
now: SystemTime,
70+
) -> Result<rustls::server::ClientCertVerified, rustls::Error> {
71+
let now = chrono::DateTime::<chrono::Local>::from(now).naive_local();
72+
73+
let chain: mbedtls::alloc::List<_> = [end_entity]
74+
.into_iter()
75+
.chain(intermediates)
76+
.map(rustls_cert_to_mbedtls_cert)
77+
.collect::<mbedtls::Result<Vec<_>>>()
78+
.map_err(mbedtls_err_into_rustls_err)?
79+
.into_iter()
80+
.collect();
81+
82+
verify_certificates_active(chain.iter().map(|c| &**c), now)?;
83+
84+
let mut error_msg = String::default();
85+
mbedtls::x509::Certificate::verify(&chain, &self.trusted_cas, None, Some(&mut error_msg))
86+
.map_err(|e| mbedtls_err_into_rustls_err_with_error_msg(e, &error_msg))?;
87+
88+
Ok(ClientCertVerified::assertion())
89+
}
90+
91+
fn verify_tls12_signature(
92+
&self,
93+
message: &[u8],
94+
cert: &rustls::Certificate,
95+
dss: &rustls::DigitallySignedStruct,
96+
) -> Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
97+
verify_tls_signature(message, cert, dss, false)
98+
}
99+
100+
fn verify_tls13_signature(
101+
&self,
102+
message: &[u8],
103+
cert: &rustls::Certificate,
104+
dss: &rustls::DigitallySignedStruct,
105+
) -> Result<rustls::client::HandshakeSignatureValid, rustls::Error> {
106+
verify_tls_signature(message, cert, dss, true)
107+
}
108+
}
109+
110+
// ../test-data/rsa/client.fullchain has these three certificates:
111+
// cert subject: CN=ponytown client, cert issuer: CN=ponytown RSA level 2 intermediate
112+
// cert subject: CN=ponytown RSA level 2 intermediate, cert issuer: CN=ponytown RSA CA
113+
// cert subject: CN=ponytown RSA CA, cert issuer: CN=ponytown RSA CA
114+
#[cfg(test)]
115+
mod tests {
116+
117+
use chrono::DateTime;
118+
use rustls::{
119+
server::ClientCertVerifier, Certificate, ClientConfig, ClientConnection, RootCertStore, ServerConfig, ServerConnection,
120+
};
121+
use std::{sync::Arc, time::SystemTime};
122+
123+
use super::MbedTlsClientCertVerifier;
124+
use crate::tests_common::{do_handshake_until_error, get_chain, get_key};
125+
126+
fn server_config_with_verifier(client_cert_verifier: MbedTlsClientCertVerifier) -> ServerConfig {
127+
ServerConfig::builder()
128+
.with_safe_defaults()
129+
.with_client_cert_verifier(Arc::new(client_cert_verifier))
130+
.with_single_cert(
131+
get_chain(include_bytes!("../test-data/rsa/end.fullchain")),
132+
get_key(include_bytes!("../test-data/rsa/end.key")),
133+
)
134+
.unwrap()
135+
}
136+
137+
#[test]
138+
fn connection_client_cert_verifier() {
139+
let client_config = ClientConfig::builder().with_safe_defaults();
140+
let root_ca = Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec());
141+
let mut root_store = RootCertStore::empty();
142+
root_store.add(&root_ca).unwrap();
143+
let cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
144+
145+
let client_config = client_config
146+
.with_root_certificates(root_store)
147+
.with_client_auth_cert(cert_chain, get_key(include_bytes!("../test-data/rsa/client.key")))
148+
.unwrap();
149+
150+
let client_cert_verifier = MbedTlsClientCertVerifier::new([&root_ca]).unwrap();
151+
152+
let server_config = server_config_with_verifier(client_cert_verifier);
153+
154+
let mut client_conn = ClientConnection::new(Arc::new(client_config), "localhost".try_into().unwrap()).unwrap();
155+
let mut server_conn = ServerConnection::new(Arc::new(server_config)).unwrap();
156+
157+
assert!(do_handshake_until_error(&mut client_conn, &mut server_conn).is_ok());
158+
}
159+
160+
fn test_connection_client_cert_verifier_with_invalid_certs(invalid_cert_chain: Vec<Certificate>) {
161+
let client_config = ClientConfig::builder().with_safe_defaults();
162+
let root_ca = Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec());
163+
let mut root_store = RootCertStore::empty();
164+
root_store.add(&root_ca).unwrap();
165+
166+
let client_config = client_config
167+
.with_root_certificates(root_store)
168+
.with_client_auth_cert(invalid_cert_chain, get_key(include_bytes!("../test-data/rsa/client.key")))
169+
.unwrap();
170+
171+
let client_cert_verifier = MbedTlsClientCertVerifier::new([&root_ca]).unwrap();
172+
173+
let server_config = server_config_with_verifier(client_cert_verifier);
174+
175+
let mut client_conn = ClientConnection::new(Arc::new(client_config), "localhost".try_into().unwrap()).unwrap();
176+
let mut server_conn = ServerConnection::new(Arc::new(server_config)).unwrap();
177+
178+
let res = do_handshake_until_error(&mut client_conn, &mut server_conn);
179+
assert!(matches!(res, Err(rustls::Error::InvalidCertificate(_))));
180+
}
181+
182+
#[test]
183+
fn connection_client_cert_verifier_with_invalid_certs() {
184+
let cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
185+
186+
let mut invalid_chain1 = cert_chain.clone();
187+
invalid_chain1.remove(1);
188+
189+
let mut invalid_chain2 = cert_chain.clone();
190+
invalid_chain2.remove(0);
191+
192+
let mut invalid_chain3 = cert_chain.clone();
193+
invalid_chain3.swap(0, 1);
194+
195+
for invalid_chain in [invalid_chain1, invalid_chain2, invalid_chain3] {
196+
test_connection_client_cert_verifier_with_invalid_certs(invalid_chain);
197+
}
198+
}
199+
200+
#[test]
201+
fn client_cert_verifier_valid_chain() {
202+
let cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
203+
let trusted_cas = [Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec())];
204+
205+
let verifier = MbedTlsClientCertVerifier::new(trusted_cas.iter()).unwrap();
206+
207+
let now = SystemTime::from(chrono::DateTime::parse_from_rfc3339("2023-11-26T12:00:00+00:00").unwrap());
208+
209+
assert!(verifier
210+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
211+
.is_ok());
212+
}
213+
214+
#[test]
215+
fn client_cert_verifier_broken_chain() {
216+
let mut cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
217+
cert_chain.remove(1);
218+
let trusted_cas = [Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec())];
219+
220+
let verifier = MbedTlsClientCertVerifier::new(trusted_cas.iter()).unwrap();
221+
222+
let now = SystemTime::from(DateTime::parse_from_rfc3339("2023-11-26T12:00:00+00:00").unwrap());
223+
224+
let verify_res = verifier.verify_client_cert(&cert_chain[0], &cert_chain[1..], now);
225+
assert!(matches!(verify_res, Err(rustls::Error::InvalidCertificate(_))));
226+
}
227+
228+
#[test]
229+
fn client_cert_verifier_expired_certs() {
230+
let cert_chain = get_chain(include_bytes!("../test-data/rsa/client.fullchain"));
231+
let trusted_cas = [Certificate(include_bytes!("../test-data/rsa/ca.der").to_vec())];
232+
233+
let verifier = MbedTlsClientCertVerifier::new(trusted_cas.iter()).unwrap();
234+
235+
let now = SystemTime::from(DateTime::parse_from_rfc3339("2052-11-26T12:00:00+00:00").unwrap());
236+
237+
assert_eq!(
238+
verifier
239+
.verify_client_cert(&cert_chain[0], &cert_chain[1..], now)
240+
.unwrap_err(),
241+
rustls::Error::InvalidCertificate(rustls::CertificateError::Expired)
242+
);
243+
}
244+
}

0 commit comments

Comments
 (0)