Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

crypto_glue/x509/
chain.rs

1use crate::x509::{AlgorithmIdentifier, BasicConstraints, Certificate, KeyUsage, oiddb::rfc5912};
2use crate::{
3    ecdsa_p256::{EcdsaP256DerSignature, EcdsaP256PublicKey, EcdsaP256VerifyingKey},
4    ecdsa_p384::{EcdsaP384DerSignature, EcdsaP384PublicKey, EcdsaP384VerifyingKey},
5    rsa::{RS256PublicKey, RS256Signature, RS256VerifyingKey, RS384VerifyingKey},
6    s256::{Sha256, Sha256Output},
7    traits::{Digest, Verifier, hazmat::PrehashVerifier},
8};
9use der::Encode;
10use der::referenced::OwnedToRef;
11use std::time::{Duration, SystemTime};
12use tracing::{debug, error, trace, warn};
13
14#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum X509VerificationError {
16    InvalidSystemTime,
17    BasicConstraintsNotPresent,
18    LeafMustNotBeCA,
19    KeyUsageNotValid,
20    ExtensionFailure,
21    NotBefore,
22    NotAfter,
23    NoMatchingIssuer,
24    InvalidIssuer,
25    ExcessivePathLength,
26    CaNotMarkedAsSuch,
27    PathLengthExceeded,
28    SignatureAlgorithmMismatch,
29    SignatureAlgorithmNotImplemented,
30    DerSignatureInvalid,
31    VerifyingKeyFromSpki,
32    SignatureVerificationFailed,
33    CertificateSerialisation,
34    KeyUsageNotPresent,
35    SubjectPublicKeyInformationInvalid,
36}
37
38pub struct X509Store {
39    store: Vec<Certificate>,
40    leaf_basic_constraints_required: bool,
41}
42
43impl X509Store {
44    pub fn new(ca_roots: &[Certificate]) -> Self {
45        Self {
46            store: ca_roots.iter().map(|c| (*c).clone()).collect(),
47            leaf_basic_constraints_required: true,
48        }
49    }
50
51    pub fn leaf_basic_constraints_required(&mut self, enable: bool) {
52        self.leaf_basic_constraints_required = enable;
53    }
54
55    pub fn verify(
56        &self,
57        leaf: &Certificate,
58        intermediates: &[Certificate],
59        current_time: SystemTime,
60    ) -> Result<&Certificate, X509VerificationError> {
61        // To verify this, we need to get the "rightmost" certificate that we then
62        // check is valid wrt to our store.
63        //
64        // Our caller has passed in:
65        //
66        // [ leaf, inter, inter, ... ]
67        //
68        // where the signing flows right to left
69        //
70        // [ leaf <- inter <- inter, ... ]
71        //
72        // So the initial stage is to validate the intermediate chain, and determine
73        // the intermediate closest to the root.
74
75        let current_time_unix = current_time
76            .duration_since(SystemTime::UNIX_EPOCH)
77            .map_err(|_| X509VerificationError::InvalidSystemTime)?;
78
79        let mut certificate_to_validate = leaf;
80        self.validate_leaf(certificate_to_validate, current_time_unix)?;
81
82        // PATH LENGTH
83        let mut path_length = 0;
84
85        for intermediate in intermediates {
86            // validate that intermediate signed the current certificate we
87            // are scrutinising.
88
89            self.validate_pair(
90                certificate_to_validate,
91                intermediate,
92                current_time_unix,
93                path_length,
94            )?;
95
96            // If it was valid, we now need to check the intermediate next.
97            certificate_to_validate = intermediate;
98
99            // The path length now increments by one as we added a CA to the path.
100            path_length = path_length
101                .checked_add(1)
102                .ok_or(X509VerificationError::ExcessivePathLength)?;
103        }
104
105        // Now, the certificate_to_validate is positioned. We have either validated
106        // the chain of intermediates to the leaf, or the leaf was the only certificate
107        // present.
108
109        // At this point, we now can check that our ca_store actually contains
110        // something that validates this certificate.
111
112        let authority_cert = self.locate_authority_certificate(certificate_to_validate)?;
113
114        self.validate_pair(
115            certificate_to_validate,
116            authority_cert,
117            current_time_unix,
118            path_length,
119        )?;
120
121        // At this point we have established the chain back to the CA is valid along
122        // the path of intermediates.
123
124        // That's it! Return the CA that ultimately signed this chain.
125        Ok(authority_cert)
126    }
127
128    fn validate_leaf(
129        &self,
130        certificate_to_validate: &Certificate,
131        current_time: Duration,
132    ) -> Result<(), X509VerificationError> {
133        // Client Leaf Cert
134        //   Basic Constraints: critical
135        //     CA:FALSE
136        let maybe_basic_constraints = certificate_to_validate
137            .tbs_certificate()
138            .get_extension::<BasicConstraints>()
139            .map_err(|_err| X509VerificationError::ExtensionFailure)?;
140        // If not present, we act as if this is not a CA.
141        // .ok_or(
142
143        if let Some((_critical, basic_constraints)) = maybe_basic_constraints {
144            if basic_constraints.ca {
145                return Err(X509VerificationError::LeafMustNotBeCA);
146            }
147        } else if self.leaf_basic_constraints_required {
148            return Err(X509VerificationError::BasicConstraintsNotPresent);
149        };
150
151        let maybe_keyusage = certificate_to_validate
152            .tbs_certificate()
153            .get_extension::<KeyUsage>()
154            .map_err(|_err| X509VerificationError::ExtensionFailure)?;
155
156        if let Some((_critical, key_usage)) = maybe_keyusage {
157            //   Key Usage: critical
158            //     Digital Signature
159            if !key_usage.digital_signature() {
160                return Err(X509VerificationError::KeyUsageNotValid);
161            }
162        }
163
164        // Valid time range.
165        let not_before = certificate_to_validate
166            .tbs_certificate()
167            .validity()
168            .not_before
169            .to_unix_duration();
170
171        if not_before > current_time {
172            trace!(?not_before, ?current_time);
173            return Err(X509VerificationError::NotBefore);
174        }
175
176        let not_after = certificate_to_validate
177            .tbs_certificate()
178            .validity()
179            .not_after
180            .to_unix_duration();
181
182        if current_time > not_after {
183            debug!(?current_time, ?not_after);
184            return Err(X509VerificationError::NotAfter);
185        }
186
187        Ok(())
188    }
189
190    fn validate_pair(
191        &self,
192        certificate_to_validate: &Certificate,
193        authority: &Certificate,
194        current_time: Duration,
195        path_length: u8,
196    ) -> Result<(), X509VerificationError> {
197        if authority.tbs_certificate().subject()
198            != certificate_to_validate.tbs_certificate().issuer()
199        {
200            return Err(X509VerificationError::InvalidIssuer);
201        }
202
203        // Intermediate:
204        //   Basic Constraints: critical
205        //     CA:TRUE
206        //     pathlen:0  // indicates no subordinate CA's
207
208        let (_critical, basic_constraints) = authority
209            .tbs_certificate()
210            .get_extension::<BasicConstraints>()
211            .map_err(|_err| X509VerificationError::ExtensionFailure)?
212            // You are a CA, you must have this.
213            .ok_or(X509VerificationError::BasicConstraintsNotPresent)?;
214
215        if !basic_constraints.ca {
216            return Err(X509VerificationError::CaNotMarkedAsSuch);
217        }
218
219        if let Some(ca_pathlen) = basic_constraints.path_len_constraint {
220            // The current depth of the validation path exceeds that of the
221            // allowed path length of the certificate.
222            if path_length > ca_pathlen {
223                return Err(X509VerificationError::PathLengthExceeded);
224            }
225        }
226
227        let (_critical, key_usage) = authority
228            .tbs_certificate()
229            .get_extension::<KeyUsage>()
230            .map_err(|_err| X509VerificationError::ExtensionFailure)?
231            .ok_or(X509VerificationError::KeyUsageNotPresent)?;
232
233        if !key_usage.key_cert_sign() {
234            return Err(X509VerificationError::KeyUsageNotValid);
235        }
236
237        // Valid time range.
238        let not_before = authority
239            .tbs_certificate()
240            .validity()
241            .not_before
242            .to_unix_duration();
243
244        if not_before > current_time {
245            return Err(X509VerificationError::NotBefore);
246        }
247
248        let not_after = authority
249            .tbs_certificate()
250            .validity()
251            .not_after
252            .to_unix_duration();
253
254        if current_time > not_after {
255            return Err(X509VerificationError::NotAfter);
256        }
257
258        // Now validate the signature of the certificate_to_validate
259        // A reasonable person would assume that a CA can only issue certificates using the same
260        // algorithm that it declares that it uses. However, that's simply just false, some
261        // CA's, especially that use RSA, may use a different digest.
262        if certificate_to_validate.signature_algorithm() != authority.tbs_certificate().signature()
263        {
264            warn!(validate_signature = ?certificate_to_validate.signature_algorithm(), authority_signature = ?authority.tbs_certificate().signature());
265            // return Err(X509VerificationError::SignatureAlgorithmMismatch);
266        }
267
268        let cert_to_validate_data = certificate_to_validate
269            .tbs_certificate()
270            .to_der()
271            .map_err(|_err| X509VerificationError::CertificateSerialisation)?;
272
273        let cert_to_validate_signature = certificate_to_validate
274            .signature()
275            .as_bytes()
276            .ok_or(X509VerificationError::DerSignatureInvalid)?;
277
278        verify_der_signature(
279            &cert_to_validate_data,
280            cert_to_validate_signature,
281            certificate_to_validate.signature_algorithm(),
282            authority,
283        )?;
284
285        Ok(())
286    }
287
288    fn locate_authority_certificate(
289        &self,
290        certificate_to_validate: &Certificate,
291    ) -> Result<&Certificate, X509VerificationError> {
292        self.store
293            .iter()
294            .find(|ca_cert| {
295                ca_cert.tbs_certificate().subject()
296                    == certificate_to_validate.tbs_certificate().issuer()
297            })
298            .ok_or(X509VerificationError::NoMatchingIssuer)
299    }
300}
301
302// We can't use the generic x509_verify_signature here because the format
303// of these signatures within an x509 cert is DER and different than the
304// "generic" signatures you may get on something like a JWT
305fn verify_der_signature(
306    data: &[u8],
307    signature: &[u8],
308    signature_algorithm: &AlgorithmIdentifier<der::Any>,
309    certificate: &Certificate,
310) -> Result<(), X509VerificationError> {
311    let subject_public_key_info = certificate
312        .tbs_certificate()
313        .subject_public_key_info()
314        .owned_to_ref();
315
316    let (spki_alg_oid, spki_alg_params) = subject_public_key_info
317        .algorithm
318        .oids()
319        .map_err(|_| X509VerificationError::SubjectPublicKeyInformationInvalid)?;
320
321    trace!(?signature_algorithm.oid, ?spki_alg_oid, ?spki_alg_params);
322
323    match (signature_algorithm.oid, spki_alg_oid, spki_alg_params) {
324        (rfc5912::ECDSA_WITH_SHA_256, rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_256_R_1)) => {
325            let signature = EcdsaP256DerSignature::try_from(signature).map_err(|err| {
326                error!(?err);
327                X509VerificationError::DerSignatureInvalid
328            })?;
329
330            let verifier = EcdsaP256PublicKey::try_from(subject_public_key_info)
331                .map(EcdsaP256VerifyingKey::from)
332                .map_err(|_err| X509VerificationError::VerifyingKeyFromSpki)?;
333
334            verifier
335                .verify(data, &signature)
336                .map_err(|_err| X509VerificationError::SignatureVerificationFailed)?;
337        }
338        (rfc5912::ECDSA_WITH_SHA_256, rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_384_R_1)) => {
339            let signature = EcdsaP384DerSignature::try_from(signature)
340                .map_err(|_err| X509VerificationError::DerSignatureInvalid)?;
341
342            let verifier = EcdsaP384PublicKey::try_from(subject_public_key_info)
343                .map(EcdsaP384VerifyingKey::from)
344                .map_err(|_err| X509VerificationError::VerifyingKeyFromSpki)?;
345
346            // let
347            let mut hasher = Sha256::new();
348            hasher.update(data);
349            let out: Sha256Output = hasher.finalize();
350
351            verifier
352                .verify_prehash(out.as_slice(), &signature)
353                .map_err(|_err| X509VerificationError::SignatureVerificationFailed)?;
354        }
355        (rfc5912::ECDSA_WITH_SHA_384, rfc5912::ID_EC_PUBLIC_KEY, Some(rfc5912::SECP_384_R_1)) => {
356            let signature = EcdsaP384DerSignature::try_from(signature)
357                .map_err(|_err| X509VerificationError::DerSignatureInvalid)?;
358
359            let verifier = EcdsaP384PublicKey::try_from(subject_public_key_info)
360                .map(EcdsaP384VerifyingKey::from)
361                .map_err(|_err| X509VerificationError::VerifyingKeyFromSpki)?;
362
363            verifier
364                .verify(data, &signature)
365                .map_err(|_err| X509VerificationError::SignatureVerificationFailed)?;
366        }
367        (rfc5912::SHA_256_WITH_RSA_ENCRYPTION, rfc5912::RSA_ENCRYPTION, None) => {
368            let signature = RS256Signature::try_from(signature)
369                .map_err(|_err| X509VerificationError::DerSignatureInvalid)?;
370
371            let verifier = RS256PublicKey::try_from(subject_public_key_info)
372                .map(RS256VerifyingKey::new)
373                .map_err(|_err| X509VerificationError::VerifyingKeyFromSpki)?;
374
375            verifier
376                .verify(data, &signature)
377                .map_err(|_err| X509VerificationError::SignatureVerificationFailed)?;
378        }
379        (rfc5912::SHA_384_WITH_RSA_ENCRYPTION, rfc5912::RSA_ENCRYPTION, None) => {
380            let signature = RS256Signature::try_from(signature)
381                .map_err(|_err| X509VerificationError::DerSignatureInvalid)?;
382
383            let verifier = RS256PublicKey::try_from(subject_public_key_info)
384                .map(RS384VerifyingKey::new)
385                .map_err(|_err| X509VerificationError::VerifyingKeyFromSpki)?;
386
387            verifier
388                .verify(data, &signature)
389                .map_err(|_err| X509VerificationError::SignatureVerificationFailed)?;
390        }
391        (signature_algorithm_oid, spki_alg_oid, spki_alg_params) => {
392            error!(?signature_algorithm_oid, ?spki_alg_oid, ?spki_alg_params);
393            return Err(X509VerificationError::SignatureAlgorithmNotImplemented);
394        }
395    }
396
397    Ok(())
398}