x509_parser/validate/
structure.rs

1use super::{Logger, Validator, X509NameStructureValidator};
2use crate::certificate::*;
3use crate::extensions::{GeneralName, ParsedExtension};
4use crate::public_key::PublicKey;
5use crate::x509::{SubjectPublicKeyInfo, X509Version};
6
7/// Default X.509 structure validator for `X509Certificate`
8///
9/// This [`Validator`] iterates the X.509 Certificate fields, and verifies the
10/// DER encoding and structure:
11/// - numbers with wrong encoding/sign (for ex. serial number)
12/// - strings with characters not allowed in DER type (for ex. '*' in `PrintableString`)
13///
14/// # Examples
15///
16/// Validate structure, collect warnings and errors to a `Vec`:
17///
18/// ```
19/// use x509_parser::certificate::X509Certificate;
20/// use x509_parser::validate::*;
21///
22/// # #[allow(deprecated)]
23/// #[cfg(feature = "validate")]
24/// fn validate_certificate(x509: &X509Certificate<'_>) -> Result<(), &'static str> {
25///     let mut logger = VecLogger::default();
26///     println!("  Subject: {}", x509.subject());
27///     // validate and print warnings and errors to stderr
28///     let ok = X509StructureValidator.validate(&x509, &mut logger);
29///     print!("Structure validation status: ");
30///     if ok {
31///         println!("Ok");
32///     } else {
33///         println!("FAIL");
34///     }
35///     for warning in logger.warnings() {
36///         eprintln!("  [W] {}", warning);
37///     }
38///     for error in logger.errors() {
39///         eprintln!("  [E] {}", error);
40///     }
41///     println!();
42///     if !logger.errors().is_empty() {
43///         return Err("validation failed");
44///     }
45///     Ok(())
46/// }
47/// ```
48#[derive(Debug, Default)]
49pub struct X509StructureValidator;
50
51impl<'a> Validator<'a> for X509StructureValidator {
52    type Item = X509Certificate<'a>;
53
54    fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
55        let mut res = true;
56        res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l);
57        res
58    }
59}
60
61/// Default X.509 structure validator for `TbsCertificate`
62#[derive(Debug, Default)]
63pub struct TbsCertificateStructureValidator;
64
65impl<'a> Validator<'a> for TbsCertificateStructureValidator {
66    type Item = TbsCertificate<'a>;
67
68    fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
69        let mut res = true;
70        // version must be 0, 1 or 2
71        if item.version.0 >= 3 {
72            l.err("Invalid version");
73            res = false;
74        }
75        let b = item.raw_serial();
76        if b.is_empty() {
77            l.err("Serial is empty");
78            res = false;
79        } else {
80            // check MSB of serial
81            if b[0] & 0x80 != 0 {
82                l.warn("Serial number is negative");
83            }
84            // check leading zeroes in serial
85            if b.len() > 1 && b[0] == 0 && b[1] & 0x80 == 0 {
86                l.warn("Leading zeroes in serial number");
87            }
88        }
89        // subject/issuer: verify charsets
90        res &= X509NameStructureValidator.validate(&item.subject, l);
91        res &= X509NameStructureValidator.validate(&item.issuer, l);
92        // subject public key
93        res &= X509PublicKeyValidator.validate(&item.subject_pki, l);
94        // validity: dates <= 2049 must use UTCTime, >= 2050 must use GeneralizedTime
95        let validity = item.validity();
96        let year_notbefore = validity.not_before.to_datetime().year();
97        if year_notbefore <= 2049 {
98            if !validity.not_before.is_utctime() {
99                l.warn("year <= 2049 should use UTCTime (notBefore)");
100            }
101        } else if !validity.not_before.is_generalizedtime() {
102            l.warn("year >= 2050 should use GeneralizedTime (notBefore)");
103        }
104        let year_notafter = validity.not_after.to_datetime().year();
105        if year_notafter <= 2049 {
106            if !validity.not_after.is_utctime() {
107                l.warn("year <= 2049 should use UTCTime (notAfter)");
108            }
109        } else if !validity.not_after.is_generalizedtime() {
110            l.warn("year >= 2050 should use GeneralizedTime (notAfter)");
111        }
112        if item.version == X509Version::V1 {
113            // unique identifiers: version must 2 or 3
114            if item.issuer_uid.is_some() {
115                l.warn("issuerUniqueID present but version 1");
116            }
117            if item.subject_uid.is_some() {
118                l.warn("subjectUniqueID present but version 1");
119            }
120        }
121        // extensions require v3
122        if !item.extensions().is_empty() && item.version != X509Version::V3 {
123            l.err("Extensions present but version is not 3");
124            res = false;
125        }
126        // check for parse errors or unsupported extensions
127        for ext in item.extensions() {
128            if let ParsedExtension::UnsupportedExtension { .. } = &ext.parsed_extension {
129                l.warn(&format!("Unsupported extension {}", ext.oid));
130            }
131            if let ParsedExtension::ParseError { error } = &ext.parsed_extension {
132                l.err(&format!("Parse error in extension {}: {}", ext.oid, error));
133                res = false;
134            }
135        }
136        // check extensions
137        for ext in item.extensions() {
138            // specific extension checks
139            // SAN
140            if let ParsedExtension::SubjectAlternativeName(san) = ext.parsed_extension() {
141                for name in &san.general_names {
142                    match name {
143                        GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => {
144                            // should be an ia5string
145                            if !s.as_bytes().iter().all(u8::is_ascii) {
146                                l.warn(&format!("Invalid charset in 'SAN' entry '{}'", s));
147                            }
148                        }
149                        _ => (),
150                    }
151                }
152            }
153        }
154        res
155    }
156}
157
158#[derive(Debug, Default)]
159pub struct X509PublicKeyValidator;
160
161impl<'a> Validator<'a> for X509PublicKeyValidator {
162    type Item = SubjectPublicKeyInfo<'a>;
163
164    fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
165        let mut res = true;
166        // res &= TbsCertificateStructureValidator.validate(&item.tbs_certificate, l);
167        match item.parsed() {
168            Ok(PublicKey::RSA(rsa)) => {
169                if rsa.modulus[0] & 0x80 != 0 {
170                    l.warn("Public key: (RSA) modulus is negative");
171                }
172                if rsa.exponent[0] & 0x80 != 0 {
173                    l.warn("Public key: (RSA) exponent is negative");
174                }
175            }
176            Ok(PublicKey::Unknown(_b)) => {
177                l.warn("Unknown public key type");
178            }
179            Ok(_) => {}
180            Err(_) => {
181                l.err("Invalid public key");
182                res = false;
183            }
184        }
185        res
186    }
187}