x509_parser/visitor/
certificate_visitor.rs

1use asn1_rs::BitString;
2use oid_registry::*;
3
4use crate::certificate::*;
5use crate::extensions::*;
6use crate::x509::*;
7
8/// Visitor pattern for [`X509Certificate`]
9///
10/// # Extensions
11///
12/// Visitor methods are provided for extensions, both in a generic way (receiving a [`X509Extension`]
13/// object) and in a specific way for standard extensions (for ex, `visit_extension_aki` receives a
14/// [`AuthorityKeyIdentifier`]).
15///
16/// For a specific method to be called, the extension OID must be correct and the extension must be
17/// successfully parsed as the specific type.
18///
19/// A specific method can be called multiple times, if the extension is present multiple times.
20///
21/// Extension parsing methods are redundant. This is not a problem because default methods do nothing,
22/// but if a trait implementation provides several `visit_extension...` methods it must be aware
23/// that it will visit the same extension multiple times.
24///
25/// # Example
26///
27/// ```rust
28/// use x509_parser::prelude::*;
29/// use x509_parser::visitor::X509CertificateVisitor;
30/// #[derive(Debug, Default)]
31/// struct SubjectIssuerVisitor {
32///     issuer: String,
33///     subject: String,
34///     is_ca: bool,
35/// }
36///
37/// impl X509CertificateVisitor for SubjectIssuerVisitor {
38///     fn visit_issuer(&mut self, name: &X509Name<'_>) {
39///         self.issuer = name.to_string();
40///     }
41///
42///     fn visit_subject(&mut self, name: &X509Name<'_>) {
43///         self.subject = name.to_string();
44///     }
45///
46///     fn visit_extension_basic_constraints(&mut self, bc: &BasicConstraints) {
47///         self.is_ca = bc.ca;
48///     }
49/// }
50/// ```
51pub trait X509CertificateVisitor {
52    /// Run the provided visitor (`self`) over the [`X509Certificate`] object
53    fn walk(&mut self, x509: &X509Certificate)
54    where
55        Self: Sized,
56    {
57        x509.walk(self);
58    }
59
60    /// Invoked for the "TBSCertificate" field of the X.509 Certificate, before visiting children
61    fn visit_tbs_certificate(&mut self, _tbs: &TbsCertificate) {}
62
63    /// Invoked for the "signatureAlgorithm" field of the X.509 Certificate
64    ///
65    /// Note: this is the "signatureAlgorithm" in the "Certificate" sequence. According to the
66    /// specifications, it should be equal to "signature" field from the "TBSCertificate" sequence.
67    fn visit_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
68
69    /// Invoked for the "signatureValue" field of the TBSCertificate
70    fn visit_signature_value(&mut self, _signature: &BitString) {}
71
72    /// Invoked for the "version" field of the TBSCertificate
73    fn visit_version(&mut self, _version: &X509Version) {}
74
75    /// Invoked for the "serialNumber" field of the TBSCertificate
76    fn visit_serial_number(&mut self, _serial: &[u8]) {}
77
78    /// Invoked for the "signature" field of the TBSCertificate
79    ///
80    /// Note: this is the "signature" field from the "TBSCertificate" sequence. According to the
81    /// specifications, it should be equal to "signatureAlgorithm" in the "Certificate" sequence.
82    fn visit_tbs_signature_algorithm(&mut self, _algorithm: &AlgorithmIdentifier) {}
83
84    /// Invoked for the "issuer" field of the TBSCertificate
85    fn visit_issuer(&mut self, _name: &X509Name) {}
86
87    /// Invoked for the "validity" field of the TBSCertificate
88    fn visit_validity(&mut self, _validity: &Validity) {}
89
90    /// Invoked for the "subject" field of the TBSCertificate
91    fn visit_subject(&mut self, _name: &X509Name) {}
92
93    /// Invoked for the "subjectPublicKeyInfo" field of the TBSCertificate
94    fn visit_subject_public_key_info(&mut self, _subject_pki: &SubjectPublicKeyInfo) {}
95
96    /// Invoked for the "issuerUniqueID" field of the TBSCertificate
97    fn visit_issuer_unique_id(&mut self, _id: Option<&UniqueIdentifier>) {}
98
99    /// Invoked for the "subjectUniqueID" field of the TBSCertificate
100    fn visit_subject_unique_id(&mut self, _id: Option<&UniqueIdentifier>) {}
101
102    /// Invoked for extensions, before visiting children
103    fn pre_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
104
105    /// Invoked for any extension that appear in the X.509 Certificate
106    ///
107    /// Note: this method may be redundant with any other extension visitor method
108    fn visit_extension(&mut self, _extension: &X509Extension) {}
109
110    /// Invoked for extensions, after visiting children
111    fn post_visit_extensions(&mut self, _extensions: &[X509Extension]) {}
112
113    /// Invoked for the "Authority Key Identifier" extension (if present)
114    fn visit_extension_aki(&mut self, _aki: &AuthorityKeyIdentifier) {}
115
116    /// Invoked for the "Subject Key Identifier" extension (if present)
117    fn visit_extension_ski(&mut self, _id: &KeyIdentifier) {}
118
119    /// Invoked for the "Key Usage" extension (if present)
120    fn visit_extension_key_usage(&mut self, _usage: &KeyUsage) {}
121
122    /// Invoked for the "Certificate Policies" extension (if present)
123    fn visit_extension_certificate_policies(&mut self, _policies: &CertificatePolicies) {}
124
125    /// Invoked for the "Subject Alternative Name" extension (if present)
126    fn visit_extension_subject_alternative_name(&mut self, _san: &SubjectAlternativeName) {}
127
128    /// Invoked for the "Issuer Alternative Name" extension (if present)
129    fn visit_extension_issuer_alternative_name(&mut self, _ian: &IssuerAlternativeName) {}
130
131    /// Invoked for the "Basic Constraints" extension (if present)
132    fn visit_extension_basic_constraints(&mut self, _bc: &BasicConstraints) {}
133
134    /// Invoked for the "Name Constraints" extension (if present)
135    fn visit_extension_name_constraints(&mut self, _constraints: &NameConstraints) {}
136
137    /// Invoked for the "Name Constraints" extension (if present)
138    fn visit_extension_nscert_comment(&mut self, _nscert_comment: &str) {}
139
140    /// Invoked for the "Name Constraints" extension (if present)
141    fn visit_extension_nscert_type(&mut self, _nscert_type: &NSCertType) {}
142
143    /// Invoked for the "Policy Constraints" extension (if present)
144    fn visit_extension_policy_constraints(&mut self, _constraints: &PolicyConstraints) {}
145
146    /// Invoked for the "Policy Mappings" extension (if present)
147    fn visit_extension_policy_mappings(&mut self, _mappings: &PolicyMappings) {}
148
149    /// Invoked for the "Extended Key Usage" extension (if present)
150    fn visit_extension_extended_key_usage(&mut self, _usage: &ExtendedKeyUsage) {}
151
152    /// Invoked for the "CRL Distribution Points" extension (if present)
153    fn visit_extension_crl_distribution_points(&mut self, _crl: &CRLDistributionPoints) {}
154
155    /// Invoked for the "Inhibit anyPolicy" extension (if present)
156    fn visit_extension_inhibit_anypolicy(&mut self, _policy: &InhibitAnyPolicy) {}
157
158    /// Invoked for the "Authority Information Access" extension (if present)
159    fn visit_extension_authority_information_access(&mut self, _info: &AuthorityInfoAccess) {}
160
161    /// Invoked for the "Signed Certificate Timestamp" (SCT) extension (if present)
162    fn visit_extension_sct(&mut self, _sct: &[SignedCertificateTimestamp]) {}
163
164    /// Invoked for any other extension than the specific (recognized) types
165    ///
166    /// This can happen for several reasons:
167    /// - the parser did not recognize the extension content
168    /// - the parser was explicitly asked to not parse extension content
169    /// - the extension could be correct (for ex in a CRL), but is not supposed to be part of a Certificate
170    fn visit_extension_unknown(&mut self, _ext: &X509Extension) {}
171
172    /// Invoked for any extension than caused a parse error
173    ///
174    /// Normally, this should not match anything except for invalid data.
175    /// This could match any known extension malformed or wrongly encoded.
176    fn visit_extension_parse_error(
177        &mut self,
178        _extension: &X509Extension,
179        _error: &asn1_rs::Err<asn1_rs::Error>,
180    ) {
181    }
182}
183
184impl X509Certificate<'_> {
185    /// Run the provided [`X509CertificateVisitor`] over the X.509 Certificate (`self`)
186    pub fn walk<V: X509CertificateVisitor>(&self, visitor: &mut V) {
187        visitor.visit_tbs_certificate(&self.tbs_certificate);
188        self.tbs_certificate.walk(visitor);
189        visitor.visit_signature_algorithm(&self.signature_algorithm);
190        visitor.visit_signature_value(&self.signature_value);
191    }
192}
193
194impl TbsCertificate<'_> {
195    /// Run the provided `visitor` over the [`TbsCertificate`] object
196    pub fn walk<V: X509CertificateVisitor>(&self, visitor: &mut V) {
197        // shorten name to reduce line length
198        let v = visitor;
199        v.visit_version(&self.version);
200        v.visit_serial_number(self.raw_serial());
201        v.visit_tbs_signature_algorithm(&self.signature);
202        v.visit_issuer(&self.issuer);
203        v.visit_validity(&self.validity);
204        v.visit_subject(&self.subject);
205        v.visit_subject_public_key_info(&self.subject_pki);
206        v.visit_issuer_unique_id(self.issuer_uid.as_ref());
207        v.visit_subject_unique_id(self.subject_uid.as_ref());
208        v.pre_visit_extensions(self.extensions());
209        for extension in self.extensions() {
210            v.visit_extension(extension);
211
212            match extension.parsed_extension() {
213                ParsedExtension::AuthorityInfoAccess(info) => {
214                    v.visit_extension_authority_information_access(info)
215                }
216                ParsedExtension::AuthorityKeyIdentifier(aki) => v.visit_extension_aki(aki),
217                ParsedExtension::BasicConstraints(bc) => v.visit_extension_basic_constraints(bc),
218                ParsedExtension::CertificatePolicies(policies) => {
219                    v.visit_extension_certificate_policies(policies)
220                }
221                ParsedExtension::CRLDistributionPoints(crl) => {
222                    v.visit_extension_crl_distribution_points(crl)
223                }
224                ParsedExtension::ExtendedKeyUsage(usage) => {
225                    v.visit_extension_extended_key_usage(usage)
226                }
227                ParsedExtension::InhibitAnyPolicy(policy) => {
228                    v.visit_extension_inhibit_anypolicy(policy)
229                }
230                ParsedExtension::IssuerAlternativeName(ian) => {
231                    v.visit_extension_issuer_alternative_name(ian)
232                }
233                ParsedExtension::KeyUsage(usage) => v.visit_extension_key_usage(usage),
234                ParsedExtension::NSCertType(nscert_type) => {
235                    v.visit_extension_nscert_type(nscert_type)
236                }
237                ParsedExtension::NameConstraints(constraints) => {
238                    v.visit_extension_name_constraints(constraints)
239                }
240                ParsedExtension::NsCertComment(comment) => {
241                    v.visit_extension_nscert_comment(comment)
242                }
243                ParsedExtension::PolicyConstraints(constraints) => {
244                    v.visit_extension_policy_constraints(constraints)
245                }
246                ParsedExtension::PolicyMappings(mappings) => {
247                    v.visit_extension_policy_mappings(mappings)
248                }
249                ParsedExtension::SCT(sct) => v.visit_extension_sct(sct),
250                ParsedExtension::SubjectAlternativeName(san) => {
251                    v.visit_extension_subject_alternative_name(san)
252                }
253                ParsedExtension::SubjectKeyIdentifier(id) => v.visit_extension_ski(id),
254                ParsedExtension::ParseError { error } => {
255                    v.visit_extension_parse_error(extension, error)
256                }
257                _ => v.visit_extension_unknown(extension),
258            }
259        }
260        v.post_visit_extensions(self.extensions());
261    }
262}
263
264#[cfg(test)]
265mod tests {
266    use super::*;
267    use crate::FromDer;
268
269    static IGCA_DER: &[u8] = include_bytes!("../../assets/IGC_A.der");
270
271    #[test]
272    fn visitor_certificate() {
273        #[derive(Debug, Default)]
274        struct SubjectIssuerVisitor {
275            issuer: String,
276            subject: String,
277            is_ca: bool,
278        }
279
280        impl X509CertificateVisitor for SubjectIssuerVisitor {
281            fn visit_issuer(&mut self, name: &X509Name) {
282                self.issuer = name.to_string();
283            }
284
285            fn visit_subject(&mut self, name: &X509Name) {
286                self.subject = name.to_string();
287            }
288
289            fn visit_extension_basic_constraints(&mut self, bc: &BasicConstraints) {
290                self.is_ca = bc.ca;
291            }
292        }
293
294        let mut visitor = SubjectIssuerVisitor::default();
295        let (_, x509) = X509Certificate::from_der(IGCA_DER).unwrap();
296
297        x509.walk(&mut visitor);
298        assert!(!visitor.issuer.is_empty());
299        assert!(visitor.is_ca);
300        assert_eq!(&visitor.issuer, &visitor.subject);
301    }
302}