refactor: verify_cert_from_res
CI / format (push) Has been cancelled
CI / test (push) Has been cancelled
CI / packaging (push) Has been cancelled

This commit is contained in:
Tien Do Nam
2026-02-27 03:10:09 +01:00
parent 731a48b10f
commit f657c76f56
4 changed files with 37 additions and 48 deletions
+8 -8
View File
@@ -4,14 +4,14 @@ use x509_parser::certificate::X509Certificate;
use x509_parser::pem::Pem;
use x509_parser::x509::SubjectPublicKeyInfo;
pub fn verify_cert_from_pem(cert: String, public_key: Option<String>) -> anyhow::Result<()> {
pub fn verify_cert_from_pem(cert: String, public_key: Option<&str>) -> anyhow::Result<()> {
let (cert_pem, _) = Pem::read(Cursor::new(cert.into_bytes()))?;
let parsed_cert: X509Certificate = cert_pem.parse_x509()?;
verify_cert_from_cert(parsed_cert, public_key)
}
pub fn verify_cert_from_der(cert: &[u8], public_key: Option<String>) -> anyhow::Result<()> {
pub fn verify_cert_from_der(cert: &[u8], public_key: Option<&str>) -> anyhow::Result<()> {
let (_, parsed_cert) = X509Certificate::from_der(&cert)?;
verify_cert_from_cert(parsed_cert, public_key)
@@ -21,7 +21,7 @@ pub fn verify_cert_from_der(cert: &[u8], public_key: Option<String>) -> anyhow::
/// - according to the signature
/// - according to the time validity
/// - according to the public key (if provided)
fn verify_cert_from_cert(cert: X509Certificate, public_key: Option<String>) -> anyhow::Result<()> {
fn verify_cert_from_cert(cert: X509Certificate, public_key: Option<&str>) -> anyhow::Result<()> {
if !cert.validity.is_valid() {
return Err(anyhow::anyhow!("Time validity error"));
}
@@ -29,7 +29,7 @@ fn verify_cert_from_cert(cert: X509Certificate, public_key: Option<String>) -> a
if let Some(public_key) = public_key {
let cert_public_key = cert.tbs_certificate.subject_pki.parsed()?;
let (public_key_pem, _) = Pem::read(Cursor::new(public_key.into_bytes()))?;
let (public_key_pem, _) = Pem::read(Cursor::new(public_key.as_bytes()))?;
let (_, public_key_spki) = SubjectPublicKeyInfo::from_der(&public_key_pem.contents)?;
let expected_public_key = public_key_spki.parsed()?;
@@ -103,7 +103,7 @@ VRus1zGVD8IVpIdPMyz01WJyS7M0fWaHXKWo+Bo=
-----END CERTIFICATE-----"
.to_string();
assert_eq!(
verify_cert_from_pem(cert, Some(PUBLIC_KEY.to_owned())).map_err(|e| e.to_string()),
verify_cert_from_pem(cert, Some(PUBLIC_KEY)).map_err(|e| e.to_string()),
Ok(())
);
@@ -128,7 +128,7 @@ VRus1zGVD8IVpIdPMyz01WJySwAAAAAAAAAAAAA=
-----END CERTIFICATE-----"
.to_string();
assert_eq!(
verify_cert_from_pem(cert_invalid_signature, Some(PUBLIC_KEY.to_owned()))
verify_cert_from_pem(cert_invalid_signature, Some(PUBLIC_KEY))
.map_err(|e| e.to_string()),
Err("signature verification error".to_string())
);
@@ -154,7 +154,7 @@ eVVihnrJ3sdk7nnreAYMse/OipyufRyZ9t3WU8A=
-----END CERTIFICATE-----"
.to_string();
assert_eq!(
verify_cert_from_pem(cert_invalid_public_key, Some(PUBLIC_KEY.to_owned()))
verify_cert_from_pem(cert_invalid_public_key, Some(PUBLIC_KEY))
.map_err(|e| e.to_string()),
Err("Public key mismatch".to_string())
);
@@ -180,7 +180,7 @@ nidU/qXQvBJ7NPUkXXgbcgqxK735iijOqQHmKts=
-----END CERTIFICATE-----"
.to_string();
assert_eq!(
verify_cert_from_pem(cert_expired, Some(PUBLIC_KEY.to_owned()))
verify_cert_from_pem(cert_expired, Some(PUBLIC_KEY))
.map_err(|e| e.to_string()),
Err("Time validity error".to_string())
);
+24
View File
@@ -5,7 +5,31 @@ pub mod v3;
pub use v2::LsHttpClientV2;
pub use v3::LsHttpClientV3;
use crate::crypto;
use reqwest::Response;
pub enum LsHttpClient {
V2(LsHttpClientV2),
V3(LsHttpClientV3),
}
/// Verifies the certificate from the response.
/// Returns the public key extracted from the certificate.
pub(super) fn verify_cert_from_res(
response: &Response,
public_key: Option<String>,
) -> anyhow::Result<String> {
let tls_info_ext = response
.extensions()
.get::<reqwest::tls::TlsInfo>()
.ok_or_else(|| anyhow::anyhow!("TLS info not found"))?;
let cert = tls_info_ext
.peer_certificate()
.ok_or_else(|| anyhow::anyhow!("Certificate not found"))?;
crypto::cert::verify_cert_from_der(cert, public_key.as_deref())?;
let public_key = match public_key {
Some(public_key) => public_key,
None => crypto::cert::public_key_from_cert_der(cert)?,
};
Ok(public_key)
}
+2 -20
View File
@@ -4,7 +4,6 @@ use crate::http::dto_v2::{
PrepareUploadResponseDtoV2, ProtocolTypeV2, RegisterDtoV2, RegisterResponseDtoV2,
};
use crate::http::StatusCodeError;
use crate::crypto;
use futures_util::StreamExt;
use reqwest::{Response, StatusCode};
use serde::{Deserialize, Serialize};
@@ -120,7 +119,7 @@ impl LsHttpClientV2 {
}
let public_key = match protocol {
ProtocolTypeV2::Https => Some(verify_cert_from_res(&res, None)?),
ProtocolTypeV2::Https => Some(super::verify_cert_from_res(&res, None)?),
_ => None,
};
@@ -186,7 +185,7 @@ impl LsHttpClientV2 {
.await?;
if let Some(public_key) = public_key {
verify_cert_from_res(&res, Some(public_key))?;
super::verify_cert_from_res(&res, Some(public_key))?;
}
if res.status() == StatusCode::NO_CONTENT {
@@ -514,23 +513,6 @@ async fn status_code_error_from_res(response: Response) -> anyhow::Result<anyhow
}))
}
/// Verifies the certificate from the response.
/// Returns the public key extracted from the certificate.
fn verify_cert_from_res(response: &Response, public_key: Option<String>) -> anyhow::Result<String> {
let tls_info_ext = response
.extensions()
.get::<reqwest::tls::TlsInfo>()
.ok_or_else(|| anyhow::anyhow!("TLS info not found"))?;
let cert = tls_info_ext
.peer_certificate()
.ok_or_else(|| anyhow::anyhow!("Certificate not found"))?;
let public_key = match public_key {
Some(public_key) => public_key.to_owned(),
None => crypto::cert::public_key_from_cert_der(cert)?,
};
crypto::cert::verify_cert_from_der(cert, Some(public_key.clone()))?;
Ok(public_key)
}
#[cfg(test)]
mod tests {
+3 -20
View File
@@ -142,7 +142,7 @@ impl LsHttpClientV3 {
.await?;
let public_key = match protocol {
ProtocolType::Https => Some(verify_cert_from_res(&res, None)?),
ProtocolType::Https => Some(super::verify_cert_from_res(&res, None)?),
_ => None,
};
@@ -174,7 +174,7 @@ impl LsHttpClientV3 {
.await?;
if let Some(public_key) = public_key {
verify_cert_from_res(&res, Some(public_key))?;
super::verify_cert_from_res(&res, Some(public_key))?;
}
if res.status() != StatusCode::OK {
@@ -272,7 +272,7 @@ fn to_identifier(
public_key: Option<String>,
) -> anyhow::Result<String> {
match require_cert {
true => verify_cert_from_res(response, public_key),
true => super::verify_cert_from_res(response, public_key),
false => response
.remote_addr()
.map(|addr| addr.ip().to_string())
@@ -280,20 +280,3 @@ fn to_identifier(
}
}
/// Verifies the certificate from the response.
/// Returns the public key extracted from the certificate.
fn verify_cert_from_res(response: &Response, public_key: Option<String>) -> anyhow::Result<String> {
let tls_info_ext = response
.extensions()
.get::<reqwest::tls::TlsInfo>()
.ok_or_else(|| anyhow::anyhow!("TLS info not found"))?;
let cert = tls_info_ext
.peer_certificate()
.ok_or_else(|| anyhow::anyhow!("Certificate not found"))?;
let public_key = match public_key {
Some(public_key) => public_key.to_owned(),
None => crypto::cert::public_key_from_cert_der(cert)?,
};
crypto::cert::verify_cert_from_der(cert, Some(public_key.clone()))?;
Ok(public_key)
}