diff options
author | Bartek Iwańczuk <biwanczuk@gmail.com> | 2024-06-13 21:41:26 +0100 |
---|---|---|
committer | GitHub <noreply@github.com> | 2024-06-13 22:41:26 +0200 |
commit | fb31eaa9ca59f6daaee0210d5cd206185c7041b9 (patch) | |
tree | 0c4ebc81ed7b44b683f31281accc47d451d09718 /ext | |
parent | 518e4d3b3a93838e0f2dbcc4d3b79f8f395db563 (diff) |
chore: upgrade to reqwest 0.12.4 and rustls 0.22 (#24056)
This commit updates Deno to use `reqwest` at 0.12.4
and `rustls` at 0.22. Other related crates were updated
as well to match versions accepted by `reqwest` and `rustls`.
Note: we are not using the latest available `rustls` yet,
but this upgrade was non-trivial already, so a bump to
0.23 for `rustls` will be done in a separate commit.
Closes #23370
---------
Signed-off-by: Ryan Dahl <ry@tinyclouds.org>
Signed-off-by: Bartek Iwańczuk <biwanczuk@gmail.com>
Co-authored-by: Ryan Dahl <ry@tinyclouds.org>
Co-authored-by: Divy Srivastava <dj.srivastava23@gmail.com>
Diffstat (limited to 'ext')
-rw-r--r-- | ext/fetch/Cargo.toml | 2 | ||||
-rw-r--r-- | ext/fetch/fs_fetch_handler.rs | 2 | ||||
-rw-r--r-- | ext/fetch/lib.rs | 13 | ||||
-rw-r--r-- | ext/kv/Cargo.toml | 2 | ||||
-rw-r--r-- | ext/kv/remote.rs | 44 | ||||
-rw-r--r-- | ext/net/ops_tls.rs | 28 | ||||
-rw-r--r-- | ext/node/Cargo.toml | 4 | ||||
-rw-r--r-- | ext/node/ops/http2.rs | 14 | ||||
-rw-r--r-- | ext/tls/Cargo.toml | 4 | ||||
-rw-r--r-- | ext/tls/lib.rs | 201 | ||||
-rw-r--r-- | ext/tls/testdata/README | 4 | ||||
-rw-r--r-- | ext/tls/testdata/example1_cert.der | bin | 0 -> 929 bytes | |||
-rw-r--r-- | ext/tls/testdata/example1_prikey.der | bin | 0 -> 1190 bytes | |||
-rw-r--r-- | ext/tls/testdata/example2_cert.der | bin | 0 -> 929 bytes | |||
-rw-r--r-- | ext/tls/testdata/example2_prikey.der | bin | 0 -> 1191 bytes | |||
-rw-r--r-- | ext/tls/tls_key.rs | 46 | ||||
-rw-r--r-- | ext/websocket/lib.rs | 11 |
17 files changed, 220 insertions, 155 deletions
diff --git a/ext/fetch/Cargo.toml b/ext/fetch/Cargo.toml index 12a6b5cc9..d9899cf18 100644 --- a/ext/fetch/Cargo.toml +++ b/ext/fetch/Cargo.toml @@ -20,7 +20,7 @@ deno_core.workspace = true deno_permissions.workspace = true deno_tls.workspace = true dyn-clone = "1" -http_v02.workspace = true +http.workspace = true reqwest.workspace = true serde.workspace = true serde_json.workspace = true diff --git a/ext/fetch/fs_fetch_handler.rs b/ext/fetch/fs_fetch_handler.rs index 8f83cef88..29bad5992 100644 --- a/ext/fetch/fs_fetch_handler.rs +++ b/ext/fetch/fs_fetch_handler.rs @@ -31,7 +31,7 @@ impl FetchHandler for FsFetchHandler { let file = tokio::fs::File::open(path).map_err(|_| ()).await?; let stream = ReaderStream::new(file); let body = reqwest::Body::wrap_stream(stream); - let response = http_v02::Response::builder() + let response = http::Response::builder() .status(StatusCode::OK) .body(body) .map_err(|_| ())? diff --git a/ext/fetch/lib.rs b/ext/fetch/lib.rs index 066f1685b..75ceb86d9 100644 --- a/ext/fetch/lib.rs +++ b/ext/fetch/lib.rs @@ -47,8 +47,8 @@ use data_url::DataUrl; use deno_tls::TlsKey; use deno_tls::TlsKeys; use deno_tls::TlsKeysHolder; -use http_v02::header::CONTENT_LENGTH; -use http_v02::Uri; +use http::header::CONTENT_LENGTH; +use http::Uri; use reqwest::header::HeaderMap; use reqwest::header::HeaderName; use reqwest::header::HeaderValue; @@ -449,12 +449,9 @@ where .decode_to_vec() .map_err(|e| type_error(format!("{e:?}")))?; - let response = http_v02::Response::builder() - .status(http_v02::StatusCode::OK) - .header( - http_v02::header::CONTENT_TYPE, - data_url.mime_type().to_string(), - ) + let response = http::Response::builder() + .status(http::StatusCode::OK) + .header(http::header::CONTENT_TYPE, data_url.mime_type().to_string()) .body(reqwest::Body::from(body))?; let fut = async move { Ok(Ok(Response::from(response))) }; diff --git a/ext/kv/Cargo.toml b/ext/kv/Cargo.toml index d95afd942..c4605fa22 100644 --- a/ext/kv/Cargo.toml +++ b/ext/kv/Cargo.toml @@ -17,6 +17,7 @@ path = "lib.rs" anyhow.workspace = true async-trait.workspace = true base64.workspace = true +bytes.workspace = true chrono = { workspace = true, features = ["now"] } deno_core.workspace = true deno_fetch.workspace = true @@ -27,6 +28,7 @@ denokv_proto.workspace = true denokv_remote.workspace = true denokv_sqlite.workspace = true faster-hex.workspace = true +http.workspace = true log.workspace = true num-bigint.workspace = true prost.workspace = true diff --git a/ext/kv/remote.rs b/ext/kv/remote.rs index a1273e78b..7541b5a06 100644 --- a/ext/kv/remote.rs +++ b/ext/kv/remote.rs @@ -8,10 +8,14 @@ use std::sync::Arc; use crate::DatabaseHandler; use anyhow::Context; use async_trait::async_trait; +use bytes::Bytes; use deno_core::error::type_error; use deno_core::error::AnyError; +use deno_core::futures::Stream; +use deno_core::futures::TryStreamExt as _; use deno_core::OpState; use deno_fetch::create_http_client; +use deno_fetch::reqwest; use deno_fetch::CreateHttpClientOptions; use deno_tls::rustls::RootCertStore; use deno_tls::Proxy; @@ -19,6 +23,8 @@ use deno_tls::RootCertStoreProvider; use deno_tls::TlsKeys; use denokv_remote::MetadataEndpoint; use denokv_remote::Remote; +use denokv_remote::RemoteResponse; +use denokv_remote::RemoteTransport; use url::Url; #[derive(Clone)] @@ -102,11 +108,44 @@ impl<P: RemoteDbHandlerPermissions + 'static> denokv_remote::RemotePermissions } } +#[derive(Clone)] +pub struct ReqwestClient(reqwest::Client); +pub struct ReqwestResponse(reqwest::Response); + +impl RemoteTransport for ReqwestClient { + type Response = ReqwestResponse; + async fn post( + &self, + url: Url, + headers: http::HeaderMap, + body: Bytes, + ) -> Result<(Url, http::StatusCode, Self::Response), anyhow::Error> { + let res = self.0.post(url).headers(headers).body(body).send().await?; + let url = res.url().clone(); + let status = res.status(); + Ok((url, status, ReqwestResponse(res))) + } +} + +impl RemoteResponse for ReqwestResponse { + async fn bytes(self) -> Result<Bytes, anyhow::Error> { + Ok(self.0.bytes().await?) + } + fn stream( + self, + ) -> impl Stream<Item = Result<Bytes, anyhow::Error>> + Send + Sync { + self.0.bytes_stream().map_err(|e| e.into()) + } + async fn text(self) -> Result<String, anyhow::Error> { + Ok(self.0.text().await?) + } +} + #[async_trait(?Send)] impl<P: RemoteDbHandlerPermissions + 'static> DatabaseHandler for RemoteDbHandler<P> { - type DB = Remote<PermissionChecker<P>>; + type DB = Remote<PermissionChecker<P>, ReqwestClient>; async fn open( &self, @@ -162,13 +201,14 @@ impl<P: RemoteDbHandlerPermissions + 'static> DatabaseHandler http2: true, }, )?; + let reqwest_client = ReqwestClient(client); let permissions = PermissionChecker { state: state.clone(), _permissions: PhantomData, }; - let remote = Remote::new(client, permissions, metadata_endpoint); + let remote = Remote::new(reqwest_client, permissions, metadata_endpoint); Ok(remote) } diff --git a/ext/net/ops_tls.rs b/ext/net/ops_tls.rs index c52985908..ccea8eb75 100644 --- a/ext/net/ops_tls.rs +++ b/ext/net/ops_tls.rs @@ -31,11 +31,11 @@ use deno_tls::create_client_config; use deno_tls::load_certs; use deno_tls::load_private_keys; use deno_tls::new_resolver; -use deno_tls::rustls::Certificate; +use deno_tls::rustls::pki_types::ServerName; use deno_tls::rustls::ClientConnection; -use deno_tls::rustls::PrivateKey; use deno_tls::rustls::ServerConfig; -use deno_tls::rustls::ServerName; +use deno_tls::webpki::types::CertificateDer; +use deno_tls::webpki::types::PrivateKeyDer; use deno_tls::ServerConfigProvider; use deno_tls::SocketUse; use deno_tls::TlsKey; @@ -48,7 +48,6 @@ use serde::Deserialize; use std::borrow::Cow; use std::cell::RefCell; use std::convert::From; -use std::convert::TryFrom; use std::fs::File; use std::io::BufReader; use std::io::ErrorKind; @@ -304,14 +303,14 @@ where { let rid = args.rid; let hostname = match &*args.hostname { - "" => "localhost", - n => n, + "" => "localhost".to_string(), + n => n.to_string(), }; { let mut s = state.borrow_mut(); let permissions = s.borrow_mut::<NP>(); - permissions.check_net(&(hostname, Some(0)), "Deno.startTls()")?; + permissions.check_net(&(&hostname, Some(0)), "Deno.startTls()")?; } let ca_certs = args @@ -320,8 +319,8 @@ where .map(|s| s.into_bytes()) .collect::<Vec<_>>(); - let hostname_dns = - ServerName::try_from(hostname).map_err(|_| invalid_hostname(hostname))?; + let hostname_dns = ServerName::try_from(hostname.to_string()) + .map_err(|_| invalid_hostname(&hostname))?; let unsafely_ignore_certificate_errors = state .borrow() @@ -422,9 +421,9 @@ where .borrow::<DefaultTlsOptions>() .root_cert_store()?; let hostname_dns = if let Some(server_name) = args.server_name { - ServerName::try_from(server_name.as_str()) + ServerName::try_from(server_name) } else { - ServerName::try_from(&*addr.hostname) + ServerName::try_from(addr.hostname.clone()) } .map_err(|_| invalid_hostname(&addr.hostname))?; let connect_addr = resolve_addr(&addr.hostname, addr.port) @@ -466,7 +465,9 @@ where Ok((rid, IpAddr::from(local_addr), IpAddr::from(remote_addr))) } -fn load_certs_from_file(path: &str) -> Result<Vec<Certificate>, AnyError> { +fn load_certs_from_file( + path: &str, +) -> Result<Vec<CertificateDer<'static>>, AnyError> { let cert_file = File::open(path)?; let reader = &mut BufReader::new(cert_file); load_certs(reader) @@ -474,7 +475,7 @@ fn load_certs_from_file(path: &str) -> Result<Vec<Certificate>, AnyError> { fn load_private_keys_from_file( path: &str, -) -> Result<Vec<PrivateKey>, AnyError> { +) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { let key_bytes = std::fs::read(path)?; load_private_keys(&key_bytes) } @@ -523,7 +524,6 @@ where TlsKeys::Null => Err(anyhow!("Deno.listenTls requires a key")), TlsKeys::Static(TlsKey(cert, key)) => { let mut tls_config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() .with_single_cert(cert, key) .map_err(|e| anyhow!(e))?; diff --git a/ext/node/Cargo.toml b/ext/node/Cargo.toml index a6b7b5c9b..83ce49060 100644 --- a/ext/node/Cargo.toml +++ b/ext/node/Cargo.toml @@ -35,10 +35,10 @@ ecb.workspace = true elliptic-curve.workspace = true errno = "0.2.8" faster-hex.workspace = true -h2 = { version = "0.3.26", features = ["unstable"] } +h2.workspace = true hkdf.workspace = true home = "0.5.9" -http_v02.workspace = true +http.workspace = true idna = "0.3.0" indexmap.workspace = true k256 = "0.13.1" diff --git a/ext/node/ops/http2.rs b/ext/node/ops/http2.rs index abf7eae5d..d12e108e6 100644 --- a/ext/node/ops/http2.rs +++ b/ext/node/ops/http2.rs @@ -26,11 +26,11 @@ use deno_net::raw::NetworkStream; use h2; use h2::Reason; use h2::RecvStream; -use http_v02; -use http_v02::request::Parts; -use http_v02::HeaderMap; -use http_v02::Response; -use http_v02::StatusCode; +use http; +use http::request::Parts; +use http::HeaderMap; +use http::Response; +use http::StatusCode; use reqwest::header::HeaderName; use reqwest::header::HeaderValue; use url::Url; @@ -311,7 +311,7 @@ pub async fn op_http2_client_request( let url = url.join(&pseudo_path)?; - let mut req = http_v02::Request::builder() + let mut req = http::Request::builder() .uri(url.as_str()) .method(pseudo_method.as_str()); @@ -383,7 +383,7 @@ pub async fn op_http2_client_send_trailers( .get::<Http2ClientStream>(stream_rid)?; let mut stream = RcRef::map(&resource, |r| &r.stream).borrow_mut().await; - let mut trailers_map = http_v02::HeaderMap::new(); + let mut trailers_map = http::HeaderMap::new(); for (name, value) in trailers { trailers_map.insert( HeaderName::from_bytes(&name).unwrap(), diff --git a/ext/tls/Cargo.toml b/ext/tls/Cargo.toml index b1ff5a2da..800126633 100644 --- a/ext/tls/Cargo.toml +++ b/ext/tls/Cargo.toml @@ -15,8 +15,8 @@ path = "lib.rs" [dependencies] deno_core.workspace = true -deno_native_certs = "0.2.0" -rustls = { workspace = true, features = ["dangerous_configuration"] } +deno_native_certs = "0.3.0" +rustls.workspace = true rustls-pemfile.workspace = true rustls-tokio-stream.workspace = true rustls-webpki.workspace = true diff --git a/ext/tls/lib.rs b/ext/tls/lib.rs index 5122264bf..c4d548ccf 100644 --- a/ext/tls/lib.rs +++ b/ext/tls/lib.rs @@ -1,7 +1,9 @@ // Copyright 2018-2024 the Deno authors. All rights reserved. MIT license. - pub use deno_native_certs; pub use rustls; +use rustls::pki_types::CertificateDer; +use rustls::pki_types::PrivateKeyDer; +use rustls::pki_types::ServerName; pub use rustls_pemfile; pub use rustls_tokio_stream::*; pub use webpki; @@ -11,14 +13,14 @@ use deno_core::anyhow::anyhow; use deno_core::error::custom_error; use deno_core::error::AnyError; -use rustls::client::HandshakeSignatureValid; -use rustls::client::ServerCertVerified; -use rustls::client::ServerCertVerifier; -use rustls::client::WebPkiVerifier; +use rustls::client::danger::HandshakeSignatureValid; +use rustls::client::danger::ServerCertVerified; +use rustls::client::danger::ServerCertVerifier; +use rustls::client::WebPkiServerVerifier; use rustls::ClientConfig; use rustls::DigitallySignedStruct; use rustls::Error; -use rustls::ServerName; +use rustls::RootCertStore; use rustls_pemfile::certs; use rustls_pemfile::ec_private_keys; use rustls_pemfile::pkcs8_private_keys; @@ -27,16 +29,12 @@ use serde::Deserialize; use std::io::BufRead; use std::io::BufReader; use std::io::Cursor; +use std::net::IpAddr; use std::sync::Arc; -use std::time::SystemTime; mod tls_key; pub use tls_key::*; -pub type Certificate = rustls::Certificate; -pub type PrivateKey = rustls::PrivateKey; -pub type RootCertStore = rustls::RootCertStore; - /// Lazily resolves the root cert store. /// /// This was done because the root cert store is not needed in all cases @@ -48,56 +46,59 @@ pub trait RootCertStoreProvider: Send + Sync { // This extension has no runtime apis, it only exports some shared native functions. deno_core::extension!(deno_tls); -struct DefaultSignatureVerification; +#[derive(Debug)] +pub struct NoCertificateVerification { + pub ic_allowlist: Vec<String>, + default_verifier: Arc<WebPkiServerVerifier>, +} -impl ServerCertVerifier for DefaultSignatureVerification { - fn verify_server_cert( - &self, - _end_entity: &Certificate, - _intermediates: &[Certificate], - _server_name: &ServerName, - _scts: &mut dyn Iterator<Item = &[u8]>, - _ocsp_response: &[u8], - _now: SystemTime, - ) -> Result<ServerCertVerified, Error> { - Err(Error::General("Should not be used".to_string())) +impl NoCertificateVerification { + pub fn new(ic_allowlist: Vec<String>) -> Self { + Self { + ic_allowlist, + default_verifier: WebPkiServerVerifier::builder( + create_default_root_cert_store().into(), + ) + .build() + .unwrap(), + } } } -pub struct NoCertificateVerification(pub Vec<String>); - impl ServerCertVerifier for NoCertificateVerification { + fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> { + self.default_verifier.supported_verify_schemes() + } + fn verify_server_cert( &self, - end_entity: &Certificate, - intermediates: &[Certificate], - server_name: &ServerName, - scts: &mut dyn Iterator<Item = &[u8]>, + end_entity: &rustls::pki_types::CertificateDer<'_>, + intermediates: &[rustls::pki_types::CertificateDer<'_>], + server_name: &rustls::pki_types::ServerName<'_>, ocsp_response: &[u8], - now: SystemTime, + now: rustls::pki_types::UnixTime, ) -> Result<ServerCertVerified, Error> { - if self.0.is_empty() { + if self.ic_allowlist.is_empty() { return Ok(ServerCertVerified::assertion()); } let dns_name_or_ip_address = match server_name { ServerName::DnsName(dns_name) => dns_name.as_ref().to_owned(), - ServerName::IpAddress(ip_address) => ip_address.to_string(), + ServerName::IpAddress(ip_address) => { + Into::<IpAddr>::into(*ip_address).to_string() + } _ => { // NOTE(bartlomieju): `ServerName` is a non-exhaustive enum // so we have this catch all errors here. return Err(Error::General("Unknown `ServerName` variant".to_string())); } }; - if self.0.contains(&dns_name_or_ip_address) { + if self.ic_allowlist.contains(&dns_name_or_ip_address) { Ok(ServerCertVerified::assertion()) } else { - let root_store = create_default_root_cert_store(); - let verifier = WebPkiVerifier::new(root_store, None); - verifier.verify_server_cert( + self.default_verifier.verify_server_cert( end_entity, intermediates, server_name, - scts, ocsp_response, now, ) @@ -107,28 +108,32 @@ impl ServerCertVerifier for NoCertificateVerification { fn verify_tls12_signature( &self, message: &[u8], - cert: &rustls::Certificate, + cert: &rustls::pki_types::CertificateDer, dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, Error> { - if self.0.is_empty() { + if self.ic_allowlist.is_empty() { return Ok(HandshakeSignatureValid::assertion()); } filter_invalid_encoding_err( - DefaultSignatureVerification.verify_tls12_signature(message, cert, dss), + self + .default_verifier + .verify_tls12_signature(message, cert, dss), ) } fn verify_tls13_signature( &self, message: &[u8], - cert: &rustls::Certificate, + cert: &rustls::pki_types::CertificateDer, dss: &DigitallySignedStruct, ) -> Result<HandshakeSignatureValid, Error> { - if self.0.is_empty() { + if self.ic_allowlist.is_empty() { return Ok(HandshakeSignatureValid::assertion()); } filter_invalid_encoding_err( - DefaultSignatureVerification.verify_tls13_signature(message, cert, dss), + self + .default_verifier + .verify_tls13_signature(message, cert, dss), ) } } @@ -149,17 +154,10 @@ pub struct BasicAuth { } pub fn create_default_root_cert_store() -> RootCertStore { - let mut root_cert_store = RootCertStore::empty(); - // TODO(@justinmchase): Consider also loading the system keychain here - root_cert_store.add_trust_anchors(webpki_roots::TLS_SERVER_ROOTS.iter().map( - |ta| { - rustls::OwnedTrustAnchor::from_subject_spki_name_constraints( - ta.subject, - ta.spki, - ta.name_constraints, - ) - }, - )); + let root_cert_store = rustls::RootCertStore { + roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(), + }; + debug_assert!(!root_cert_store.is_empty()); root_cert_store } @@ -183,10 +181,10 @@ pub fn create_client_config( ) -> Result<ClientConfig, AnyError> { if let Some(ic_allowlist) = unsafely_ignore_certificate_errors { let client_config = ClientConfig::builder() - .with_safe_defaults() - .with_custom_certificate_verifier(Arc::new(NoCertificateVerification( - ic_allowlist, - ))); + .dangerous() + .with_custom_certificate_verifier(Arc::new( + NoCertificateVerification::new(ic_allowlist), + )); // NOTE(bartlomieju): this if/else is duplicated at the end of the body of this function. // However it's not really feasible to deduplicate it as the `client_config` instances @@ -194,7 +192,7 @@ pub fn create_client_config( // or client cert". let mut client = match maybe_cert_chain_and_key { TlsKeys::Static(TlsKey(cert_chain, private_key)) => client_config - .with_client_auth_cert(cert_chain, private_key) + .with_client_auth_cert(cert_chain, private_key.clone_key()) .expect("invalid client key or certificate"), TlsKeys::Null => client_config.with_no_client_auth(), TlsKeys::Resolver(_) => unimplemented!(), @@ -204,33 +202,33 @@ pub fn create_client_config( return Ok(client); } - let client_config = ClientConfig::builder() - .with_safe_defaults() - .with_root_certificates({ - let mut root_cert_store = - root_cert_store.unwrap_or_else(create_default_root_cert_store); - // If custom certs are specified, add them to the store - for cert in ca_certs { - let reader = &mut BufReader::new(Cursor::new(cert)); - // This function does not return specific errors, if it fails give a generic message. - match rustls_pemfile::certs(reader) { - Ok(certs) => { - root_cert_store.add_parsable_certificates(&certs); - } - Err(e) => { - return Err(anyhow!( - "Unable to add pem file to certificate store: {}", - e - )); - } + let mut root_cert_store = + root_cert_store.unwrap_or_else(create_default_root_cert_store); + // If custom certs are specified, add them to the store + for cert in ca_certs { + let reader = &mut BufReader::new(Cursor::new(cert)); + // This function does not return specific errors, if it fails give a generic message. + for r in rustls_pemfile::certs(reader) { + match r { + Ok(cert) => { + root_cert_store.add(cert)?; + } + Err(e) => { + return Err(anyhow!( + "Unable to add pem file to certificate store: {}", + e + )); } } - root_cert_store - }); + } + } + + let client_config = + ClientConfig::builder().with_root_certificates(root_cert_store); let mut client = match maybe_cert_chain_and_key { TlsKeys::Static(TlsKey(cert_chain, private_key)) => client_config - .with_client_auth_cert(cert_chain, private_key) + .with_client_auth_cert(cert_chain, private_key.clone_key()) .expect("invalid client key or certificate"), TlsKeys::Null => client_config.with_no_client_auth(), TlsKeys::Resolver(_) => unimplemented!(), @@ -257,15 +255,17 @@ fn add_alpn(client: &mut ClientConfig, socket_use: SocketUse) { pub fn load_certs( reader: &mut dyn BufRead, -) -> Result<Vec<Certificate>, AnyError> { - let certs = certs(reader) +) -> Result<Vec<CertificateDer<'static>>, AnyError> { + let certs: Result<Vec<_>, _> = certs(reader).collect(); + + let certs = certs .map_err(|_| custom_error("InvalidData", "Unable to decode certificate"))?; if certs.is_empty() { return Err(cert_not_found_err()); } - Ok(certs.into_iter().map(rustls::Certificate).collect()) + Ok(certs) } fn key_decode_err() -> AnyError { @@ -281,21 +281,32 @@ fn cert_not_found_err() -> AnyError { } /// Starts with -----BEGIN RSA PRIVATE KEY----- -fn load_rsa_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { - let keys = rsa_private_keys(&mut bytes).map_err(|_| key_decode_err())?; - Ok(keys.into_iter().map(rustls::PrivateKey).collect()) +fn load_rsa_keys( + mut bytes: &[u8], +) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { + let keys: Result<Vec<_>, _> = rsa_private_keys(&mut bytes).collect(); + let keys = keys.map_err(|_| key_decode_err())?; + Ok(keys.into_iter().map(PrivateKeyDer::Pkcs1).collect()) } /// Starts with -----BEGIN EC PRIVATE KEY----- -fn load_ec_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { - let keys = ec_private_keys(&mut bytes).map_err(|_| key_decode_err())?; - Ok(keys.into_iter().map(rustls::PrivateKey).collect()) +fn load_ec_keys( + mut bytes: &[u8], +) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { + let keys: Result<Vec<_>, std::io::Error> = + ec_private_keys(&mut bytes).collect(); + let keys2 = keys.map_err(|_| key_decode_err())?; + Ok(keys2.into_iter().map(PrivateKeyDer::Sec1).collect()) } /// Starts with -----BEGIN PRIVATE KEY----- -fn load_pkcs8_keys(mut bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { - let keys = pkcs8_private_keys(&mut bytes).map_err(|_| key_decode_err())?; - Ok(keys.into_iter().map(rustls::PrivateKey).collect()) +fn load_pkcs8_keys( + mut bytes: &[u8], +) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { + let keys: Result<Vec<_>, std::io::Error> = + pkcs8_private_keys(&mut bytes).collect(); + let keys2 = keys.map_err(|_| key_decode_err())?; + Ok(keys2.into_iter().map(PrivateKeyDer::Pkcs8).collect()) } fn filter_invalid_encoding_err( @@ -309,7 +320,9 @@ fn filter_invalid_encoding_err( } } -pub fn load_private_keys(bytes: &[u8]) -> Result<Vec<PrivateKey>, AnyError> { +pub fn load_private_keys( + bytes: &[u8], +) -> Result<Vec<PrivateKeyDer<'static>>, AnyError> { let mut keys = load_rsa_keys(bytes)?; if keys.is_empty() { diff --git a/ext/tls/testdata/README b/ext/tls/testdata/README new file mode 100644 index 000000000..12046561c --- /dev/null +++ b/ext/tls/testdata/README @@ -0,0 +1,4 @@ + +openssl req -x509 -newkey rsa:2048 -nodes -keyout example2_prikey.pem -out example2_cert.der -subj "/C=US/ST=State/L=Locality/O=Organization/CN=example2.com" -outform der + +openssl pkey -in example2_prikey.pem -out example2_prikey.der -outform der diff --git a/ext/tls/testdata/example1_cert.der b/ext/tls/testdata/example1_cert.der Binary files differnew file mode 100644 index 000000000..fb1b2e64b --- /dev/null +++ b/ext/tls/testdata/example1_cert.der diff --git a/ext/tls/testdata/example1_prikey.der b/ext/tls/testdata/example1_prikey.der Binary files differnew file mode 100644 index 000000000..1cf7ef1d2 --- /dev/null +++ b/ext/tls/testdata/example1_prikey.der diff --git a/ext/tls/testdata/example2_cert.der b/ext/tls/testdata/example2_cert.der Binary files differnew file mode 100644 index 000000000..4428f3f6b --- /dev/null +++ b/ext/tls/testdata/example2_cert.der diff --git a/ext/tls/testdata/example2_prikey.der b/ext/tls/testdata/example2_prikey.der Binary files differnew file mode 100644 index 000000000..8bdef8df3 --- /dev/null +++ b/ext/tls/testdata/example2_prikey.der diff --git a/ext/tls/tls_key.rs b/ext/tls/tls_key.rs index 18064a91a..1e60e7cf0 100644 --- a/ext/tls/tls_key.rs +++ b/ext/tls/tls_key.rs @@ -11,8 +11,6 @@ //! key lookup can handle closing one end of the pair, in which case they will just //! attempt to clean up the associated resources. -use crate::Certificate; -use crate::PrivateKey; use deno_core::anyhow::anyhow; use deno_core::error::AnyError; use deno_core::futures::future::poll_fn; @@ -32,12 +30,21 @@ use std::sync::Arc; use tokio::sync::broadcast; use tokio::sync::mpsc; use tokio::sync::oneshot; +use webpki::types::CertificateDer; +use webpki::types::PrivateKeyDer; type ErrorType = Rc<AnyError>; /// A TLS certificate/private key pair. -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct TlsKey(pub Vec<Certificate>, pub PrivateKey); +/// see https://docs.rs/rustls-pki-types/latest/rustls_pki_types/#cloning-private-keys +#[derive(Debug, PartialEq, Eq)] +pub struct TlsKey(pub Vec<CertificateDer<'static>>, pub PrivateKeyDer<'static>); + +impl Clone for TlsKey { + fn clone(&self) -> Self { + Self(self.0.clone(), self.1.clone_key()) + } +} #[derive(Clone, Debug, Default)] pub enum TlsKeys { @@ -109,9 +116,8 @@ impl TlsKeyResolver { let key = self.resolve(sni).await?; let mut tls_config = ServerConfig::builder() - .with_safe_defaults() .with_no_client_auth() - .with_single_cert(key.0, key.1)?; + .with_single_cert(key.0, key.1.clone_key())?; tls_config.alpn_protocols = alpn; Ok(tls_config.into()) } @@ -251,14 +257,18 @@ impl TlsKeyLookup { pub mod tests { use super::*; use deno_core::unsync::spawn; - use rustls::Certificate; - use rustls::PrivateKey; fn tls_key_for_test(sni: &str) -> TlsKey { - TlsKey( - vec![Certificate(format!("{sni}-cert").into_bytes())], - PrivateKey(format!("{sni}-key").into_bytes()), - ) + let manifest_dir = + std::path::PathBuf::from(std::env::var("CARGO_MANIFEST_DIR").unwrap()); + let sni = sni.replace(".com", ""); + let cert_file = manifest_dir.join(format!("testdata/{}_cert.der", sni)); + let prikey_file = manifest_dir.join(format!("testdata/{}_prikey.der", sni)); + let cert = std::fs::read(cert_file).unwrap(); + let prikey = std::fs::read(prikey_file).unwrap(); + let cert = CertificateDer::from(cert); + let prikey = PrivateKeyDer::try_from(prikey).unwrap(); + TlsKey(vec![cert], prikey) } #[tokio::test] @@ -270,8 +280,8 @@ pub mod tests { } }); - let key = resolver.resolve("example.com".to_owned()).await.unwrap(); - assert_eq!(tls_key_for_test("example.com"), key); + let key = resolver.resolve("example1.com".to_owned()).await.unwrap(); + assert_eq!(tls_key_for_test("example1.com"), key); drop(resolver); task.await.unwrap(); @@ -286,13 +296,13 @@ pub mod tests { } }); - let f1 = resolver.resolve("example.com".to_owned()); - let f2 = resolver.resolve("example.com".to_owned()); + let f1 = resolver.resolve("example1.com".to_owned()); + let f2 = resolver.resolve("example1.com".to_owned()); let key = f1.await.unwrap(); - assert_eq!(tls_key_for_test("example.com"), key); + assert_eq!(tls_key_for_test("example1.com"), key); let key = f2.await.unwrap(); - assert_eq!(tls_key_for_test("example.com"), key); + assert_eq!(tls_key_for_test("example1.com"), key); drop(resolver); task.await.unwrap(); diff --git a/ext/websocket/lib.rs b/ext/websocket/lib.rs index 87503120b..a8bb3415d 100644 --- a/ext/websocket/lib.rs +++ b/ext/websocket/lib.rs @@ -36,14 +36,13 @@ use http::Request; use http::StatusCode; use http::Uri; use once_cell::sync::Lazy; +use rustls_tokio_stream::rustls::pki_types::ServerName; use rustls_tokio_stream::rustls::RootCertStore; -use rustls_tokio_stream::rustls::ServerName; use rustls_tokio_stream::TlsStream; use serde::Serialize; use std::borrow::Cow; use std::cell::Cell; use std::cell::RefCell; -use std::convert::TryFrom; use std::fmt; use std::future::Future; use std::num::NonZeroUsize; @@ -245,8 +244,8 @@ async fn handshake_http1_wss( ) -> Result<(WebSocket<WebSocketStream>, http::HeaderMap), AnyError> { let tcp_socket = TcpStream::connect(addr).await?; let tls_config = create_ws_client_config(state, SocketUse::Http1Only)?; - let dnsname = - ServerName::try_from(domain).map_err(|_| invalid_hostname(domain))?; + let dnsname = ServerName::try_from(domain.to_string()) + .map_err(|_| invalid_hostname(domain))?; let mut tls_connector = TlsStream::new_client_side( tcp_socket, ClientConnection::new(tls_config.into(), dnsname)?, @@ -270,8 +269,8 @@ async fn handshake_http2_wss( ) -> Result<(WebSocket<WebSocketStream>, http::HeaderMap), AnyError> { let tcp_socket = TcpStream::connect(addr).await?; let tls_config = create_ws_client_config(state, SocketUse::Http2Only)?; - let dnsname = - ServerName::try_from(domain).map_err(|_| invalid_hostname(domain))?; + let dnsname = ServerName::try_from(domain.to_string()) + .map_err(|_| invalid_hostname(domain))?; // We need to better expose the underlying errors here let mut tls_connector = TlsStream::new_client_side( tcp_socket, |