s2n_quic_rustls/
certificate.rs

1// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2// SPDX-License-Identifier: Apache-2.0
3
4use crate::Error;
5
6macro_rules! cert_type {
7    ($name:ident, $trait:ident, $method:ident, $inner:ty) => {
8        pub struct $name(pub(crate) $inner);
9
10        pub trait $trait {
11            fn $method(self) -> Result<$name, Error>;
12        }
13
14        impl $trait for $name {
15            fn $method(self) -> Result<$name, Error> {
16                Ok(self)
17            }
18        }
19
20        impl $trait for String {
21            fn $method(self) -> Result<$name, Error> {
22                let cert = pem::$method(self.as_bytes())?;
23                Ok($name(cert))
24            }
25        }
26
27        impl $trait for &String {
28            fn $method(self) -> Result<$name, Error> {
29                let cert = pem::$method(self.as_bytes())?;
30                Ok($name(cert))
31            }
32        }
33
34        impl $trait for &str {
35            fn $method(self) -> Result<$name, Error> {
36                let cert = pem::$method(self.as_bytes())?;
37                Ok($name(cert))
38            }
39        }
40
41        impl $trait for Vec<u8> {
42            fn $method(self) -> Result<$name, Error> {
43                let cert = der::$method(self)?;
44                Ok($name(cert))
45            }
46        }
47
48        impl $trait for &[u8] {
49            fn $method(self) -> Result<$name, Error> {
50                self.to_vec().$method()
51            }
52        }
53
54        impl $trait for &std::path::Path {
55            fn $method(self) -> Result<$name, Error> {
56                match self.extension() {
57                    Some(ext) if ext == "der" => {
58                        let der = std::fs::read(self)?;
59                        der.$method()
60                    }
61                    _ => {
62                        let pem = std::fs::read_to_string(self)?;
63                        pem.$method()
64                    }
65                }
66            }
67        }
68    };
69}
70
71cert_type!(
72    PrivateKey,
73    IntoPrivateKey,
74    into_private_key,
75    rustls::pki_types::PrivateKeyDer<'static>
76);
77cert_type!(
78    Certificate,
79    IntoCertificate,
80    into_certificate,
81    Vec<rustls::pki_types::CertificateDer<'static>>
82);
83
84impl IntoCertificate for Vec<Vec<u8>> {
85    fn into_certificate(self) -> Result<Certificate, Error> {
86        let mut certs = vec![];
87        for der in self {
88            let der = der.into_certificate()?;
89            certs.extend(der.0);
90        }
91        Ok(Certificate(certs))
92    }
93}
94
95mod pem {
96    use rustls::{
97        pki_types::{CertificateDer, PrivateKeyDer},
98        Error,
99    };
100    use rustls_pki_types::pem::PemObject;
101
102    pub fn into_certificate(contents: &[u8]) -> Result<Vec<CertificateDer<'static>>, Error> {
103        let mut cursor = std::io::Cursor::new(contents);
104        rustls_pki_types::CertificateDer::pem_reader_iter(&mut cursor)
105            .map(|cert| cert.map_err(|_| Error::General("Could not read certificate".to_string())))
106            .collect()
107    }
108
109    pub fn into_private_key(contents: &[u8]) -> Result<PrivateKeyDer<'static>, Error> {
110        let mut cursor = std::io::Cursor::new(contents);
111
112        macro_rules! parse_key {
113            ($parser:ident, $key_type:expr) => {
114                cursor.set_position(0);
115
116                let keys: Result<Vec<_>, Error> = $parser(&mut cursor)
117                    .map(|key| {
118                        key.map_err(|_| {
119                            Error::General("Could not load any private keys".to_string())
120                        })
121                    })
122                    .collect();
123                match keys {
124                    // try the next parser
125                    Err(_) => (),
126                    // try the next parser
127                    Ok(keys) if keys.is_empty() => (),
128                    Ok(mut keys) if keys.len() == 1 => {
129                        return Ok($key_type(keys.pop().unwrap()));
130                    }
131                    Ok(keys) => {
132                        return Err(Error::General(format!(
133                            "Unexpected number of keys: {} (only 1 supported)",
134                            keys.len()
135                        )));
136                    }
137                }
138            };
139        }
140
141        // attempt to parse PKCS8 encoded key. Returns early if a key is found
142        parse_key!(pkcs8_private_keys, PrivateKeyDer::Pkcs8);
143        // attempt to parse RSA key. Returns early if a key is found
144        parse_key!(rsa_private_keys, PrivateKeyDer::Pkcs1);
145        // attempt to parse a SEC1-encoded EC key. Returns early if a key is found
146        parse_key!(ec_private_keys, PrivateKeyDer::Sec1);
147
148        Err(Error::General(
149            "could not load any valid private keys".to_string(),
150        ))
151    }
152
153    // parser wrapper for pkcs #8 encoded private keys
154    fn pkcs8_private_keys<R: std::io::Read>(
155        reader: &mut R,
156    ) -> impl Iterator<
157        Item = Result<rustls_pki_types::PrivatePkcs8KeyDer<'static>, rustls_pki_types::pem::Error>,
158    > + '_ {
159        rustls_pki_types::PrivatePkcs8KeyDer::pem_reader_iter(reader)
160            .map(|result| result.map(|key| key.clone_key()))
161    }
162
163    // parser wrapper for pkcs #1 encoded private keys
164    fn rsa_private_keys<R: std::io::Read>(
165        reader: &mut R,
166    ) -> impl Iterator<
167        Item = Result<rustls_pki_types::PrivatePkcs1KeyDer<'static>, rustls_pki_types::pem::Error>,
168    > + '_ {
169        rustls_pki_types::PrivatePkcs1KeyDer::pem_reader_iter(reader)
170            .map(|result| result.map(|key| key.clone_key()))
171    }
172
173    // parser wrapper for sec1 encoded private keys
174    fn ec_private_keys<R: std::io::Read>(
175        reader: &mut R,
176    ) -> impl Iterator<
177        Item = Result<rustls_pki_types::PrivateSec1KeyDer<'static>, rustls_pki_types::pem::Error>,
178    > + '_ {
179        rustls_pki_types::PrivateSec1KeyDer::pem_reader_iter(reader)
180            .map(|result| result.map(|key| key.clone_key()))
181    }
182}
183
184mod der {
185    use rustls::{
186        pki_types::{CertificateDer, PrivateKeyDer},
187        Error,
188    };
189
190    pub fn into_certificate(contents: Vec<u8>) -> Result<Vec<CertificateDer<'static>>, Error> {
191        // der files only have a single cert
192        Ok(vec![CertificateDer::from(contents)])
193    }
194
195    pub fn into_private_key(contents: Vec<u8>) -> Result<PrivateKeyDer<'static>, Error> {
196        // PKCS #8 is used since it's capable of encoding RSA as well as other key
197        // types (eg. ECDSA). Additionally, multiple attacks have been discovered
198        // against PKCS #1 so PKCS #8 should be preferred.
199        //
200        // https://stackoverflow.com/a/48960291
201        Ok(PrivateKeyDer::Pkcs8(contents.into()))
202    }
203}
204
205#[cfg(test)]
206mod tests {
207    use super::*;
208    use s2n_quic_core::crypto::tls::testing::certificates::*;
209
210    #[test]
211    fn load_pem() {
212        let _ = CERT_PEM.into_certificate().unwrap();
213        let _ = CERT_PKCS1_PEM.into_certificate().unwrap();
214        // PKCS #8 encoded key
215        let _ = KEY_PEM.into_private_key().unwrap();
216        // PKCS #1 encoded key
217        let _ = KEY_PKCS1_PEM.into_private_key().unwrap();
218    }
219
220    #[test]
221    fn load_der() {
222        let _ = CERT_DER.into_certificate().unwrap();
223        let _ = KEY_DER.into_private_key().unwrap();
224    }
225}