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};
27
28pub use rustls::{
29 client::WebPkiServerVerifier, version, CertificateError, ClientConfig, DigitallySignedStruct,
30 Error as RusTlsError, KeyLogFile, RootCertStore, ServerConfig, SignatureScheme, Stream,
31};
32pub use rustls_native_certs::load_native_certs;
33use rustls_pemfile::Item;
34pub use rustls_pki_types::{CertificateDer, PrivateKeyDer, ServerName, UnixTime};
35pub use tokio_rustls::client::TlsStream as ClientTlsStream;
36pub use tokio_rustls::server::TlsStream as ServerTlsStream;
37pub use tokio_rustls::{Accept, Connect, TlsAcceptor, TlsConnector, TlsStream};
38
39pub use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
41
42fn load_file<P>(path: P) -> Result<BufReader<File>>
45where
46 P: AsRef<Path>,
47{
48 File::open(path)
49 .or_err(ErrorType::FileReadError, "Failed to load file")
50 .map(BufReader::new)
51}
52
53fn load_pem_file<P>(path: P) -> Result<Vec<Item>>
55where
56 P: AsRef<Path>,
57{
58 rustls_pemfile::read_all(&mut load_file(path)?)
59 .map(|item_res| {
60 item_res.or_err(
61 ErrorType::InvalidCert,
62 "Certificate in pem file could not be read",
63 )
64 })
65 .collect()
66}
67
68pub fn load_ca_file_into_store<P>(path: P, cert_store: &mut RootCertStore) -> Result<()>
71where
72 P: AsRef<Path>,
73{
74 for pem_item in load_pem_file(path)? {
75 let Item::X509Certificate(content) = pem_item else {
77 return Error::e_explain(
78 ErrorType::InvalidCert,
79 "Pem file contains un-loadable certificate type",
80 );
81 };
82 cert_store.add(content).or_err(
83 ErrorType::InvalidCert,
84 "Failed to load X509 certificate into root store",
85 )?;
86 }
87
88 Ok(())
89}
90
91pub fn load_platform_certs_incl_env_into_store(ca_certs: &mut RootCertStore) -> Result<()> {
93 for cert in load_native_certs()
95 .or_err(ErrorType::InvalidCert, "Failed to load native certificates")?
96 .into_iter()
97 {
98 ca_certs.add(cert).or_err(
99 ErrorType::InvalidCert,
100 "Failed to load native certificate into root store",
101 )?;
102 }
103
104 Ok(())
105}
106
107pub fn load_certs_and_key_files<'a>(
109 cert: &str,
110 key: &str,
111) -> Result<Option<(Vec<CertificateDer<'a>>, PrivateKeyDer<'a>)>> {
112 let certs_file = load_pem_file(cert)?;
113 let key_file = load_pem_file(key)?;
114
115 let certs = certs_file
116 .into_iter()
117 .filter_map(|item| {
118 if let Item::X509Certificate(cert) = item {
119 Some(cert)
120 } else {
121 None
122 }
123 })
124 .collect::<Vec<_>>();
125
126 let private_key_opt = key_file
129 .into_iter()
130 .filter_map(|key_item| match key_item {
131 Item::Pkcs1Key(key) => Some(PrivateKeyDer::from(key)),
132 Item::Pkcs8Key(key) => Some(PrivateKeyDer::from(key)),
133 Item::Sec1Key(key) => Some(PrivateKeyDer::from(key)),
134 _ => None,
135 })
136 .next();
137
138 if let (Some(private_key), false) = (private_key_opt, certs.is_empty()) {
139 Ok(Some((certs, private_key)))
140 } else {
141 Ok(None)
142 }
143}
144
145pub fn load_pem_file_ca(path: &String) -> Result<Vec<u8>> {
147 let mut reader = load_file(path)?;
148 let cas_file_items = rustls_pemfile::certs(&mut reader)
149 .map(|item_res| {
150 item_res.or_err(
151 ErrorType::InvalidCert,
152 "Failed to load certificate from file",
153 )
154 })
155 .collect::<Result<Vec<_>>>()?;
156
157 Ok(cas_file_items
158 .first()
159 .map(|ca| ca.to_vec())
160 .unwrap_or_default())
161}
162
163pub fn load_pem_file_private_key(path: &String) -> Result<Vec<u8>> {
164 Ok(rustls_pemfile::private_key(&mut load_file(path)?)
165 .or_err(
166 ErrorType::InvalidCert,
167 "Failed to load private key from file",
168 )?
169 .map(|key| key.secret_der().to_vec())
170 .unwrap_or_default())
171}
172
173pub fn hash_certificate(cert: &CertificateDer) -> Vec<u8> {
174 let hash = ring::digest::digest(&ring::digest::SHA256, cert.as_ref());
175 hash.as_ref().to_vec()
176}