1use std::{
2 ops::Deref,
3 sync::{Arc, LazyLock, OnceLock},
4};
5
6use nu_engine::command_prelude::IoError;
7use nu_protocol::ShellError;
8use rustls::{
9 DigitallySignedStruct, RootCertStore, SignatureScheme,
10 client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
11 crypto::CryptoProvider,
12 pki_types::{CertificateDer, ServerName, UnixTime},
13};
14use ureq::TlsConnector;
15
16#[derive(Debug)]
50pub struct NuCryptoProvider(OnceLock<Result<Arc<CryptoProvider>, ShellError>>);
51
52pub static CRYPTO_PROVIDER: NuCryptoProvider = NuCryptoProvider(OnceLock::new());
62
63impl NuCryptoProvider {
64 pub fn get(&self) -> Result<Arc<CryptoProvider>, ShellError> {
72 match self.0.get() {
75 Some(val) => val.clone(),
76 None => Err(ShellError::GenericError {
77 error: "tls crypto provider not found".to_string(),
78 msg: "no crypto provider for rustls was defined".to_string(),
79 span: None,
80 help: Some("ensure that nu_command::tls::CRYPTO_PROVIDER is set".to_string()),
81 inner: vec![],
82 }),
83 }
84 }
85
86 pub fn set(&self, f: impl FnOnce() -> Result<CryptoProvider, ShellError>) -> bool {
92 let value = f().map(Arc::new);
93 self.0.set(value).is_ok()
94 }
95
96 pub fn default(&self) -> bool {
102 self.set(|| Ok(rustls::crypto::ring::default_provider()))
103 }
104}
105
106#[cfg(feature = "os")]
107static ROOT_CERT_STORE: LazyLock<Result<Arc<RootCertStore>, ShellError>> = LazyLock::new(|| {
108 let mut roots = RootCertStore::empty();
109
110 let native_certs = rustls_native_certs::load_native_certs();
111
112 let errors: Vec<_> = native_certs
113 .errors
114 .into_iter()
115 .map(|err| match err.kind {
116 rustls_native_certs::ErrorKind::Io { inner, path } => ShellError::Io(
117 IoError::new_internal_with_path(inner, err.context, nu_protocol::location!(), path),
118 ),
119 rustls_native_certs::ErrorKind::Os(error) => ShellError::GenericError {
120 error: error.to_string(),
121 msg: err.context.to_string(),
122 span: None,
123 help: None,
124 inner: vec![],
125 },
126 rustls_native_certs::ErrorKind::Pem(error) => ShellError::GenericError {
127 error: error.to_string(),
128 msg: err.context.to_string(),
129 span: None,
130 help: None,
131 inner: vec![],
132 },
133 _ => ShellError::GenericError {
134 error: String::from("unknown error loading native certs"),
135 msg: err.context.to_string(),
136 span: None,
137 help: None,
138 inner: vec![],
139 },
140 })
141 .collect();
142 if !errors.is_empty() {
143 return Err(ShellError::GenericError {
144 error: String::from("error loading native certs"),
145 msg: String::from("could not load native certs"),
146 span: None,
147 help: None,
148 inner: errors,
149 });
150 }
151
152 for cert in native_certs.certs {
153 roots.add(cert).map_err(|err| ShellError::GenericError {
154 error: err.to_string(),
155 msg: String::from("could not add root cert"),
156 span: None,
157 help: None,
158 inner: vec![],
159 })?;
160 }
161
162 Ok(Arc::new(roots))
163});
164
165#[cfg(not(feature = "os"))]
166static ROOT_CERT_STORE: LazyLock<Result<Arc<RootCertStore>, ShellError>> = LazyLock::new(|| {
167 Ok(Arc::new(rustls::RootCertStore {
168 roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
169 }))
170});
171
172#[doc = include_str!("./tls.rustdoc.md")]
173pub fn tls(allow_insecure: bool) -> Result<impl TlsConnector, ShellError> {
174 let crypto_provider = CRYPTO_PROVIDER.get()?;
175
176 let make_protocol_versions_error = |err: rustls::Error| ShellError::GenericError {
177 error: err.to_string(),
178 msg: "crypto provider is incompatible with protocol versions".to_string(),
179 span: None,
180 help: None,
181 inner: vec![],
182 };
183
184 let client_config = match allow_insecure {
185 false => rustls::ClientConfig::builder_with_provider(crypto_provider)
186 .with_safe_default_protocol_versions()
187 .map_err(make_protocol_versions_error)?
188 .with_root_certificates(ROOT_CERT_STORE.deref().clone()?)
189 .with_no_client_auth(),
190 true => rustls::ClientConfig::builder_with_provider(crypto_provider)
191 .with_safe_default_protocol_versions()
192 .map_err(make_protocol_versions_error)?
193 .dangerous()
194 .with_custom_certificate_verifier(Arc::new(UnsecureServerCertVerifier))
195 .with_no_client_auth(),
196 };
197
198 Ok(Arc::new(client_config))
199}
200
201#[derive(Debug)]
202struct UnsecureServerCertVerifier;
203
204impl ServerCertVerifier for UnsecureServerCertVerifier {
205 fn verify_server_cert(
206 &self,
207 _end_entity: &CertificateDer<'_>,
208 _intermediates: &[CertificateDer<'_>],
209 _server_name: &ServerName<'_>,
210 _ocsp_response: &[u8],
211 _now: UnixTime,
212 ) -> Result<ServerCertVerified, rustls::Error> {
213 Ok(ServerCertVerified::assertion())
214 }
215
216 fn verify_tls12_signature(
217 &self,
218 _message: &[u8],
219 _cert: &CertificateDer<'_>,
220 _dss: &DigitallySignedStruct,
221 ) -> Result<HandshakeSignatureValid, rustls::Error> {
222 Ok(HandshakeSignatureValid::assertion())
223 }
224
225 fn verify_tls13_signature(
226 &self,
227 _message: &[u8],
228 _cert: &CertificateDer<'_>,
229 _dss: &DigitallySignedStruct,
230 ) -> Result<HandshakeSignatureValid, rustls::Error> {
231 Ok(HandshakeSignatureValid::assertion())
232 }
233
234 fn supported_verify_schemes(&self) -> Vec<SignatureScheme> {
235 vec![
236 SignatureScheme::RSA_PKCS1_SHA1,
237 SignatureScheme::ECDSA_SHA1_Legacy,
238 SignatureScheme::RSA_PKCS1_SHA256,
239 SignatureScheme::ECDSA_NISTP256_SHA256,
240 SignatureScheme::RSA_PKCS1_SHA384,
241 SignatureScheme::ECDSA_NISTP384_SHA384,
242 SignatureScheme::RSA_PKCS1_SHA512,
243 SignatureScheme::ECDSA_NISTP521_SHA512,
244 SignatureScheme::RSA_PSS_SHA256,
245 SignatureScheme::RSA_PSS_SHA384,
246 SignatureScheme::RSA_PSS_SHA512,
247 SignatureScheme::ED25519,
248 SignatureScheme::ED448,
249 ]
250 }
251}