Skip to content

Commit 237e64f

Browse files
authored
refactor: Introduce ChainVerifier struct
1 parent ab1cda2 commit 237e64f

File tree

6 files changed

+427
-416
lines changed

6 files changed

+427
-416
lines changed

src/chain_verifier.rs

Lines changed: 119 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::chain_verifier::ChainVerificationFailureReason::{
2-
CertificateExpired, InvalidCertificate, InvalidChainLength, InvalidEffectiveDate,
2+
CertificateExpired, InvalidCertificate, InvalidEffectiveDate,
33
};
44
use thiserror::Error;
55

@@ -47,148 +47,144 @@ pub enum ChainVerificationFailureReason {
4747
CertificateRevoked,
4848
}
4949

50-
const EXPECTED_CHAIN_LENGTH: usize = 3;
51-
52-
/// Verifies a certificate chain.
53-
///
54-
/// This function verifies a certificate chain consisting of multiple certificates. It performs various
55-
/// checks to ensure the validity and integrity of the chain.
56-
///
57-
/// # Arguments
58-
///
59-
/// * `certificates`: A vector of byte slices containing the certificates in the chain.
60-
/// * `root_certificates`: A vector of byte slices containing the root certificates.
61-
/// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
62-
///
63-
/// # Returns
64-
///
65-
/// * `Ok(Vec<u8>)`: If the certificate chain is valid, it returns the public key data from the leaf certificate.
66-
/// * `Err(ChainVerifierError)`: If the chain verification fails for any reason, it returns a `ChainVerifierError` enum.
50+
/// A structure for verifying certificate chains.
6751
///
68-
/// # Example
69-
///
70-
/// ```rust
71-
/// use app_store_server_library::chain_verifier::{verify_chain, ChainVerifierError};
72-
///
73-
/// fn main() {
74-
/// let certificates: Vec<Vec<u8>> = vec![]; // Load your certificates here
75-
/// let root_certificates: Vec<Vec<u8>> = vec![]; // Load your root certificates here
76-
/// let effective_date: Option<u64> = None; // Provide an effective date if needed
77-
///
78-
/// match verify_chain(&certificates, &root_certificates, effective_date) {
79-
/// Ok(public_key) => println!("Certificate chain is valid. Public key: {:?}", public_key),
80-
/// Err(err) => eprintln!("Certificate chain verification failed: {}", err),
81-
/// }
82-
/// }
83-
/// ```
84-
///
85-
/// TODO: Implement issuer checking
86-
pub fn verify_chain(
87-
certificates: &Vec<Vec<u8>>,
88-
root_certificates: &Vec<Vec<u8>>,
89-
effective_date: Option<u64>,
90-
) -> Result<Vec<u8>, ChainVerifierError> {
91-
if root_certificates.is_empty() {
92-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
93-
}
52+
/// This struct holds the root certificates and provides methods to verify certificate chains.
53+
pub struct ChainVerifier {
54+
root_certificates: Vec<Vec<u8>>,
55+
}
9456

95-
if certificates.len() != EXPECTED_CHAIN_LENGTH {
96-
return Err(ChainVerifierError::VerificationFailure(InvalidChainLength));
57+
impl ChainVerifier {
58+
/// Creates a new `ChainVerifier` with the provided root certificates.
59+
///
60+
/// # Arguments
61+
///
62+
/// * `root_certificates`: A vector of byte slices containing the root certificates.
63+
///
64+
/// # Returns
65+
///
66+
/// A new instance of `ChainVerifier`.
67+
pub fn new(root_certificates: Vec<Vec<u8>>) -> Self {
68+
ChainVerifier { root_certificates }
9769
}
9870

99-
let leaf_certificate = &certificates[0];
100-
let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
101-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
102-
};
103-
let leaf_certificate = leaf_certificate.1;
104-
105-
let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))? else {
106-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
107-
};
108-
109-
let intermediate_certificate = &certificates[1];
110-
let Ok(intermediate_certificate) = X509Certificate::from_der(intermediate_certificate.as_slice()) else {
111-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
112-
};
113-
let intermediate_certificate = intermediate_certificate.1;
71+
/// Verifies a certificate pair (leaf and intermediate).
72+
///
73+
/// This method verifies a leaf certificate against an intermediate certificate. It performs various
74+
/// checks to ensure the validity and integrity of the certificates.
75+
///
76+
/// # Arguments
77+
///
78+
/// * `leaf_certificate`: The leaf certificate as a byte slice.
79+
/// * `intermediate_certificate`: The intermediate certificate as a byte slice.
80+
/// * `effective_date`: An optional Unix timestamp representing the effective date for the chain validation.
81+
///
82+
/// # Returns
83+
///
84+
/// * `Ok(Vec<u8>)`: If the certificates are valid, it returns the public key data from the leaf certificate.
85+
/// * `Err(ChainVerifierError)`: If the verification fails for any reason, it returns a `ChainVerifierError` enum.
86+
/// TODO: Implement issuer checking
87+
pub fn verify(
88+
&self,
89+
leaf_certificate: &Vec<u8>,
90+
intermediate_certificate: &Vec<u8>,
91+
effective_date: Option<u64>,
92+
) -> Result<Vec<u8>, ChainVerifierError> {
93+
if self.root_certificates.is_empty() {
94+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
95+
}
96+
let Ok(leaf_certificate) = X509Certificate::from_der(leaf_certificate.as_slice()) else {
97+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
98+
};
99+
let leaf_certificate = leaf_certificate.1;
114100

115-
let Some(_) = intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))? else {
116-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
117-
};
101+
let Some(_) = leaf_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.11.1))? else {
102+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
103+
};
118104

