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-util-wasm",
|
||||
"js-sys",
|
||||
"lazy_static",
|
||||
"parking_lot_core",
|
||||
"pin-project-lite",
|
||||
"ring",
|
||||
"rustls-pemfile",
|
||||
"rustls-pki-types",
|
||||
"rustls-webpki",
|
||||
"send_wrapper 0.6.0",
|
||||
"thiserror",
|
||||
"tokio",
|
||||
|
@ -1459,6 +1460,16 @@ dependencies = [
|
|||
"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]]
|
||||
name = "rustls-pki-types"
|
||||
version = "1.8.0"
|
||||
|
|
|
@ -21,8 +21,9 @@ http-body-util = "0.1.2"
|
|||
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"] }
|
||||
js-sys = "0.3.70"
|
||||
lazy_static = "1.5.0"
|
||||
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"] }
|
||||
thiserror = "1.0.63"
|
||||
tokio = "1.39.3"
|
||||
|
@ -51,5 +52,5 @@ features = ["nightly"]
|
|||
|
||||
[features]
|
||||
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")]
|
||||
#[error("Fastwebsockets: {0:?} ({0})")]
|
||||
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}")]
|
||||
WispTransport(String),
|
||||
|
@ -188,6 +194,10 @@ pub struct EpoxyClientOptions {
|
|||
pub redirect_limit: usize,
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub user_agent: String,
|
||||
pub disable_certificate_validation: bool,
|
||||
#[cfg(feature = "full")]
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub pem_files: Vec<String>,
|
||||
}
|
||||
|
||||
#[wasm_bindgen]
|
||||
|
@ -206,6 +216,9 @@ impl Default for EpoxyClientOptions {
|
|||
websocket_protocols: Vec::new(),
|
||||
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(),
|
||||
disable_certificate_validation: false,
|
||||
#[cfg(feature = "full")]
|
||||
pem_files: Vec::new(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -242,6 +255,8 @@ pub struct EpoxyClient {
|
|||
stream_provider: Arc<StreamProvider>,
|
||||
client: Client<StreamProviderService, HttpBody>,
|
||||
|
||||
certs_tampered: bool,
|
||||
|
||||
pub redirect_limit: usize,
|
||||
#[wasm_bindgen(getter_with_clone)]
|
||||
pub user_agent: String,
|
||||
|
@ -335,6 +350,7 @@ impl EpoxyClient {
|
|||
client,
|
||||
redirect_limit: options.redirect_limit,
|
||||
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)
|
||||
.await?;
|
||||
|
||||
if self.certs_tampered {
|
||||
response.headers_mut().insert(
|
||||
HeaderName::from_static("X-Epoxy-CertsTampered"),
|
||||
HeaderValue::from_static("true"),
|
||||
);
|
||||
}
|
||||
|
||||
let response_headers: Array = response
|
||||
.headers()
|
||||
.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::{
|
||||
rustls::{ClientConfig, RootCertStore},
|
||||
rustls::{crypto::ring::default_provider, ClientConfig, RootCertStore},
|
||||
TlsConnector,
|
||||
};
|
||||
use futures_util::{
|
||||
|
@ -10,7 +16,6 @@ use futures_util::{
|
|||
AsyncRead, AsyncWrite, Future,
|
||||
};
|
||||
use hyper_util_wasm::client::legacy::connect::{ConnectSvc, Connected, Connection};
|
||||
use lazy_static::lazy_static;
|
||||
use pin_project_lite::pin_project;
|
||||
use wasm_bindgen_futures::spawn_local;
|
||||
use webpki_roots::TLS_SERVER_ROOTS;
|
||||
|
@ -20,18 +25,11 @@ use wisp_mux::{
|
|||
ClientMux, MuxStreamAsyncRW, MuxStreamIo, StreamType,
|
||||
};
|
||||
|
||||
use crate::{console_log, utils::IgnoreCloseNotify, EpoxyClientOptions, EpoxyError};
|
||||
|
||||
lazy_static! {
|
||||
static ref CLIENT_CONFIG: Arc<ClientConfig> = {
|
||||
let certstore = RootCertStore::from_iter(TLS_SERVER_ROOTS.iter().cloned());
|
||||
Arc::new(
|
||||
ClientConfig::builder()
|
||||
.with_root_certificates(certstore)
|
||||
.with_no_client_auth(),
|
||||
)
|
||||
use crate::{
|
||||
console_log,
|
||||
utils::{IgnoreCloseNotify, NoCertificateVerification},
|
||||
EpoxyClientOptions, EpoxyError,
|
||||
};
|
||||
}
|
||||
|
||||
pub type ProviderUnencryptedStream = MuxStreamIo;
|
||||
pub type ProviderUnencryptedAsyncRW = MuxStreamAsyncRW;
|
||||
|
@ -62,6 +60,8 @@ pub struct StreamProvider {
|
|||
udp_extension: bool,
|
||||
|
||||
current_client: Arc<Mutex<Option<ClientMux>>>,
|
||||
|
||||
client_config: Arc<ClientConfig>,
|
||||
}
|
||||
|
||||
impl StreamProvider {
|
||||
|
@ -69,11 +69,44 @@ impl StreamProvider {
|
|||
wisp_generator: ProviderWispTransportGenerator,
|
||||
options: &EpoxyClientOptions,
|
||||
) -> 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 {
|
||||
wisp_generator,
|
||||
current_client: Arc::new(Mutex::new(None)),
|
||||
wisp_v2: options.wisp_v2,
|
||||
udp_extension: options.udp_extension_required,
|
||||
client_config,
|
||||
})
|
||||
}
|
||||
|
||||
|
@ -149,7 +182,7 @@ impl StreamProvider {
|
|||
let stream = self
|
||||
.get_asyncread(StreamType::Tcp, host.clone(), port)
|
||||
.await?;
|
||||
let connector = TlsConnector::from(CLIENT_CONFIG.clone());
|
||||
let connector = TlsConnector::from(self.client_config.clone());
|
||||
let ret = connector
|
||||
.connect(host.try_into()?, stream)
|
||||
.into_fallible()
|
||||
|
|
|
@ -6,12 +6,21 @@ use std::{
|
|||
|
||||
use async_trait::async_trait;
|
||||
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 http::{HeaderValue, Uri};
|
||||
use hyper::{body::Body, rt::Executor};
|
||||
use js_sys::{Array, ArrayBuffer, JsString, Object, Uint8Array};
|
||||
use pin_project_lite::pin_project;
|
||||
use rustls_pki_types::{CertificateDer, ServerName, UnixTime};
|
||||
use send_wrapper::SendWrapper;
|
||||
use wasm_bindgen::{prelude::*, JsCast, JsValue};
|
||||
use wasm_bindgen_futures::JsFuture;
|
||||
|
@ -285,9 +294,7 @@ impl AsyncWrite for IgnoreCloseNotify {
|
|||
cx: &mut Context<'_>,
|
||||
buf: &[u8],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
self.project()
|
||||
.inner
|
||||
.poll_write(cx, buf)
|
||||
self.project().inner.poll_write(cx, buf)
|
||||
}
|
||||
|
||||
fn poll_write_vectored(
|
||||
|
@ -295,9 +302,7 @@ impl AsyncWrite for IgnoreCloseNotify {
|
|||
cx: &mut Context<'_>,
|
||||
bufs: &[std::io::IoSlice<'_>],
|
||||
) -> Poll<std::io::Result<usize>> {
|
||||
self.project()
|
||||
.inner
|
||||
.poll_write_vectored(cx, bufs)
|
||||
self.project().inner.poll_write_vectored(cx, bufs)
|
||||
}
|
||||
|
||||
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 {
|
||||
[301, 302, 303, 307, 308].contains(&code)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue