1#![warn(clippy::all)]
19
20use std::fs::File;
21use std::io::BufReader;
22use std::path::Path;
23
24use log::warn;
25pub use no_debug::{Ellipses, NoDebug, WithTypeInfo};
26use pingora_error::{Error, ErrorType, OrErr, Result};
27pub use rustls::{version, ClientConfig, RootCertStore, ServerConfig, Stream};
28pub use rustls_native_certs::load_native_certs;
29use rustls_pemfile::Item;
30pub use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName};
31pub use tokio_rustls::client::TlsStream as ClientTlsStream;
32pub use tokio_rustls::server::TlsStream as ServerTlsStream;
33pub use tokio_rustls::{Accept, Connect, TlsAcceptor, TlsConnector, TlsStream};
34
35fn load_file<P>(path: P) -> Result<BufReader<File>>
38where
39 P: AsRef<Path>,
40{
41 File::open(path)
42 .or_err(ErrorType::FileReadError, "Failed to load file")
43 .map(BufReader::new)
44}
45
46fn load_pem_file<P>(path: P) -> Result<Vec<Item>>
48where
49 P: AsRef<Path>,
50{
51 rustls_pemfile::read_all(&mut load_file(path)?)
52 .map(|item_res| {
53 item_res.or_err(
54 ErrorType::InvalidCert,
55 "Certificate in pem file could not be read",
56 )
57 })
58 .collect()
59}
60
61pub fn load_ca_file_into_store<P>(path: P, cert_store: &mut RootCertStore) -> Result<()>
64where
65 P: AsRef<Path>,
66{
67 for pem_item in load_pem_file(path)? {
68 let Item::X509Certificate(content) = pem_item else {
70 return Error::e_explain(
71 ErrorType::InvalidCert,
72 "Pem file contains un-loadable certificate type",
73 );
74 };
75 cert_store.add(content).or_err(
76 ErrorType::InvalidCert,
77 "Failed to load X509 certificate into root store",
78 )?;
79 }
80
81 Ok(())
82}
83
84pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) -> Result<()> {
86 for cert in load_native_certs()
88 .or_err(ErrorType::InvalidCert, "Failed to load native certificates")?
89 .into_iter()
90 {
91 ca_certs.add(cert).or_err(
92 ErrorType::InvalidCert,
93 "Failed to load native certificate into root store",
94 )?;
95 }
96
97 Ok(())
98}
99
100pub fn load_certs_and_key_files<'a>(
102 cert: &str,
103 key: &str,
104) -> Result<Option<(Vec<CertificateDer<'a>>, PrivateKeyDer<'a>)>> {
105 let certs_file = load_pem_file(cert)?;
106 let key_file = load_pem_file(key)?;
107
108 let certs = certs_file
109 .into_iter()
110 .filter_map(|item| {
111 if let Item::X509Certificate(cert) = item {
112 Some(cert)
113 } else {
114 None
115 }
116 })
117 .collect::<Vec<_>>();
118
119 let private_key_opt = key_file
122 .into_iter()
123 .filter_map(|key_item| match key_item {
124 Item::Pkcs1Key(key) => Some(PrivateKeyDer::from(key)),
125 Item::Pkcs8Key(key) => Some(PrivateKeyDer::from(key)),
126 Item::Sec1Key(key) => Some(PrivateKeyDer::from(key)),
127 _ => None,
128 })
129 .next();
130
131 if let (Some(private_key), false) = (private_key_opt, certs.is_empty()) {
132 Ok(Some((certs, private_key)))
133 } else {
134 Ok(None)
135 }
136}
137
138pub fn load_pem_file_ca(path: &String) -> Result<Vec<u8>> {
140 let mut reader = load_file(path)?;
141 let cas_file_items = rustls_pemfile::certs(&mut reader)
142 .map(|item_res| {
143 item_res.or_err(
144 ErrorType::InvalidCert,
145 "Failed to load certificate from file",
146 )
147 })
148 .collect::<Result<Vec<_>>>()?;
149
150 Ok(cas_file_items
151 .first()
152 .map(|ca| ca.to_vec())
153 .unwrap_or_default())
154}
155
156pub fn load_pem_file_private_key(path: &String) -> Result<Vec<u8>> {
157 Ok(rustls_pemfile::private_key(&mut load_file(path)?)
158 .or_err(
159 ErrorType::InvalidCert,
160 "Failed to load private key from file",
161 )?
162 .map(|key| key.secret_der().to_vec())
163 .unwrap_or_default())
164}
165
166pub fn hash_certificate(cert: &CertificateDer) -> Vec<u8> {
167 let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref());
168 hash.as_ref().to_vec()
169}