119-
let mut root_certificate: Option<X509Certificate> = None;
105+
let Ok(intermediate_certificate) = X509Certificate::from_der(intermediate_certificate.as_slice()) else {
106+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
107+
};
108+
let intermediate_certificate = intermediate_certificate.1;
120109

121-
for cert in root_certificates {
122-
let Ok(cert) = X509Certificate::from_der(&cert) else {
110+
let Some(_) = intermediate_certificate.get_extension_unique(&oid!(1.2.840.113635.100.6.2.1))? else {
123111
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
124112
};
125113

126-
match intermediate_certificate.verify_signature(Some(cert.1.public_key())) {
127-
Ok(_) => (),
128-
Err(_) => continue,
129-
}
114+
let mut root_certificate: Option<X509Certificate> = None;
130115

131-
root_certificate = Some(cert.1)
132-
}
116+
for cert in &self.root_certificates {
117+
let Ok(cert) = X509Certificate::from_der(&cert) else {
118+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
119+
};
133120

134-
let Some(root_certificate) = root_certificate else {
135-
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
136-
};
121+
match intermediate_certificate.verify_signature(Some(cert.1.public_key())) {
122+
Ok(_) => (),
123+
Err(_) => continue,
124+
}
137125

138-
verify_chain_impl(
139-
&leaf_certificate,
140-
&intermediate_certificate,
141-
&root_certificate,
142-
effective_date,
143-
)
144-
}
126+
root_certificate = Some(cert.1)
127+
}
145128

146-
fn verify_chain_impl(
147-
leaf: &X509Certificate,
148-
intermediate: &X509Certificate,
149-
root_certificate: &X509Certificate,
150-
effective_date: Option<u64>,
151-
) -> Result<Vec<u8>, ChainVerifierError> {
152-
leaf.verify_signature(Some(intermediate.public_key()))?;
153-
154-
if let Some(date) = effective_date {
155-
let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
156-
return Err(ChainVerifierError::VerificationFailure(
157-
InvalidEffectiveDate,
158-
));
129+
let Some(root_certificate) = root_certificate else {
130+
return Err(ChainVerifierError::VerificationFailure(InvalidCertificate));
159131
};
160132

161-
if !(root_certificate.validity.is_valid_at(time) &&
162-
leaf.validity.is_valid_at(time) &&
163-
intermediate.validity.is_valid_at(time))
164-
{
165-
return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
166-
}
133+
self.verify_chain(
134+
&leaf_certificate,
135+
&intermediate_certificate,
136+
&root_certificate,
137+
effective_date,
138+
)
167139
}
168140

169-
let k = leaf.public_key().raw.to_vec();
170-
171-
// Make online verification as additional step if ocsp flag enabled
172-
#[cfg(all(feature = "ocsp"))]
173-
{
174-
use crate::chain_verifier_ocsp::check_ocsp_status;
175-
// Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
176-
match check_ocsp_status(leaf, intermediate) {
177-
Ok(()) => {
178-
// Certificate is valid according to OCSP
179-
}
180-
Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
181-
// Certificate is revoked - this should fail
141+
fn verify_chain(
142+
&self,
143+
leaf: &X509Certificate,
144+
intermediate: &X509Certificate,
145+
root_certificate: &X509Certificate,
146+
effective_date: Option<u64>,
147+
) -> Result<Vec<u8>, ChainVerifierError> {
148+
leaf.verify_signature(Some(intermediate.public_key()))?;
149+
150+
if let Some(date) = effective_date {
151+
let Ok(time) = ASN1Time::from_timestamp(i64::try_from(date).unwrap()) else {
182152
return Err(ChainVerifierError::VerificationFailure(
183-
ChainVerificationFailureReason::CertificateRevoked,
153+
InvalidEffectiveDate,
184154
));
185-
}
186-
Err(e) => {
187-
// Other OCSP errors (network, parsing, etc.) - log but don't fail
188-
eprintln!("OCSP check failed (non-fatal): {:?}", e);
155+
};
156+
157+
if !(root_certificate.validity.is_valid_at(time) &&
158+
leaf.validity.is_valid_at(time) &&
159+
intermediate.validity.is_valid_at(time))
160+
{
161+
return Err(ChainVerifierError::VerificationFailure(CertificateExpired));
189162
}
190163
}
191-
};
192164

193-
Ok(k)
194-
}
165+
let k = leaf.public_key().raw.to_vec();
166+
167+
// Make online verification as additional step if ocsp flag enabled
168+
#[cfg(all(feature = "ocsp"))]
169+
{
170+
// Perform OCSP check - this is best-effort, so we don't fail on OCSP errors
171+
match self.check_ocsp_status(leaf, intermediate) {
172+
Ok(()) => {
173+
// Certificate is valid according to OCSP
174+
}
175+
Err(ChainVerifierError::VerificationFailure(ChainVerificationFailureReason::CertificateRevoked)) => {
176+
// Certificate is revoked - this should fail
177+
return Err(ChainVerifierError::VerificationFailure(
178+
ChainVerificationFailureReason::CertificateRevoked,
179+
));
180+
}
181+
Err(e) => {
182+
// Other OCSP errors (network, parsing, etc.) - log but don't fail
183+
eprintln!("OCSP check failed (non-fatal): {:?}", e);
184+
}
185+
}
186+
};
187+
188+
Ok(k)
189+
}
190+
}

0 commit comments

Comments
 (0)