Add: H3 support - incomplete
This commit is contained in:
@@ -35,6 +35,9 @@ pub struct TlsCertSummary {
|
||||
pub not_before: String,
|
||||
pub not_after: String,
|
||||
pub san: Vec<String>,
|
||||
pub signature_algorithm: Option<String>,
|
||||
pub key_usage: Option<Vec<String>>,
|
||||
pub extended_key_usage: Option<Vec<String>>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||
@@ -45,6 +48,7 @@ pub struct TlsHandshakeReport {
|
||||
pub alpn_negotiated: Option<String>,
|
||||
pub tls_version: Option<String>,
|
||||
pub cipher: Option<String>,
|
||||
pub ocsp_stapled: Option<bool>,
|
||||
pub cert_chain: Vec<TlsCertSummary>,
|
||||
}
|
||||
|
||||
@@ -56,6 +60,7 @@ pub struct TlsVerifyReport {
|
||||
pub alpn_negotiated: Option<String>,
|
||||
pub tls_version: Option<String>,
|
||||
pub cipher: Option<String>,
|
||||
pub ocsp_stapled: Option<bool>,
|
||||
pub verified: bool,
|
||||
pub error: Option<String>,
|
||||
}
|
||||
@@ -64,6 +69,7 @@ pub struct TlsVerifyReport {
|
||||
pub struct TlsCertReport {
|
||||
pub target: String,
|
||||
pub sni: Option<String>,
|
||||
pub ocsp_stapled: Option<bool>,
|
||||
pub cert_chain: Vec<TlsCertSummary>,
|
||||
}
|
||||
|
||||
@@ -83,6 +89,8 @@ pub struct TlsOptions {
|
||||
pub insecure: bool,
|
||||
pub socks5: Option<String>,
|
||||
pub prefer_ipv4: bool,
|
||||
pub show_extensions: bool,
|
||||
pub ocsp: bool,
|
||||
}
|
||||
|
||||
pub async fn handshake(target: &str, options: TlsOptions) -> Result<TlsHandshakeReport, TlsError> {
|
||||
@@ -120,7 +128,8 @@ pub async fn handshake(target: &str, options: TlsOptions) -> Result<TlsHandshake
|
||||
cipher: session
|
||||
.negotiated_cipher_suite()
|
||||
.map(|suite| format!("{suite:?}")),
|
||||
cert_chain: extract_cert_chain(session.peer_certificates())?,
|
||||
ocsp_stapled: ocsp_status(session, options.ocsp),
|
||||
cert_chain: extract_cert_chain(session.peer_certificates(), options.show_extensions)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -160,6 +169,7 @@ pub async fn verify(target: &str, options: TlsOptions) -> Result<TlsVerifyReport
|
||||
cipher: session
|
||||
.negotiated_cipher_suite()
|
||||
.map(|suite| format!("{suite:?}")),
|
||||
ocsp_stapled: ocsp_status(session, options.ocsp),
|
||||
verified: true,
|
||||
error: None,
|
||||
})
|
||||
@@ -171,6 +181,7 @@ pub async fn verify(target: &str, options: TlsOptions) -> Result<TlsVerifyReport
|
||||
alpn_negotiated: None,
|
||||
tls_version: None,
|
||||
cipher: None,
|
||||
ocsp_stapled: None,
|
||||
verified: false,
|
||||
error: Some(err.to_string()),
|
||||
}),
|
||||
@@ -203,7 +214,8 @@ pub async fn certs(target: &str, options: TlsOptions) -> Result<TlsCertReport, T
|
||||
Ok(TlsCertReport {
|
||||
target: target.to_string(),
|
||||
sni: options.sni,
|
||||
cert_chain: extract_cert_chain(session.peer_certificates())?,
|
||||
ocsp_stapled: ocsp_status(session, options.ocsp),
|
||||
cert_chain: extract_cert_chain(session.peer_certificates(), options.show_extensions)?,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -427,26 +439,41 @@ fn socks5_target_host(proxy: &str, host: &str) -> (String, bool) {
|
||||
(host.to_string(), remote_dns)
|
||||
}
|
||||
|
||||
fn extract_cert_chain(certs: Option<&[Certificate]>) -> Result<Vec<TlsCertSummary>, TlsError> {
|
||||
fn extract_cert_chain(
|
||||
certs: Option<&[Certificate]>,
|
||||
show_extensions: bool,
|
||||
) -> Result<Vec<TlsCertSummary>, TlsError> {
|
||||
let mut results = Vec::new();
|
||||
if let Some(certs) = certs {
|
||||
for cert in certs {
|
||||
let summary = parse_cert(&cert.0)?;
|
||||
let summary = parse_cert(&cert.0, show_extensions)?;
|
||||
results.push(summary);
|
||||
}
|
||||
}
|
||||
Ok(results)
|
||||
}
|
||||
|
||||
fn parse_cert(der: &[u8]) -> Result<TlsCertSummary, TlsError> {
|
||||
fn parse_cert(der: &[u8], show_extensions: bool) -> Result<TlsCertSummary, TlsError> {
|
||||
let (_, cert) =
|
||||
X509Certificate::from_der(der).map_err(|err| TlsError::Parse(err.to_string()))?;
|
||||
let (key_usage, extended_key_usage, signature_algorithm) = if show_extensions {
|
||||
(
|
||||
extract_key_usage(&cert),
|
||||
extract_extended_key_usage(&cert),
|
||||
Some(cert.signature_algorithm.algorithm.to_string()),
|
||||
)
|
||||
} else {
|
||||
(None, None, None)
|
||||
};
|
||||
Ok(TlsCertSummary {
|
||||
subject: cert.subject().to_string(),
|
||||
issuer: cert.issuer().to_string(),
|
||||
not_before: cert.validity().not_before.to_string(),
|
||||
not_after: cert.validity().not_after.to_string(),
|
||||
san: extract_san(&cert),
|
||||
signature_algorithm,
|
||||
key_usage,
|
||||
extended_key_usage,
|
||||
})
|
||||
}
|
||||
|
||||
@@ -460,6 +487,85 @@ fn extract_san(cert: &X509Certificate<'_>) -> Vec<String> {
|
||||
result
|
||||
}
|
||||
|
||||
fn extract_key_usage(cert: &X509Certificate<'_>) -> Option<Vec<String>> {
|
||||
let ext = cert.key_usage().ok()??;
|
||||
let mut result = Vec::new();
|
||||
if ext.value.digital_signature() {
|
||||
result.push("digitalSignature".to_string());
|
||||
}
|
||||
if ext.value.non_repudiation() {
|
||||
result.push("nonRepudiation".to_string());
|
||||
}
|
||||
if ext.value.key_encipherment() {
|
||||
result.push("keyEncipherment".to_string());
|
||||
}
|
||||
if ext.value.data_encipherment() {
|
||||
result.push("dataEncipherment".to_string());
|
||||
}
|
||||
if ext.value.key_agreement() {
|
||||
result.push("keyAgreement".to_string());
|
||||
}
|
||||
if ext.value.key_cert_sign() {
|
||||
result.push("keyCertSign".to_string());
|
||||
}
|
||||
if ext.value.crl_sign() {
|
||||
result.push("cRLSign".to_string());
|
||||
}
|
||||
if ext.value.encipher_only() {
|
||||
result.push("encipherOnly".to_string());
|
||||
}
|
||||
if ext.value.decipher_only() {
|
||||
result.push("decipherOnly".to_string());
|
||||
}
|
||||
if result.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn extract_extended_key_usage(cert: &X509Certificate<'_>) -> Option<Vec<String>> {
|
||||
let ext = cert.extended_key_usage().ok()??;
|
||||
let mut result = Vec::new();
|
||||
if ext.value.any {
|
||||
result.push("any".to_string());
|
||||
}
|
||||
if ext.value.server_auth {
|
||||
result.push("serverAuth".to_string());
|
||||
}
|
||||
if ext.value.client_auth {
|
||||
result.push("clientAuth".to_string());
|
||||
}
|
||||
if ext.value.code_signing {
|
||||
result.push("codeSigning".to_string());
|
||||
}
|
||||
if ext.value.email_protection {
|
||||
result.push("emailProtection".to_string());
|
||||
}
|
||||
if ext.value.time_stamping {
|
||||
result.push("timeStamping".to_string());
|
||||
}
|
||||
if ext.value.ocsp_signing {
|
||||
result.push("ocspSigning".to_string());
|
||||
}
|
||||
for oid in &ext.value.other {
|
||||
result.push(oid.to_string());
|
||||
}
|
||||
if result.is_empty() {
|
||||
None
|
||||
} else {
|
||||
Some(result)
|
||||
}
|
||||
}
|
||||
|
||||
fn ocsp_status(_session: &rustls::ClientConnection, enabled: bool) -> Option<bool> {
|
||||
if enabled {
|
||||
None
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
struct NoVerifier;
|
||||
|
||||
impl rustls::client::ServerCertVerifier for NoVerifier {
|
||||
|
||||
Reference in New Issue
Block a user