mirror of
https://github.com/MercuryWorkshop/epoxy-tls.git
synced 2025-05-13 06:20:02 -04:00
allow custom certs/disabling cert verification
This commit is contained in:
parent
40a3ed616a
commit
55e1ef92bf
5 changed files with 154 additions and 27 deletions
13
Cargo.lock
generated
13
Cargo.lock
generated
|
@ -530,11 +530,12 @@ dependencies = [
|
||||||
"hyper",
|
"hyper",
|
||||||
"hyper-util-wasm",
|
"hyper-util-wasm",
|
||||||
"js-sys",
|
"js-sys",
|
||||||
"lazy_static",
|
|
||||||
"parking_lot_core",
|
"parking_lot_core",
|
||||||
"pin-project-lite",
|
"pin-project-lite",
|
||||||
"ring",
|
"ring",
|
||||||
|
"rustls-pemfile",
|
||||||
"rustls-pki-types",
|
"rustls-pki-types",
|
||||||
|
"rustls-webpki",
|
||||||
"send_wrapper 0.6.0",
|
"send_wrapper 0.6.0",
|
||||||
"thiserror",
|
"thiserror",
|
||||||
"tokio",
|
"tokio",
|
||||||
|
@ -1459,6 +1460,16 @@ dependencies = [
|
||||||
"zeroize",
|
"zeroize",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustls-pemfile"
|
||||||
|
version = "2.1.3"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "196fe16b00e106300d3e45ecfcb764fa292a535d7326a29a5875c579c7417425"
|
||||||
|
dependencies = [
|
||||||
|
"base64 0.22.1",
|
||||||
|
"rustls-pki-types",
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "rustls-pki-types"
|
name = "rustls-pki-types"
|
||||||
version = "1.8.0"
|
version = "1.8.0"
|
||||||
|
|
|
@ -21,8 +21,9 @@ http-body-util = "0.1.2"
|
||||||
hyper = "1.4.1"
|
hyper = "1.4.1"
|
||||||
hyper-util-wasm = { git = "https://github.com/r58Playz/hyper-util-wasm", branch = "opinionated", version = "0.1.7", features = ["client-legacy", "http1"] }
|
hyper-util-wasm = { git = "https://github.com/r58Playz/hyper-util-wasm", branch = "opinionated", version = "0.1.7", features = ["client-legacy", "http1"] }
|
||||||
js-sys = "0.3.70"
|
js-sys = "0.3.70"
|
||||||
lazy_static = "1.5.0"
|
|
||||||
pin-project-lite = "0.2.14"
|
pin-project-lite = "0.2.14"
|
||||||
|
rustls-pemfile = { version = "2.1.3", optional = true }
|
||||||
|
rustls-webpki = { version = "0.102.7", optional = true }
|
||||||
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
send_wrapper = { version = "0.6.0", features = ["futures"] }
|
||||||
thiserror = "1.0.63"
|
thiserror = "1.0.63"
|
||||||
tokio = "1.39.3"
|
tokio = "1.39.3"
|
||||||
|
@ -51,5 +52,5 @@ features = ["nightly"]
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["full"]
|
default = ["full"]
|
||||||
full = ["fastwebsockets", "async-compression", "hyper-util-wasm/http2"]
|
full = ["dep:fastwebsockets", "dep:async-compression", "dep:rustls-webpki", "dep:rustls-pemfile", "hyper-util-wasm/http2"]
|
||||||
|
|
||||||
|
|
|
@ -73,6 +73,12 @@ pub enum EpoxyError {
|
||||||
#[cfg(feature = "full")]
|
#[cfg(feature = "full")]
|
||||||
#[error("Fastwebsockets: {0:?} ({0})")]
|
#[error("Fastwebsockets: {0:?} ({0})")]
|
||||||
FastWebSockets(#[from] fastwebsockets::WebSocketError),
|
FastWebSockets(#[from] fastwebsockets::WebSocketError),
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
#[error("Pemfile: {0:?} ({0})")]
|
||||||
|
Pemfile(std::io::Error),
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
#[error("Webpki: {0:?} ({0})")]
|
||||||
|
Webpki(#[from] webpki::Error),
|
||||||
|
|
||||||
#[error("Custom wisp transport: {0}")]
|
#[error("Custom wisp transport: {0}")]
|
||||||
WispTransport(String),
|
WispTransport(String),
|
||||||
|
@ -188,6 +194,10 @@ pub struct EpoxyClientOptions {
|
||||||
pub redirect_limit: usize,
|
pub redirect_limit: usize,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
|
pub disable_certificate_validation: bool,
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
|
pub pem_files: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[wasm_bindgen]
|
#[wasm_bindgen]
|
||||||
|
@ -206,6 +216,9 @@ impl Default for EpoxyClientOptions {
|
||||||
websocket_protocols: Vec::new(),
|
websocket_protocols: Vec::new(),
|
||||||
redirect_limit: 10,
|
redirect_limit: 10,
|
||||||
user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36".to_string(),
|
user_agent: "Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36".to_string(),
|
||||||
|
disable_certificate_validation: false,
|
||||||
|
#[cfg(feature = "full")]
|
||||||
|
pem_files: Vec::new(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -242,6 +255,8 @@ pub struct EpoxyClient {
|
||||||
stream_provider: Arc<StreamProvider>,
|
stream_provider: Arc<StreamProvider>,
|
||||||
client: Client<StreamProviderService, HttpBody>,
|
client: Client<StreamProviderService, HttpBody>,
|
||||||
|
|
||||||
|
certs_tampered: bool,
|
||||||
|
|
||||||
pub redirect_limit: usize,
|
pub redirect_limit: usize,
|
||||||
#[wasm_bindgen(getter_with_clone)]
|
#[wasm_bindgen(getter_with_clone)]
|
||||||
pub user_agent: String,
|
pub user_agent: String,
|
||||||
|
@ -335,6 +350,7 @@ impl EpoxyClient {
|
||||||
client,
|
client,
|
||||||
redirect_limit: options.redirect_limit,
|
redirect_limit: options.redirect_limit,
|
||||||
user_agent: options.user_agent,
|
user_agent: options.user_agent,
|
||||||
|
certs_tampered: options.disable_certificate_validation || !options.pem_files.is_empty(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -566,10 +582,17 @@ impl EpoxyClient {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let (response, response_uri, redirected) = self
|
let (mut response, response_uri, redirected) = self
|
||||||
.send_req(request_builder.body(HttpBody::new(body))?, request_redirect)
|
.send_req(request_builder.body(HttpBody::new(body))?, request_redirect)
|
||||||
.await?;
|
.await?;
|
||||||
|
|
||||||
|
if self.certs_tampered {
|
||||||
|
response.headers_mut().insert(
|
||||||
|
HeaderName::from_static("X-Epoxy-CertsTampered"),
|
||||||
|
HeaderValue::from_static("true"),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
let response_headers: Array = response
|
let response_headers: Array = response
|
||||||
.headers()
|
.headers()
|
||||||
.iter()
|
.iter()
|
||||||
|
|
|
@ -1,7 +1,13 @@
|
||||||
use std::{io::ErrorKind, pin::Pin, sync::Arc, task::Poll};
|
use std::{
|
||||||
|
io::{BufReader, ErrorKind},
|
||||||
|
pin::Pin,
|
||||||
|
sync::Arc,
|
||||||
|
task::Poll,
|
||||||
|
};
|
||||||
|
|
||||||
|
use cfg_if::cfg_if;
|
||||||
use futures_rustls::{
|
use futures_rustls::{
|
||||||
rustls::{ClientConfig, RootCertStore},
|
rustls::{crypto::ring::default_provider, ClientConfig, RootCertStore},
|
||||||
TlsConnector,
|
TlsConnector,
|
||||||
};
|
};
|
||||||
use futures_util::{
|
use futures_util::{
|
||||||
|
@ -10,7 +16,6 @@ use futures_util::{
|
||||||
AsyncRead, AsyncWrite, Future,
|
AsyncRead, AsyncWrite, Future,
|
||||||
};
|
};
|
||||||
use hyper_util_wasm::client::legacy::connect::{ConnectSvc, Connected, Connection};
|
use hyper_util_wasm::client::legacy::connect::{ConnectSvc, Connected, Connection};
|
||||||
use lazy_static::lazy_static;
|
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
use wasm_bindgen_futures::spawn_local;
|
use wasm_bindgen_futures::spawn_local;
|
||||||
use webpki_roots::TLS_SERVER_ROOTS;
|
use webpki_roots::TLS_SERVER_ROOTS;
|
||||||
|
@ -20,18 +25,11 @@ use wisp_mux::{
|
||||||
ClientMux, MuxStreamAsyncRW, MuxStreamIo, StreamType,
|
ClientMux, MuxStreamAsyncRW, MuxStreamIo, StreamType,
|
||||||
};
|
};
|
||||||
|
|
||||||
use crate::{console_log, utils::IgnoreCloseNotify, EpoxyClientOptions, EpoxyError};
|
use crate::{
|
||||||
|
console_log,
|
||||||
lazy_static! {
|
utils::{IgnoreCloseNotify, NoCertificateVerification},
|
||||||
static ref CLIENT_CONFIG: Arc<ClientConfig> = {
|
EpoxyClientOptions, EpoxyError,
|
||||||
let certstore = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned());
|
};
|
||||||
Arc::new(
|
|
||||||
ClientConfig::builder()
|
|
||||||
.with_root_certificates(certstore)
|
|
||||||
.with_no_client_auth(),
|
|
||||||
)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
pub type ProviderUnencryptedStream = MuxStreamIo;
|
pub type ProviderUnencryptedStream = MuxStreamIo;
|
||||||
pub type ProviderUnencryptedAsyncRW = MuxStreamAsyncRW;
|
pub type ProviderUnencryptedAsyncRW = MuxStreamAsyncRW;
|
||||||
|
@ -62,6 +60,8 @@ pub struct StreamProvider {
|
||||||
udp_extension: bool,
|
udp_extension: bool,
|
||||||
|
|
||||||
current_client: Arc<Mutex<Option<ClientMux>>>,
|
current_client: Arc<Mutex<Option<ClientMux>>>,
|
||||||
|
|
||||||
|
client_config: Arc<ClientConfig>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl StreamProvider {
|
impl StreamProvider {
|
||||||
|
@ -69,11 +69,44 @@ impl StreamProvider {
|
||||||
wisp_generator: ProviderWispTransportGenerator,
|
wisp_generator: ProviderWispTransportGenerator,
|
||||||
options: &EpoxyClientOptions,
|
options: &EpoxyClientOptions,
|
||||||
) -> Result<Self, EpoxyError> {
|
) -> Result<Self, EpoxyError> {
|
||||||
|
cfg_if! {
|
||||||
|
if #[cfg(feature = "full")] {
|
||||||
|
let pems: Result<Result<Vec<_>, webpki::Error>, std::io::Error> = options
|
||||||
|
.pem_files
|
||||||
|
.iter()
|
||||||
|
.flat_map(|x| {
|
||||||
|
rustls_pemfile::certs(&mut BufReader::new(x.as_bytes()))
|
||||||
|
.map(|x| x.map(|x| webpki::anchor_from_trusted_cert(&x).map(|x| x.to_owned())))
|
||||||
|
.collect::<Vec<_>>()
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
let pems = pems.map_err(EpoxyError::Pemfile)??;
|
||||||
|
let certstore = RootCertStore::from_iter(pems.into_iter().chain(TLS_SERVER_ROOTS.iter().cloned()));
|
||||||
|
} else {
|
||||||
|
let certstore = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let client_config = if options.disable_certificate_validation {
|
||||||
|
ClientConfig::builder()
|
||||||
|
.dangerous()
|
||||||
|
.with_custom_certificate_verifier(Arc::new(NoCertificateVerification(
|
||||||
|
default_provider(),
|
||||||
|
)))
|
||||||
|
.with_no_client_auth()
|
||||||
|
} else {
|
||||||
|
ClientConfig::builder()
|
||||||
|
.with_root_certificates(certstore)
|
||||||
|
.with_no_client_auth()
|
||||||
|
};
|
||||||
|
let client_config = Arc::new(client_config);
|
||||||
|
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
wisp_generator,
|
wisp_generator,
|
||||||
current_client: Arc::new(Mutex::new(None)),
|
current_client: Arc::new(Mutex::new(None)),
|
||||||
wisp_v2: options.wisp_v2,
|
wisp_v2: options.wisp_v2,
|
||||||
udp_extension: options.udp_extension_required,
|
udp_extension: options.udp_extension_required,
|
||||||
|
client_config,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -149,7 +182,7 @@ impl StreamProvider {
|
||||||
let stream = self
|
let stream = self
|
||||||
.get_asyncread(StreamType::Tcp, host.clone(), port)
|
.get_asyncread(StreamType::Tcp, host.clone(), port)
|
||||||
.await?;
|
.await?;
|
||||||
let connector = TlsConnector::from(CLIENT_CONFIG.clone());
|
let connector = TlsConnector::from(self.client_config.clone());
|
||||||
let ret = connector
|
let ret = connector
|
||||||
.connect(host.try_into()?, stream)
|
.connect(host.try_into()?, stream)
|
||||||
.into_fallible()
|
.into_fallible()
|
||||||
|
|
|
@ -6,12 +6,21 @@ use std::{
|
||||||
|
|
||||||
use async_trait::async_trait;
|
use async_trait::async_trait;
|
||||||
use bytes::{buf::UninitSlice, BufMut, Bytes, BytesMut};
|
use bytes::{buf::UninitSlice, BufMut, Bytes, BytesMut};
|
||||||
use futures_rustls::TlsStream;
|
use futures_rustls::{
|
||||||
|
rustls::{
|
||||||
|
self,
|
||||||
|
client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier},
|
||||||
|
crypto::{verify_tls12_signature, verify_tls13_signature, CryptoProvider},
|
||||||
|
DigitallySignedStruct,
|
||||||
|
},
|
||||||
|
TlsStream,
|
||||||
|
};
|
||||||
use futures_util::{ready, AsyncRead, AsyncWrite, Future, Stream, StreamExt, TryStreamExt};
|
use futures_util::{ready, AsyncRead, AsyncWrite, Future, Stream, StreamExt, TryStreamExt};
|
||||||
use http::{HeaderValue, Uri};
|
use http::{HeaderValue, Uri};
|
||||||
use hyper::{body::Body, rt::Executor};
|
use hyper::{body::Body, rt::Executor};
|
||||||
use js_sys::{Array, ArrayBuffer, JsString, Object, Uint8Array};
|
use js_sys::{Array, ArrayBuffer, JsString, Object, Uint8Array};
|
||||||
use pin_project_lite::pin_project;
|
use pin_project_lite::pin_project;
|
||||||
|
use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
|
||||||
use send_wrapper::SendWrapper;
|
use send_wrapper::SendWrapper;
|
||||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||||
use wasm_bindgen_futures::JsFuture;
|
use wasm_bindgen_futures::JsFuture;
|
||||||
|
@ -285,9 +294,7 @@ impl AsyncWrite for IgnoreCloseNotify {
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
buf: &[u8],
|
buf: &[u8],
|
||||||
) -> Poll<std::io::Result<usize>> {
|
) -> Poll<std::io::Result<usize>> {
|
||||||
self.project()
|
self.project().inner.poll_write(cx, buf)
|
||||||
.inner
|
|
||||||
.poll_write(cx, buf)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_write_vectored(
|
fn poll_write_vectored(
|
||||||
|
@ -295,9 +302,7 @@ impl AsyncWrite for IgnoreCloseNotify {
|
||||||
cx: &mut Context<'_>,
|
cx: &mut Context<'_>,
|
||||||
bufs: &[std::io::IoSlice<'_>],
|
bufs: &[std::io::IoSlice<'_>],
|
||||||
) -> Poll<std::io::Result<usize>> {
|
) -> Poll<std::io::Result<usize>> {
|
||||||
self.project()
|
self.project().inner.poll_write_vectored(cx, bufs)
|
||||||
.inner
|
|
||||||
.poll_write_vectored(cx, bufs)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
fn poll_flush(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<std::io::Result<()>> {
|
||||||
|
@ -309,6 +314,60 @@ impl AsyncWrite for IgnoreCloseNotify {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub(crate) struct NoCertificateVerification(pub CryptoProvider);
|
||||||
|
|
||||||
|
impl NoCertificateVerification {
|
||||||
|
pub fn new(provider: CryptoProvider) -> Self {
|
||||||
|
Self(provider)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl ServerCertVerifier for NoCertificateVerification {
|
||||||
|
fn verify_server_cert(
|
||||||
|
&self,
|
||||||
|
_end_entity: &CertificateDer<'_>,
|
||||||
|
_intermediates: &[CertificateDer<'_>],
|
||||||
|
_server_name: &ServerName<'_>,
|
||||||
|
_ocsp: &[u8],
|
||||||
|
_now: UnixTime,
|
||||||
|
) -> Result<ServerCertVerified, rustls::Error> {
|
||||||
|
Ok(rustls::client::danger::ServerCertVerified::assertion())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls12_signature(
|
||||||
|
&self,
|
||||||
|
message: &[u8],
|
||||||
|
cert: &CertificateDer<'_>,
|
||||||
|
dss: &DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||||
|
verify_tls12_signature(
|
||||||
|
message,
|
||||||
|
cert,
|
||||||
|
dss,
|
||||||
|
&self.0.signature_verification_algorithms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn verify_tls13_signature(
|
||||||
|
&self,
|
||||||
|
message: &[u8],
|
||||||
|
cert: &CertificateDer<'_>,
|
||||||
|
dss: &DigitallySignedStruct,
|
||||||
|
) -> Result<HandshakeSignatureValid, rustls::Error> {
|
||||||
|
verify_tls13_signature(
|
||||||
|
message,
|
||||||
|
cert,
|
||||||
|
dss,
|
||||||
|
&self.0.signature_verification_algorithms,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
|
||||||
|
self.0.signature_verification_algorithms.supported_schemes()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub fn is_redirect(code: u16) -> bool {
|
pub fn is_redirect(code: u16) -> bool {
|
||||||
[301, 302, 303, 307, 308].contains(&code)
|
[301, 302, 303, 307, 308].contains(&code)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue