Files
telemt/src/tls_front/fetcher.rs
T

142 lines
4.3 KiB
Rust
Raw Normal View History

2026-02-20 12:51:35 +03:00
use std::sync::Arc;
use std::time::Duration;
2026-02-20 13:36:54 +03:00
use anyhow::Result;
2026-02-20 12:51:35 +03:00
use tokio::net::TcpStream;
use tokio::time::timeout;
use tokio_rustls::client::TlsStream;
use tokio_rustls::TlsConnector;
2026-02-20 13:36:54 +03:00
use tracing::debug;
2026-02-20 12:51:35 +03:00
2026-02-20 13:36:54 +03:00
use rustls::client::danger::{HandshakeSignatureValid, ServerCertVerified, ServerCertVerifier};
use rustls::client::ClientConfig;
use rustls::pki_types::{CertificateDer, ServerName, UnixTime};
2026-02-20 12:51:35 +03:00
use rustls::{DigitallySignedStruct, Error as RustlsError};
use crate::tls_front::types::{ParsedServerHello, TlsFetchResult};
/// No-op verifier: accept any certificate (we only need lengths and metadata).
2026-02-20 13:36:54 +03:00
#[derive(Debug)]
2026-02-20 12:51:35 +03:00
struct NoVerify;
impl ServerCertVerifier for NoVerify {
fn verify_server_cert(
&self,
_end_entity: &CertificateDer<'_>,
_intermediates: &[CertificateDer<'_>],
2026-02-20 13:36:54 +03:00
_server_name: &ServerName<'_>,
2026-02-20 12:51:35 +03:00
_ocsp: &[u8],
_now: UnixTime,
2026-02-20 13:36:54 +03:00
) -> Result<ServerCertVerified, RustlsError> {
Ok(ServerCertVerified::assertion())
2026-02-20 12:51:35 +03:00
}
fn verify_tls12_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
2026-02-20 13:36:54 +03:00
) -> Result<HandshakeSignatureValid, RustlsError> {
Ok(HandshakeSignatureValid::assertion())
2026-02-20 12:51:35 +03:00
}
fn verify_tls13_signature(
&self,
_message: &[u8],
_cert: &CertificateDer<'_>,
_dss: &DigitallySignedStruct,
2026-02-20 13:36:54 +03:00
) -> Result<HandshakeSignatureValid, RustlsError> {
Ok(HandshakeSignatureValid::assertion())
}
fn supported_verify_schemes(&self) -> Vec<rustls::SignatureScheme> {
use rustls::SignatureScheme::*;
vec![
RSA_PKCS1_SHA256,
RSA_PSS_SHA256,
ECDSA_NISTP256_SHA256,
ECDSA_NISTP384_SHA384,
]
2026-02-20 12:51:35 +03:00
}
}
fn build_client_config() -> Arc<ClientConfig> {
2026-02-20 13:36:54 +03:00
let root = rustls::RootCertStore::empty();
let provider = rustls::crypto::ring::default_provider();
let mut config = ClientConfig::builder_with_provider(Arc::new(provider))
.with_protocol_versions(&[&rustls::version::TLS13, &rustls::version::TLS12])
.expect("protocol versions")
.with_root_certificates(root)
.with_no_client_auth();
config
.dangerous()
.set_certificate_verifier(Arc::new(NoVerify));
Arc::new(config)
2026-02-20 12:51:35 +03:00
}
2026-02-20 13:36:54 +03:00
/// Fetch real TLS metadata for the given SNI: negotiated cipher and cert lengths.
2026-02-20 12:51:35 +03:00
pub async fn fetch_real_tls(
host: &str,
port: u16,
sni: &str,
connect_timeout: Duration,
2026-02-20 13:36:54 +03:00
) -> Result<TlsFetchResult> {
let addr = format!("{host}:{port}");
2026-02-20 12:51:35 +03:00
let stream = timeout(connect_timeout, TcpStream::connect(addr)).await??;
let config = build_client_config();
let connector = TlsConnector::from(config);
2026-02-20 13:36:54 +03:00
let server_name = ServerName::try_from(sni.to_owned())
.or_else(|_| ServerName::try_from(host.to_owned()))
2026-02-20 12:51:35 +03:00
.map_err(|_| RustlsError::General("invalid SNI".into()))?;
2026-02-20 13:36:54 +03:00
let tls_stream: TlsStream<TcpStream> = connector.connect(server_name, stream).await?;
2026-02-20 12:51:35 +03:00
// Extract negotiated parameters and certificates
2026-02-20 13:36:54 +03:00
let (_io, session) = tls_stream.get_ref();
2026-02-20 12:51:35 +03:00
let cipher_suite = session
.negotiated_cipher_suite()
2026-02-20 13:36:54 +03:00
.map(|s| u16::from(s.suite()).to_be_bytes())
2026-02-20 12:51:35 +03:00
.unwrap_or([0x13, 0x01]);
let certs: Vec<CertificateDer<'static>> = session
.peer_certificates()
2026-02-20 13:36:54 +03:00
.map(|slice| slice.to_vec())
2026-02-20 12:51:35 +03:00
.unwrap_or_default();
let total_cert_len: usize = certs.iter().map(|c| c.len()).sum::<usize>().max(1024);
// Heuristic: split across two records if large to mimic real servers a bit.
let app_data_records_sizes = if total_cert_len > 3000 {
vec![total_cert_len / 2, total_cert_len - total_cert_len / 2]
} else {
vec![total_cert_len]
};
let parsed = ParsedServerHello {
version: [0x03, 0x03],
random: [0u8; 32],
session_id: Vec::new(),
cipher_suite,
compression: 0,
extensions: Vec::new(),
};
debug!(
sni = %sni,
len = total_cert_len,
cipher = format!("0x{:04x}", u16::from_be_bytes(cipher_suite)),
"Fetched TLS metadata"
);
Ok(TlsFetchResult {
server_hello_parsed: parsed,
app_data_records_sizes: app_data_records_sizes.clone(),
total_app_data_len: app_data_records_sizes.iter().sum(),
})
}