/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x509-parser-0.17.0/src/extensions/mod.rs
Line | Count | Source |
1 | | //! X.509 Extensions objects and types |
2 | | |
3 | | use crate::error::{X509Error, X509Result}; |
4 | | use crate::time::ASN1Time; |
5 | | use crate::utils::format_serial; |
6 | | use crate::x509::{ReasonCode, RelativeDistinguishedName}; |
7 | | |
8 | | use asn1_rs::FromDer; |
9 | | use der_parser::ber::parse_ber_bool; |
10 | | use der_parser::der::*; |
11 | | use der_parser::error::{BerError, BerResult}; |
12 | | use der_parser::num_bigint::BigUint; |
13 | | use nom::combinator::{all_consuming, complete, cut, map, map_res, opt}; |
14 | | use nom::multi::{many0, many1}; |
15 | | use nom::{Err, IResult, Parser}; |
16 | | use oid_registry::*; |
17 | | use std::collections::HashMap; |
18 | | use std::fmt::{self, LowerHex}; |
19 | | |
20 | | mod generalname; |
21 | | mod keyusage; |
22 | | mod nameconstraints; |
23 | | mod policymappings; |
24 | | mod sct; |
25 | | |
26 | | pub use generalname::*; |
27 | | pub use keyusage::*; |
28 | | pub use nameconstraints::*; |
29 | | pub use policymappings::*; |
30 | | pub use sct::*; |
31 | | |
32 | | /// X.509 version 3 extension |
33 | | /// |
34 | | /// X.509 extensions allow adding attributes to objects like certificates or revocation lists. |
35 | | /// |
36 | | /// Each extension in a certificate is designated as either critical or non-critical. A |
37 | | /// certificate using system MUST reject the certificate if it encounters a critical extension it |
38 | | /// does not recognize; however, a non-critical extension MAY be ignored if it is not recognized. |
39 | | /// |
40 | | /// Each extension includes an OID and an ASN.1 structure. When an extension appears in a |
41 | | /// certificate, the OID appears as the field extnID and the corresponding ASN.1 encoded structure |
42 | | /// is the value of the octet string extnValue. A certificate MUST NOT include more than one |
43 | | /// instance of a particular extension. |
44 | | /// |
45 | | /// When parsing an extension, the global extension structure (described above) is parsed, |
46 | | /// and the object is returned if it succeeds. |
47 | | /// During this step, it also attempts to parse the content of the extension, if known. |
48 | | /// The returned object has a |
49 | | /// [`X509Extension::parsed_extension()`] method. The returned |
50 | | /// enum is either a known extension, or the special value `ParsedExtension::UnsupportedExtension`. |
51 | | /// |
52 | | /// # Example |
53 | | /// |
54 | | /// ```rust |
55 | | /// use x509_parser::prelude::FromDer; |
56 | | /// use x509_parser::extensions::{X509Extension, ParsedExtension}; |
57 | | /// |
58 | | /// static DER: &[u8] = &[ |
59 | | /// 0x30, 0x1D, 0x06, 0x03, 0x55, 0x1D, 0x0E, 0x04, 0x16, 0x04, 0x14, 0xA3, 0x05, 0x2F, 0x18, |
60 | | /// 0x60, 0x50, 0xC2, 0x89, 0x0A, 0xDD, 0x2B, 0x21, 0x4F, 0xFF, 0x8E, 0x4E, 0xA8, 0x30, 0x31, |
61 | | /// 0x36 ]; |
62 | | /// |
63 | | /// # fn main() { |
64 | | /// let res = X509Extension::from_der(DER); |
65 | | /// match res { |
66 | | /// Ok((_rem, ext)) => { |
67 | | /// println!("Extension OID: {}", ext.oid); |
68 | | /// println!(" Critical: {}", ext.critical); |
69 | | /// let parsed_ext = ext.parsed_extension(); |
70 | | /// assert!(!parsed_ext.unsupported()); |
71 | | /// assert!(parsed_ext.error().is_none()); |
72 | | /// if let ParsedExtension::SubjectKeyIdentifier(key_id) = parsed_ext { |
73 | | /// assert!(key_id.0.len() > 0); |
74 | | /// } else { |
75 | | /// panic!("Extension has wrong type"); |
76 | | /// } |
77 | | /// }, |
78 | | /// _ => panic!("x509 extension parsing failed: {:?}", res), |
79 | | /// } |
80 | | /// # } |
81 | | /// ``` |
82 | | #[derive(Clone, Debug, PartialEq)] |
83 | | pub struct X509Extension<'a> { |
84 | | /// OID describing the extension content |
85 | | pub oid: Oid<'a>, |
86 | | /// Boolean value describing the 'critical' attribute of the extension |
87 | | /// |
88 | | /// An extension includes the boolean critical, with a default value of FALSE. |
89 | | pub critical: bool, |
90 | | /// Raw content of the extension |
91 | | pub value: &'a [u8], |
92 | | pub(crate) parsed_extension: ParsedExtension<'a>, |
93 | | } |
94 | | |
95 | | impl<'a> X509Extension<'a> { |
96 | | /// Creates a new extension with the provided values. |
97 | | #[inline] |
98 | 0 | pub const fn new( |
99 | 0 | oid: Oid<'a>, |
100 | 0 | critical: bool, |
101 | 0 | value: &'a [u8], |
102 | 0 | parsed_extension: ParsedExtension<'a>, |
103 | 0 | ) -> X509Extension<'a> { |
104 | 0 | X509Extension { |
105 | 0 | oid, |
106 | 0 | critical, |
107 | 0 | value, |
108 | 0 | parsed_extension, |
109 | 0 | } |
110 | 0 | } |
111 | | |
112 | | /// Return the extension type or `UnsupportedExtension` if the extension is not implemented. |
113 | | #[inline] |
114 | 0 | pub fn parsed_extension(&self) -> &ParsedExtension<'a> { |
115 | 0 | &self.parsed_extension |
116 | 0 | } |
117 | | } |
118 | | |
119 | | /// <pre> |
120 | | /// Extension ::= SEQUENCE { |
121 | | /// extnID OBJECT IDENTIFIER, |
122 | | /// critical BOOLEAN DEFAULT FALSE, |
123 | | /// extnValue OCTET STRING } |
124 | | /// </pre> |
125 | | impl<'a> FromDer<'a, X509Error> for X509Extension<'a> { |
126 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
127 | 0 | X509ExtensionParser::new().parse(i) |
128 | 0 | } |
129 | | } |
130 | | |
131 | | /// `X509Extension` parser builder |
132 | | #[derive(Clone, Copy, Debug)] |
133 | | pub struct X509ExtensionParser { |
134 | | deep_parse_extensions: bool, |
135 | | } |
136 | | |
137 | | impl X509ExtensionParser { |
138 | | #[inline] |
139 | 0 | pub const fn new() -> Self { |
140 | 0 | X509ExtensionParser { |
141 | 0 | deep_parse_extensions: true, |
142 | 0 | } |
143 | 0 | } |
144 | | |
145 | | #[inline] |
146 | 0 | pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self { |
147 | 0 | X509ExtensionParser { |
148 | 0 | deep_parse_extensions, |
149 | 0 | } |
150 | 0 | } |
151 | | } |
152 | | |
153 | | impl Default for X509ExtensionParser { |
154 | 0 | fn default() -> Self { |
155 | 0 | X509ExtensionParser::new() |
156 | 0 | } |
157 | | } |
158 | | |
159 | | impl<'a> Parser<&'a [u8], X509Extension<'a>, X509Error> for X509ExtensionParser { |
160 | 0 | fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Extension<'a>, X509Error> { |
161 | 0 | parse_der_sequence_defined_g(|i, _| { |
162 | 0 | let (i, oid) = Oid::from_der(i)?; |
163 | 0 | let (i, critical) = der_read_critical(i)?; |
164 | 0 | let (i, value) = <&[u8]>::from_der(i)?; |
165 | 0 | let (i, parsed_extension) = if self.deep_parse_extensions { |
166 | 0 | parser::parse_extension(i, value, &oid)? |
167 | | } else { |
168 | 0 | (&[] as &[_], ParsedExtension::Unparsed) |
169 | | }; |
170 | 0 | let ext = X509Extension { |
171 | 0 | oid, |
172 | 0 | critical, |
173 | 0 | value, |
174 | 0 | parsed_extension, |
175 | 0 | }; |
176 | 0 | Ok((i, ext)) |
177 | 0 | })(input) |
178 | 0 | .map_err(|_| X509Error::InvalidExtensions.into()) |
179 | 0 | } |
180 | | } |
181 | | |
182 | | #[derive(Clone, Debug, PartialEq)] |
183 | | pub enum ParsedExtension<'a> { |
184 | | /// Crate parser does not support this extension (yet) |
185 | | UnsupportedExtension { |
186 | | oid: Oid<'a>, |
187 | | }, |
188 | | ParseError { |
189 | | error: Err<BerError>, |
190 | | }, |
191 | | /// Section 4.2.1.1 of rfc 5280 |
192 | | AuthorityKeyIdentifier(AuthorityKeyIdentifier<'a>), |
193 | | /// Section 4.2.1.2 of rfc 5280 |
194 | | SubjectKeyIdentifier(KeyIdentifier<'a>), |
195 | | /// Section 4.2.1.3 of rfc 5280 |
196 | | KeyUsage(KeyUsage), |
197 | | /// Section 4.2.1.4 of rfc 5280 |
198 | | CertificatePolicies(CertificatePolicies<'a>), |
199 | | /// Section 4.2.1.5 of rfc 5280 |
200 | | PolicyMappings(PolicyMappings<'a>), |
201 | | /// Section 4.2.1.6 of rfc 5280 |
202 | | SubjectAlternativeName(SubjectAlternativeName<'a>), |
203 | | /// Section 4.2.1.7 of rfc 5280 |
204 | | IssuerAlternativeName(IssuerAlternativeName<'a>), |
205 | | /// Section 4.2.1.9 of rfc 5280 |
206 | | BasicConstraints(BasicConstraints), |
207 | | /// Section 4.2.1.10 of rfc 5280 |
208 | | NameConstraints(NameConstraints<'a>), |
209 | | /// Section 4.2.1.11 of rfc 5280 |
210 | | PolicyConstraints(PolicyConstraints), |
211 | | /// Section 4.2.1.12 of rfc 5280 |
212 | | ExtendedKeyUsage(ExtendedKeyUsage<'a>), |
213 | | /// Section 4.2.1.13 of rfc 5280 |
214 | | CRLDistributionPoints(CRLDistributionPoints<'a>), |
215 | | /// Section 4.2.1.14 of rfc 5280 |
216 | | InhibitAnyPolicy(InhibitAnyPolicy), |
217 | | /// Section 4.2.2.1 of rfc 5280 |
218 | | AuthorityInfoAccess(AuthorityInfoAccess<'a>), |
219 | | /// Netscape certificate type (subject is SSL client, an SSL server, or a CA) |
220 | | NSCertType(NSCertType), |
221 | | /// Netscape certificate comment |
222 | | NsCertComment(&'a str), |
223 | | /// Section 5.2.5 of rfc 5280 |
224 | | IssuingDistributionPoint(IssuingDistributionPoint<'a>), |
225 | | /// Section 5.3.1 of rfc 5280 |
226 | | CRLNumber(BigUint), |
227 | | /// Section 5.3.1 of rfc 5280 |
228 | | ReasonCode(ReasonCode), |
229 | | /// Section 5.3.3 of rfc 5280 |
230 | | InvalidityDate(ASN1Time), |
231 | | /// rfc 6962 |
232 | | SCT(Vec<SignedCertificateTimestamp<'a>>), |
233 | | /// Unparsed extension (was not requested in parsing options) |
234 | | Unparsed, |
235 | | } |
236 | | |
237 | | impl ParsedExtension<'_> { |
238 | | /// Return `true` if the extension is unsupported |
239 | 0 | pub fn unsupported(&self) -> bool { |
240 | 0 | matches!(self, &ParsedExtension::UnsupportedExtension { .. }) |
241 | 0 | } |
242 | | |
243 | | /// Return a reference on the parsing error if the extension parsing failed |
244 | 0 | pub fn error(&self) -> Option<&Err<BerError>> { |
245 | 0 | match self { |
246 | 0 | ParsedExtension::ParseError { error } => Some(error), |
247 | 0 | _ => None, |
248 | | } |
249 | 0 | } |
250 | | } |
251 | | |
252 | | #[derive(Clone, Debug, PartialEq)] |
253 | | pub struct AuthorityKeyIdentifier<'a> { |
254 | | pub key_identifier: Option<KeyIdentifier<'a>>, |
255 | | pub authority_cert_issuer: Option<Vec<GeneralName<'a>>>, |
256 | | pub authority_cert_serial: Option<&'a [u8]>, |
257 | | } |
258 | | |
259 | | impl<'a> FromDer<'a, X509Error> for AuthorityKeyIdentifier<'a> { |
260 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
261 | 0 | parser::parse_authoritykeyidentifier(i).map_err(Err::convert) |
262 | 0 | } |
263 | | } |
264 | | |
265 | | pub type CertificatePolicies<'a> = Vec<PolicyInformation<'a>>; |
266 | | |
267 | | // impl<'a> FromDer<'a> for CertificatePolicies<'a> { |
268 | | // fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
269 | | // parser::parse_certificatepolicies(i).map_err(Err::convert) |
270 | | // } |
271 | | // } |
272 | | |
273 | | #[derive(Clone, Debug, PartialEq, Eq)] |
274 | | pub struct PolicyInformation<'a> { |
275 | | pub policy_id: Oid<'a>, |
276 | | pub policy_qualifiers: Option<Vec<PolicyQualifierInfo<'a>>>, |
277 | | } |
278 | | |
279 | | #[derive(Clone, Debug, PartialEq, Eq)] |
280 | | pub struct PolicyQualifierInfo<'a> { |
281 | | pub policy_qualifier_id: Oid<'a>, |
282 | | pub qualifier: &'a [u8], |
283 | | } |
284 | | |
285 | | /// Identifies whether the subject of the certificate is a CA, and the max validation depth. |
286 | | #[derive(Clone, Debug, PartialEq, Eq)] |
287 | | pub struct BasicConstraints { |
288 | | pub ca: bool, |
289 | | pub path_len_constraint: Option<u32>, |
290 | | } |
291 | | |
292 | | impl<'a> FromDer<'a, X509Error> for BasicConstraints { |
293 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
294 | 0 | parser::parse_basicconstraints(i).map_err(Err::convert) |
295 | 0 | } |
296 | | } |
297 | | |
298 | | #[derive(Clone, Debug, PartialEq, Eq)] |
299 | | pub struct KeyIdentifier<'a>(pub &'a [u8]); |
300 | | |
301 | | impl<'a> FromDer<'a, X509Error> for KeyIdentifier<'a> { |
302 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
303 | 0 | parser::parse_keyidentifier(i).map_err(Err::convert) |
304 | 0 | } |
305 | | } |
306 | | |
307 | | impl LowerHex for KeyIdentifier<'_> { |
308 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
309 | 0 | let s = format_serial(self.0); |
310 | 0 | f.write_str(&s) |
311 | 0 | } |
312 | | } |
313 | | |
314 | | #[derive(Clone, Copy, Debug, PartialEq, Eq)] |
315 | | pub struct NSCertType(u8); |
316 | | |
317 | | // The value is a bit-string, where the individual bit positions are defined as: |
318 | | // |
319 | | // bit-0 SSL client - this cert is certified for SSL client authentication use |
320 | | // bit-1 SSL server - this cert is certified for SSL server authentication use |
321 | | // bit-2 S/MIME - this cert is certified for use by clients (New in PR3) |
322 | | // bit-3 Object Signing - this cert is certified for signing objects such as Java applets and plugins(New in PR3) |
323 | | // bit-4 Reserved - this bit is reserved for future use |
324 | | // bit-5 SSL CA - this cert is certified for issuing certs for SSL use |
325 | | // bit-6 S/MIME CA - this cert is certified for issuing certs for S/MIME use (New in PR3) |
326 | | // bit-7 Object Signing CA - this cert is certified for issuing certs for Object Signing (New in PR3) |
327 | | impl NSCertType { |
328 | 0 | pub fn ssl_client(&self) -> bool { |
329 | 0 | self.0 & 0x1 == 1 |
330 | 0 | } |
331 | 0 | pub fn ssl_server(&self) -> bool { |
332 | 0 | (self.0 >> 1) & 1 == 1 |
333 | 0 | } |
334 | 0 | pub fn smime(&self) -> bool { |
335 | 0 | (self.0 >> 2) & 1 == 1 |
336 | 0 | } |
337 | 0 | pub fn object_signing(&self) -> bool { |
338 | 0 | (self.0 >> 3) & 1 == 1 |
339 | 0 | } |
340 | 0 | pub fn ssl_ca(&self) -> bool { |
341 | 0 | (self.0 >> 5) & 1 == 1 |
342 | 0 | } |
343 | 0 | pub fn smime_ca(&self) -> bool { |
344 | 0 | (self.0 >> 6) & 1 == 1 |
345 | 0 | } |
346 | 0 | pub fn object_signing_ca(&self) -> bool { |
347 | 0 | (self.0 >> 7) & 1 == 1 |
348 | 0 | } |
349 | | } |
350 | | |
351 | | const NS_CERT_TYPE_FLAGS: &[&str] = &[ |
352 | | "SSL CLient", |
353 | | "SSL Server", |
354 | | "S/MIME", |
355 | | "Object Signing", |
356 | | "Reserved", |
357 | | "SSL CA", |
358 | | "S/MIME CA", |
359 | | "Object Signing CA", |
360 | | ]; |
361 | | |
362 | | impl fmt::Display for NSCertType { |
363 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
364 | 0 | let mut s = String::new(); |
365 | 0 | let mut acc = self.0; |
366 | 0 | for flag_text in NS_CERT_TYPE_FLAGS { |
367 | 0 | if acc & 1 != 0 { |
368 | 0 | s = s + flag_text + ", "; |
369 | 0 | } |
370 | 0 | acc >>= 1; |
371 | | } |
372 | 0 | s.pop(); |
373 | 0 | s.pop(); |
374 | 0 | f.write_str(&s) |
375 | 0 | } |
376 | | } |
377 | | |
378 | | impl<'a> FromDer<'a, X509Error> for NSCertType { |
379 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
380 | 0 | parser::parse_nscerttype(i).map_err(Err::convert) |
381 | 0 | } |
382 | | } |
383 | | |
384 | | #[derive(Clone, Debug, PartialEq)] |
385 | | pub struct AuthorityInfoAccess<'a> { |
386 | | pub accessdescs: Vec<AccessDescription<'a>>, |
387 | | } |
388 | | |
389 | | impl<'a> AuthorityInfoAccess<'a> { |
390 | | /// Returns an iterator over the Access Descriptors |
391 | 0 | pub fn iter(&self) -> impl Iterator<Item = &AccessDescription<'a>> { |
392 | 0 | self.accessdescs.iter() |
393 | 0 | } |
394 | | |
395 | | /// Returns a `HashMap` mapping `Oid` to the list of references to `GeneralNames` |
396 | | /// |
397 | | /// If several names match the same `Oid`, they are merged in the same entry. |
398 | 0 | pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&GeneralName<'a>>> { |
399 | | // create the hashmap and merge entries with same OID |
400 | 0 | let mut m: HashMap<Oid, Vec<&GeneralName>> = HashMap::new(); |
401 | 0 | for desc in &self.accessdescs { |
402 | | let AccessDescription { |
403 | 0 | access_method: oid, |
404 | 0 | access_location: gn, |
405 | 0 | } = desc; |
406 | 0 | if let Some(general_names) = m.get_mut(oid) { |
407 | 0 | general_names.push(gn); |
408 | 0 | } else { |
409 | 0 | m.insert(oid.clone(), vec![gn]); |
410 | 0 | } |
411 | | } |
412 | 0 | m |
413 | 0 | } |
414 | | |
415 | | /// Returns a `HashMap` mapping `Oid` to the list of `GeneralNames` (consuming the input) |
416 | | /// |
417 | | /// If several names match the same `Oid`, they are merged in the same entry. |
418 | 0 | pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<GeneralName<'a>>> { |
419 | 0 | let mut aia_list = self.accessdescs; |
420 | | // create the hashmap and merge entries with same OID |
421 | 0 | let mut m: HashMap<Oid, Vec<GeneralName>> = HashMap::new(); |
422 | 0 | for desc in aia_list.drain(..) { |
423 | | let AccessDescription { |
424 | 0 | access_method: oid, |
425 | 0 | access_location: gn, |
426 | 0 | } = desc; |
427 | 0 | if let Some(general_names) = m.get_mut(&oid) { |
428 | 0 | general_names.push(gn); |
429 | 0 | } else { |
430 | 0 | m.insert(oid, vec![gn]); |
431 | 0 | } |
432 | | } |
433 | 0 | m |
434 | 0 | } |
435 | | } |
436 | | |
437 | | impl<'a> FromDer<'a, X509Error> for AuthorityInfoAccess<'a> { |
438 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
439 | 0 | parser::parse_authorityinfoaccess(i).map_err(Err::convert) |
440 | 0 | } |
441 | | } |
442 | | |
443 | | #[derive(Clone, Debug, PartialEq)] |
444 | | pub struct AccessDescription<'a> { |
445 | | pub access_method: Oid<'a>, |
446 | | pub access_location: GeneralName<'a>, |
447 | | } |
448 | | |
449 | | impl<'a> AccessDescription<'a> { |
450 | 0 | pub const fn new(access_method: Oid<'a>, access_location: GeneralName<'a>) -> Self { |
451 | 0 | AccessDescription { |
452 | 0 | access_method, |
453 | 0 | access_location, |
454 | 0 | } |
455 | 0 | } |
456 | | } |
457 | | |
458 | | #[derive(Clone, Debug, PartialEq, Eq)] |
459 | | pub struct InhibitAnyPolicy { |
460 | | pub skip_certs: u32, |
461 | | } |
462 | | |
463 | | impl<'a> FromDer<'a, X509Error> for InhibitAnyPolicy { |
464 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
465 | 0 | map(parse_der_u32, |skip_certs| InhibitAnyPolicy { skip_certs })(i).map_err(Err::convert) |
466 | 0 | } |
467 | | } |
468 | | |
469 | | #[derive(Clone, Debug, PartialEq, Eq)] |
470 | | pub struct PolicyConstraints { |
471 | | pub require_explicit_policy: Option<u32>, |
472 | | pub inhibit_policy_mapping: Option<u32>, |
473 | | } |
474 | | |
475 | | impl<'a> FromDer<'a, X509Error> for PolicyConstraints { |
476 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
477 | 0 | parser::parse_policyconstraints(i).map_err(Err::convert) |
478 | 0 | } |
479 | | } |
480 | | |
481 | | #[derive(Clone, Debug, PartialEq)] |
482 | | pub struct SubjectAlternativeName<'a> { |
483 | | pub general_names: Vec<GeneralName<'a>>, |
484 | | } |
485 | | |
486 | | impl<'a> FromDer<'a, X509Error> for SubjectAlternativeName<'a> { |
487 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
488 | 0 | parse_der_sequence_defined_g(|input, _| { |
489 | 0 | let (i, general_names) = |
490 | 0 | all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?; |
491 | 0 | Ok((i, SubjectAlternativeName { general_names })) |
492 | 0 | })(i) |
493 | 0 | } |
494 | | } |
495 | | |
496 | | #[derive(Clone, Debug, PartialEq)] |
497 | | pub struct IssuerAlternativeName<'a> { |
498 | | pub general_names: Vec<GeneralName<'a>>, |
499 | | } |
500 | | |
501 | | impl<'a> FromDer<'a, X509Error> for IssuerAlternativeName<'a> { |
502 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
503 | 0 | parse_der_sequence_defined_g(|input, _| { |
504 | 0 | let (i, general_names) = |
505 | 0 | all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?; |
506 | 0 | Ok((i, IssuerAlternativeName { general_names })) |
507 | 0 | })(i) |
508 | 0 | } |
509 | | } |
510 | | |
511 | | #[derive(Clone, Debug, PartialEq)] |
512 | | pub struct CRLDistributionPoints<'a> { |
513 | | pub points: Vec<CRLDistributionPoint<'a>>, |
514 | | } |
515 | | |
516 | | impl<'a> std::ops::Deref for CRLDistributionPoints<'a> { |
517 | | type Target = Vec<CRLDistributionPoint<'a>>; |
518 | | |
519 | 0 | fn deref(&self) -> &Self::Target { |
520 | 0 | &self.points |
521 | 0 | } |
522 | | } |
523 | | |
524 | | impl<'a> FromDer<'a, X509Error> for CRLDistributionPoints<'a> { |
525 | 0 | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
526 | 0 | parser::parse_crldistributionpoints(i).map_err(Err::convert) |
527 | 0 | } |
528 | | } |
529 | | |
530 | | #[derive(Clone, Debug, PartialEq)] |
531 | | pub struct CRLDistributionPoint<'a> { |
532 | | pub distribution_point: Option<DistributionPointName<'a>>, |
533 | | pub reasons: Option<ReasonFlags>, |
534 | | pub crl_issuer: Option<Vec<GeneralName<'a>>>, |
535 | | } |
536 | | |
537 | | #[derive(Clone, Debug, PartialEq)] |
538 | | pub enum DistributionPointName<'a> { |
539 | | FullName(Vec<GeneralName<'a>>), |
540 | | NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>), |
541 | | } |
542 | | |
543 | | #[derive(Clone, Debug, PartialEq, Eq)] |
544 | | pub struct ReasonFlags { |
545 | | pub flags: u16, |
546 | | } |
547 | | |
548 | | impl ReasonFlags { |
549 | 0 | pub fn key_compromise(&self) -> bool { |
550 | 0 | (self.flags >> 1) & 1 == 1 |
551 | 0 | } |
552 | 0 | pub fn ca_compromise(&self) -> bool { |
553 | 0 | (self.flags >> 2) & 1 == 1 |
554 | 0 | } |
555 | 0 | pub fn affilation_changed(&self) -> bool { |
556 | 0 | (self.flags >> 3) & 1 == 1 |
557 | 0 | } |
558 | 0 | pub fn superseded(&self) -> bool { |
559 | 0 | (self.flags >> 4) & 1 == 1 |
560 | 0 | } |
561 | 0 | pub fn cessation_of_operation(&self) -> bool { |
562 | 0 | (self.flags >> 5) & 1 == 1 |
563 | 0 | } |
564 | 0 | pub fn certificate_hold(&self) -> bool { |
565 | 0 | (self.flags >> 6) & 1 == 1 |
566 | 0 | } |
567 | 0 | pub fn privelege_withdrawn(&self) -> bool { |
568 | 0 | (self.flags >> 7) & 1 == 1 |
569 | 0 | } |
570 | 0 | pub fn aa_compromise(&self) -> bool { |
571 | 0 | (self.flags >> 8) & 1 == 1 |
572 | 0 | } |
573 | | } |
574 | | |
575 | | const REASON_FLAGS: &[&str] = &[ |
576 | | "Unused", |
577 | | "Key Compromise", |
578 | | "CA Compromise", |
579 | | "Affiliation Changed", |
580 | | "Superseded", |
581 | | "Cessation Of Operation", |
582 | | "Certificate Hold", |
583 | | "Privilege Withdrawn", |
584 | | "AA Compromise", |
585 | | ]; |
586 | | |
587 | | impl fmt::Display for ReasonFlags { |
588 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
589 | 0 | let mut s = String::new(); |
590 | 0 | let mut acc = self.flags; |
591 | 0 | for flag_text in REASON_FLAGS { |
592 | 0 | if acc & 1 != 0 { |
593 | 0 | s = s + flag_text + ", "; |
594 | 0 | } |
595 | 0 | acc >>= 1; |
596 | | } |
597 | 0 | s.pop(); |
598 | 0 | s.pop(); |
599 | 0 | f.write_str(&s) |
600 | 0 | } |
601 | | } |
602 | | |
603 | | #[derive(Clone, Debug, PartialEq)] |
604 | | pub struct IssuingDistributionPoint<'a> { |
605 | | pub distribution_point: Option<DistributionPointName<'a>>, |
606 | | pub only_contains_user_certs: bool, |
607 | | pub only_contains_ca_certs: bool, |
608 | | pub only_some_reasons: Option<ReasonFlags>, |
609 | | pub indirect_crl: bool, |
610 | | pub only_contains_attribute_certs: bool, |
611 | | } |
612 | | |
613 | | pub(crate) mod parser { |
614 | | use crate::extensions::*; |
615 | | use asn1_rs::{GeneralizedTime, ParseResult}; |
616 | | use der_parser::ber::BerObject; |
617 | | use lazy_static::lazy_static; |
618 | | |
619 | | type ExtParser = fn(&[u8]) -> IResult<&[u8], ParsedExtension, BerError>; |
620 | | |
621 | | lazy_static! { |
622 | | static ref EXTENSION_PARSERS: HashMap<Oid<'static>, ExtParser> = { |
623 | | macro_rules! add { |
624 | | ($m:ident, $oid:ident, $p:ident) => { |
625 | | $m.insert($oid, $p as ExtParser); |
626 | | }; |
627 | | } |
628 | | |
629 | | let mut m = HashMap::new(); |
630 | | add!( |
631 | | m, |
632 | | OID_X509_EXT_SUBJECT_KEY_IDENTIFIER, |
633 | | parse_keyidentifier_ext |
634 | | ); |
635 | | add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage_ext); |
636 | | add!( |
637 | | m, |
638 | | OID_X509_EXT_SUBJECT_ALT_NAME, |
639 | | parse_subjectalternativename_ext |
640 | | ); |
641 | | add!( |
642 | | m, |
643 | | OID_X509_EXT_ISSUER_ALT_NAME, |
644 | | parse_issueralternativename_ext |
645 | | ); |
646 | | add!( |
647 | | m, |
648 | | OID_X509_EXT_BASIC_CONSTRAINTS, |
649 | | parse_basicconstraints_ext |
650 | | ); |
651 | | add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints_ext); |
652 | | add!( |
653 | | m, |
654 | | OID_X509_EXT_CERTIFICATE_POLICIES, |
655 | | parse_certificatepolicies_ext |
656 | | ); |
657 | | add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings_ext); |
658 | | add!( |
659 | | m, |
660 | | OID_X509_EXT_POLICY_CONSTRAINTS, |
661 | | parse_policyconstraints_ext |
662 | | ); |
663 | | add!( |
664 | | m, |
665 | | OID_X509_EXT_EXTENDED_KEY_USAGE, |
666 | | parse_extendedkeyusage_ext |
667 | | ); |
668 | | add!( |
669 | | m, |
670 | | OID_X509_EXT_CRL_DISTRIBUTION_POINTS, |
671 | | parse_crldistributionpoints_ext |
672 | | ); |
673 | | add!( |
674 | | m, |
675 | | OID_X509_EXT_INHIBITANT_ANY_POLICY, |
676 | | parse_inhibitanypolicy_ext |
677 | | ); |
678 | | add!( |
679 | | m, |
680 | | OID_PKIX_AUTHORITY_INFO_ACCESS, |
681 | | parse_authorityinfoaccess_ext |
682 | | ); |
683 | | add!( |
684 | | m, |
685 | | OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER, |
686 | | parse_authoritykeyidentifier_ext |
687 | | ); |
688 | | add!(m, OID_CT_LIST_SCT, parse_sct_ext); |
689 | | add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype_ext); |
690 | | add!(m, OID_X509_EXT_CERT_COMMENT, parse_nscomment_ext); |
691 | | add!(m, OID_X509_EXT_CRL_NUMBER, parse_crl_number); |
692 | | add!(m, OID_X509_EXT_REASON_CODE, parse_reason_code); |
693 | | add!(m, OID_X509_EXT_INVALIDITY_DATE, parse_invalidity_date); |
694 | | add!( |
695 | | m, |
696 | | OID_X509_EXT_ISSUER_DISTRIBUTION_POINT, |
697 | | parse_issuingdistributionpoint_ext |
698 | | ); |
699 | | m |
700 | | }; |
701 | | } |
702 | | |
703 | | // look into the parser map if the extension is known, and parse it |
704 | | // otherwise, leave it as UnsupportedExtension |
705 | 0 | fn parse_extension0<'a>( |
706 | 0 | orig_i: &'a [u8], |
707 | 0 | i: &'a [u8], |
708 | 0 | oid: &Oid, |
709 | 0 | ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> { |
710 | 0 | if let Some(parser) = EXTENSION_PARSERS.get(oid) { |
711 | 0 | match parser(i) { |
712 | 0 | Ok((_, ext)) => Ok((orig_i, ext)), |
713 | 0 | Err(error) => Ok((orig_i, ParsedExtension::ParseError { error })), |
714 | | } |
715 | | } else { |
716 | 0 | Ok(( |
717 | 0 | orig_i, |
718 | 0 | ParsedExtension::UnsupportedExtension { |
719 | 0 | oid: oid.to_owned(), |
720 | 0 | }, |
721 | 0 | )) |
722 | | } |
723 | 0 | } |
724 | | |
725 | 0 | pub(crate) fn parse_extension<'a>( |
726 | 0 | orig_i: &'a [u8], |
727 | 0 | i: &'a [u8], |
728 | 0 | oid: &Oid, |
729 | 0 | ) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> { |
730 | 0 | parse_extension0(orig_i, i, oid) |
731 | 0 | } |
732 | | |
733 | | /// Parse a "Basic Constraints" extension |
734 | | /// |
735 | | /// <pre> |
736 | | /// id-ce-basicConstraints OBJECT IDENTIFIER ::= { id-ce 19 } |
737 | | /// BasicConstraints ::= SEQUENCE { |
738 | | /// cA BOOLEAN DEFAULT FALSE, |
739 | | /// pathLenConstraint INTEGER (0..MAX) OPTIONAL } |
740 | | /// </pre> |
741 | | /// |
742 | | /// Note the maximum length of the `pathLenConstraint` field is limited to the size of a 32-bits |
743 | | /// unsigned integer, and parsing will fail if value if larger. |
744 | 0 | pub(super) fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], BasicConstraints, BerError> { |
745 | 0 | let (rem, obj) = parse_der_sequence(i)?; |
746 | 0 | if let Ok(seq) = obj.as_sequence() { |
747 | 0 | let (ca, path_len_constraint) = match seq.len() { |
748 | 0 | 0 => (false, None), |
749 | | 1 => { |
750 | 0 | if let Ok(b) = seq[0].as_bool() { |
751 | 0 | (b, None) |
752 | 0 | } else if let Ok(u) = seq[0].as_u32() { |
753 | 0 | (false, Some(u)) |
754 | | } else { |
755 | 0 | return Err(Err::Error(BerError::InvalidTag)); |
756 | | } |
757 | | } |
758 | | 2 => { |
759 | 0 | let ca = seq[0] |
760 | 0 | .as_bool() |
761 | 0 | .or(Err(Err::Error(BerError::InvalidLength)))?; |
762 | 0 | let pl = seq[1] |
763 | 0 | .as_u32() |
764 | 0 | .or(Err(Err::Error(BerError::InvalidLength)))?; |
765 | 0 | (ca, Some(pl)) |
766 | | } |
767 | 0 | _ => return Err(Err::Error(BerError::InvalidLength)), |
768 | | }; |
769 | 0 | Ok(( |
770 | 0 | rem, |
771 | 0 | BasicConstraints { |
772 | 0 | ca, |
773 | 0 | path_len_constraint, |
774 | 0 | }, |
775 | 0 | )) |
776 | | } else { |
777 | 0 | Err(Err::Error(BerError::InvalidLength)) |
778 | | } |
779 | 0 | } |
780 | | |
781 | 0 | fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
782 | 0 | map(parse_basicconstraints, ParsedExtension::BasicConstraints)(i) |
783 | 0 | } |
784 | | |
785 | 0 | fn parse_nameconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
786 | 0 | map(parse_nameconstraints, ParsedExtension::NameConstraints)(i) |
787 | 0 | } |
788 | | |
789 | 0 | pub(super) fn parse_subjectalternativename_ext( |
790 | 0 | i: &[u8], |
791 | 0 | ) -> IResult<&[u8], ParsedExtension, BerError> { |
792 | 0 | parse_der_sequence_defined_g(|input, _| { |
793 | 0 | let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; |
794 | 0 | Ok(( |
795 | 0 | i, |
796 | 0 | ParsedExtension::SubjectAlternativeName(SubjectAlternativeName { general_names }), |
797 | 0 | )) |
798 | 0 | })(i) |
799 | 0 | } |
800 | | |
801 | 0 | pub(super) fn parse_issueralternativename_ext( |
802 | 0 | i: &[u8], |
803 | 0 | ) -> IResult<&[u8], ParsedExtension, BerError> { |
804 | 0 | parse_der_sequence_defined_g(|input, _| { |
805 | 0 | let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?; |
806 | 0 | Ok(( |
807 | 0 | i, |
808 | 0 | ParsedExtension::IssuerAlternativeName(IssuerAlternativeName { general_names }), |
809 | 0 | )) |
810 | 0 | })(i) |
811 | 0 | } |
812 | | |
813 | 0 | pub(super) fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], PolicyConstraints, BerError> { |
814 | 0 | parse_der_sequence_defined_g(|input, _| { |
815 | 0 | let (i, require_explicit_policy) = opt(complete(map_res( |
816 | 0 | parse_der_tagged_implicit(0, parse_der_content(Tag::Integer)), |
817 | 0 | |x| x.as_u32(), |
818 | 0 | )))(input)?; |
819 | 0 | let (i, inhibit_policy_mapping) = all_consuming(opt(complete(map_res( |
820 | 0 | parse_der_tagged_implicit(1, parse_der_content(Tag::Integer)), |
821 | 0 | |x| x.as_u32(), |
822 | 0 | ))))(i)?; |
823 | 0 | let policy_constraint = PolicyConstraints { |
824 | 0 | require_explicit_policy, |
825 | 0 | inhibit_policy_mapping, |
826 | 0 | }; |
827 | 0 | Ok((i, policy_constraint)) |
828 | 0 | })(i) |
829 | 0 | } |
830 | | |
831 | 0 | fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
832 | 0 | map(parse_policyconstraints, ParsedExtension::PolicyConstraints)(i) |
833 | 0 | } |
834 | | |
835 | 0 | fn parse_policymappings_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
836 | 0 | map(parse_policymappings, ParsedExtension::PolicyMappings)(i) |
837 | 0 | } |
838 | | |
839 | 0 | fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
840 | 0 | let (ret, skip_certs) = parse_der_u32(i)?; |
841 | 0 | Ok(( |
842 | 0 | ret, |
843 | 0 | ParsedExtension::InhibitAnyPolicy(InhibitAnyPolicy { skip_certs }), |
844 | 0 | )) |
845 | 0 | } |
846 | | |
847 | 0 | fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
848 | 0 | map(parse_extendedkeyusage, ParsedExtension::ExtendedKeyUsage)(i) |
849 | 0 | } |
850 | | |
851 | | // DistributionPointName ::= CHOICE { |
852 | | // fullName [0] GeneralNames, |
853 | | // nameRelativeToCRLIssuer [1] RelativeDistinguishedName } |
854 | 0 | fn parse_distributionpointname(i: &[u8]) -> IResult<&[u8], DistributionPointName, BerError> { |
855 | 0 | let (rem, header) = der_read_element_header(i)?; |
856 | 0 | match header.tag().0 { |
857 | | 0 => { |
858 | 0 | let (rem, names) = many1(complete(parse_generalname))(rem)?; |
859 | 0 | Ok((rem, DistributionPointName::FullName(names))) |
860 | | } |
861 | | 1 => { |
862 | 0 | let (rem, rdn) = RelativeDistinguishedName::from_der(rem) |
863 | 0 | .map_err(|_| BerError::BerValueError)?; |
864 | 0 | Ok((rem, DistributionPointName::NameRelativeToCRLIssuer(rdn))) |
865 | | } |
866 | 0 | _ => Err(Err::Error(BerError::InvalidTag)), |
867 | | } |
868 | 0 | } |
869 | | |
870 | 0 | fn parse_implicit_tagged_reasons(tag: u32) -> impl Fn(&[u8]) -> BerResult<ReasonFlags> { |
871 | 0 | move |i: &[u8]| { |
872 | 0 | let (rem, obj) = parse_der_tagged_implicit(tag, parse_der_content(Tag::BitString))(i)?; |
873 | 0 | parse_reasons(rem, obj) |
874 | 0 | } |
875 | 0 | } |
876 | | |
877 | | // ReasonFlags ::= BIT STRING { |
878 | | // unused (0), |
879 | | // keyCompromise (1), |
880 | | // cACompromise (2), |
881 | | // affiliationChanged (3), |
882 | | // superseded (4), |
883 | | // cessationOfOperation (5), |
884 | | // certificateHold (6), |
885 | | // privilegeWithdrawn (7), |
886 | | // aACompromise (8) } |
887 | 0 | fn parse_reasons<'a>(rem: &'a [u8], obj: BerObject<'a>) -> BerResult<'a, ReasonFlags> { |
888 | 0 | if let DerObjectContent::BitString(_, b) = obj.content { |
889 | 0 | let flags = b |
890 | 0 | .data |
891 | 0 | .iter() |
892 | 0 | .rev() |
893 | 0 | .fold(0, |acc, x| (acc << 8) | (x.reverse_bits() as u16)); |
894 | 0 | Ok((rem, ReasonFlags { flags })) |
895 | | } else { |
896 | 0 | Err(Err::Failure(BerError::InvalidTag)) |
897 | | } |
898 | 0 | } |
899 | | |
900 | 0 | fn parse_crlissuer_content(i: &[u8]) -> BerResult<Vec<GeneralName>> { |
901 | 0 | many1(complete(parse_generalname))(i) |
902 | 0 | } |
903 | | |
904 | | // DistributionPoint ::= SEQUENCE { |
905 | | // distributionPoint [0] DistributionPointName OPTIONAL, |
906 | | // reasons [1] ReasonFlags OPTIONAL, |
907 | | // cRLIssuer [2] GeneralNames OPTIONAL } |
908 | 0 | pub(super) fn parse_crldistributionpoint( |
909 | 0 | i: &[u8], |
910 | 0 | ) -> IResult<&[u8], CRLDistributionPoint, BerError> { |
911 | 0 | parse_der_sequence_defined_g(|content, _| { |
912 | 0 | let (rem, distribution_point) = |
913 | 0 | opt(complete(parse_der_tagged_explicit_g(0, |b, _| { |
914 | 0 | parse_distributionpointname(b) |
915 | 0 | })))(content)?; |
916 | 0 | let (rem, reasons) = opt(complete(parse_implicit_tagged_reasons(1)))(rem)?; |
917 | 0 | let (rem, crl_issuer) = opt(complete(parse_der_tagged_implicit_g(2, |i, _, _| { |
918 | 0 | parse_crlissuer_content(i) |
919 | 0 | })))(rem)?; |
920 | 0 | let crl_dp = CRLDistributionPoint { |
921 | 0 | distribution_point, |
922 | 0 | reasons, |
923 | 0 | crl_issuer, |
924 | 0 | }; |
925 | 0 | Ok((rem, crl_dp)) |
926 | 0 | })(i) |
927 | 0 | } |
928 | | |
929 | 0 | pub(super) fn parse_crldistributionpoints( |
930 | 0 | i: &[u8], |
931 | 0 | ) -> IResult<&[u8], CRLDistributionPoints, BerError> { |
932 | 0 | let (ret, crldps) = parse_der_sequence_of_v(parse_crldistributionpoint)(i)?; |
933 | 0 | Ok((ret, CRLDistributionPoints { points: crldps })) |
934 | 0 | } |
935 | | |
936 | 0 | fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
937 | 0 | map( |
938 | 0 | parse_crldistributionpoints, |
939 | 0 | ParsedExtension::CRLDistributionPoints, |
940 | 0 | )(i) |
941 | 0 | } |
942 | | |
943 | | // IssuingDistributionPoint ::= SEQUENCE { |
944 | | // distributionPoint [0] DistributionPointName OPTIONAL, |
945 | | // onlyContainsUserCerts [1] BOOLEAN DEFAULT FALSE, |
946 | | // onlyContainsCACerts [2] BOOLEAN DEFAULT FALSE, |
947 | | // onlySomeReasons [3] ReasonFlags OPTIONAL, |
948 | | // indirectCRL [4] BOOLEAN DEFAULT FALSE, |
949 | | // onlyContainsAttributeCerts [5] BOOLEAN DEFAULT FALSE } |
950 | 0 | pub(super) fn parse_issuingdistributionpoint( |
951 | 0 | i: &[u8], |
952 | 0 | ) -> IResult<&[u8], IssuingDistributionPoint, BerError> { |
953 | 0 | parse_der_sequence_defined_g(|content, _| { |
954 | 0 | let parse_tagged_bool = |tag: u32, rem| -> IResult<&[u8], bool, BerError> { |
955 | 0 | let (rem, value) = opt(complete(|_| { |
956 | 0 | parse_der_implicit(rem, tag, parse_der_content(Tag::Boolean)) |
957 | 0 | .map(|(res, ob)| (res, ob.as_bool().unwrap_or(false))) |
958 | 0 | }))(rem)?; |
959 | 0 | Ok((rem, value.unwrap_or_default())) |
960 | 0 | }; |
961 | | |
962 | 0 | let (rem, distribution_point) = |
963 | 0 | opt(complete(parse_der_tagged_explicit_g(0, |b, _| { |
964 | 0 | parse_distributionpointname(b) |
965 | 0 | })))(content)?; |
966 | | |
967 | 0 | let (rem, only_contains_user_certs) = parse_tagged_bool(1, rem)?; |
968 | 0 | let (rem, only_contains_ca_certs) = parse_tagged_bool(2, rem)?; |
969 | 0 | let (rem, only_some_reasons) = opt(complete(parse_implicit_tagged_reasons(3)))(rem)?; |
970 | 0 | let (rem, indirect_crl) = parse_tagged_bool(4, rem)?; |
971 | 0 | let (rem, only_contains_attribute_certs) = parse_tagged_bool(5, rem)?; |
972 | | |
973 | 0 | let crl_idp = IssuingDistributionPoint { |
974 | 0 | distribution_point, |
975 | 0 | only_contains_user_certs, |
976 | 0 | only_contains_ca_certs, |
977 | 0 | only_some_reasons, |
978 | 0 | indirect_crl, |
979 | 0 | only_contains_attribute_certs, |
980 | 0 | }; |
981 | 0 | Ok((rem, crl_idp)) |
982 | 0 | })(i) |
983 | 0 | } |
984 | | |
985 | 0 | fn parse_issuingdistributionpoint_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
986 | 0 | map( |
987 | 0 | parse_issuingdistributionpoint, |
988 | 0 | ParsedExtension::IssuingDistributionPoint, |
989 | 0 | )(i) |
990 | 0 | } |
991 | | |
992 | | // AuthorityInfoAccessSyntax ::= |
993 | | // SEQUENCE SIZE (1..MAX) OF AccessDescription |
994 | | // |
995 | | // AccessDescription ::= SEQUENCE { |
996 | | // accessMethod OBJECT IDENTIFIER, |
997 | | // accessLocation GeneralName } |
998 | 0 | pub(super) fn parse_authorityinfoaccess( |
999 | 0 | i: &[u8], |
1000 | 0 | ) -> IResult<&[u8], AuthorityInfoAccess, BerError> { |
1001 | 0 | fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> { |
1002 | 0 | parse_der_sequence_defined_g(|content, _| { |
1003 | | // Read first element, an oid. |
1004 | 0 | let (gn, oid) = Oid::from_der(content)?; |
1005 | | // Parse second element |
1006 | 0 | let (rest, gn) = parse_generalname(gn)?; |
1007 | 0 | Ok((rest, AccessDescription::new(oid, gn))) |
1008 | 0 | })(i) |
1009 | 0 | } |
1010 | 0 | let (ret, accessdescs) = parse_der_sequence_of_v(parse_aia)(i)?; |
1011 | 0 | Ok((ret, AuthorityInfoAccess { accessdescs })) |
1012 | 0 | } |
1013 | | |
1014 | 0 | fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1015 | 0 | map( |
1016 | 0 | parse_authorityinfoaccess, |
1017 | 0 | ParsedExtension::AuthorityInfoAccess, |
1018 | 0 | )(i) |
1019 | 0 | } |
1020 | | |
1021 | 0 | fn parse_aki_content<'a>( |
1022 | 0 | i: &'a [u8], |
1023 | 0 | _hdr: Header<'_>, |
1024 | 0 | ) -> IResult<&'a [u8], AuthorityKeyIdentifier<'a>, BerError> { |
1025 | 0 | let (i, key_identifier) = opt(complete(parse_der_tagged_implicit_g(0, |d, _, _| { |
1026 | 0 | Ok((&[], KeyIdentifier(d))) |
1027 | 0 | })))(i)?; |
1028 | 0 | let (i, authority_cert_issuer) = |
1029 | 0 | opt(complete(parse_der_tagged_implicit_g(1, |d, _, _| { |
1030 | 0 | many0(complete(parse_generalname))(d) |
1031 | 0 | })))(i)?; |
1032 | 0 | let (i, authority_cert_serial) = opt(complete(parse_der_tagged_implicit( |
1033 | 0 | 2, |
1034 | 0 | parse_der_content(Tag::Integer), |
1035 | 0 | )))(i)?; |
1036 | 0 | let authority_cert_serial = authority_cert_serial.and_then(|o| o.as_slice().ok()); |
1037 | 0 | let aki = AuthorityKeyIdentifier { |
1038 | 0 | key_identifier, |
1039 | 0 | authority_cert_issuer, |
1040 | 0 | authority_cert_serial, |
1041 | 0 | }; |
1042 | 0 | Ok((i, aki)) |
1043 | 0 | } |
1044 | | |
1045 | | // RFC 5280 section 4.2.1.1: Authority Key Identifier |
1046 | 0 | pub(super) fn parse_authoritykeyidentifier( |
1047 | 0 | i: &[u8], |
1048 | 0 | ) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> { |
1049 | 0 | let (rem, aki) = parse_der_sequence_defined_g(parse_aki_content)(i)?; |
1050 | 0 | Ok((rem, aki)) |
1051 | 0 | } |
1052 | | |
1053 | 0 | fn parse_authoritykeyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1054 | 0 | map( |
1055 | 0 | parse_authoritykeyidentifier, |
1056 | 0 | ParsedExtension::AuthorityKeyIdentifier, |
1057 | 0 | )(i) |
1058 | 0 | } |
1059 | | |
1060 | 0 | pub(super) fn parse_keyidentifier(i: &[u8]) -> IResult<&[u8], KeyIdentifier, BerError> { |
1061 | 0 | let (rest, id) = <&[u8]>::from_der(i)?; |
1062 | 0 | let ki = KeyIdentifier(id); |
1063 | 0 | Ok((rest, ki)) |
1064 | 0 | } |
1065 | | |
1066 | 0 | fn parse_keyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1067 | 0 | map(parse_keyidentifier, ParsedExtension::SubjectKeyIdentifier)(i) |
1068 | 0 | } |
1069 | | |
1070 | 0 | fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1071 | 0 | map(parse_keyusage, ParsedExtension::KeyUsage)(i) |
1072 | 0 | } |
1073 | | |
1074 | 0 | pub(super) fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], NSCertType, BerError> { |
1075 | 0 | let (rest, obj) = parse_der_bitstring(i)?; |
1076 | 0 | let bitstring = obj |
1077 | 0 | .content |
1078 | 0 | .as_bitstring() |
1079 | 0 | .or(Err(Err::Error(BerError::BerTypeError)))?; |
1080 | | // bitstring should be 1 byte long |
1081 | 0 | if bitstring.data.len() != 1 { |
1082 | 0 | return Err(Err::Error(BerError::BerValueError)); |
1083 | 0 | } |
1084 | 0 | let flags = bitstring.data[0].reverse_bits(); |
1085 | 0 | Ok((rest, NSCertType(flags))) |
1086 | 0 | } |
1087 | | |
1088 | 0 | fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1089 | 0 | map(parse_nscerttype, ParsedExtension::NSCertType)(i) |
1090 | 0 | } |
1091 | | |
1092 | 0 | fn parse_nscomment_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1093 | 0 | match parse_der_ia5string(i) { |
1094 | 0 | Ok((i, obj)) => { |
1095 | 0 | let s = obj.as_str()?; |
1096 | 0 | Ok((i, ParsedExtension::NsCertComment(s))) |
1097 | | } |
1098 | 0 | Err(e) => { |
1099 | | // Some implementations encode the comment directly, without |
1100 | | // wrapping it in an IA5String |
1101 | 0 | if let Ok(s) = std::str::from_utf8(i) { |
1102 | 0 | Ok((&[], ParsedExtension::NsCertComment(s))) |
1103 | | } else { |
1104 | 0 | Err(e) |
1105 | | } |
1106 | | } |
1107 | | } |
1108 | 0 | } |
1109 | | |
1110 | | // CertificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation |
1111 | | // |
1112 | | // PolicyInformation ::= SEQUENCE { |
1113 | | // policyIdentifier CertPolicyId, |
1114 | | // policyQualifiers SEQUENCE SIZE (1..MAX) OF |
1115 | | // PolicyQualifierInfo OPTIONAL } |
1116 | | // |
1117 | | // CertPolicyId ::= OBJECT IDENTIFIER |
1118 | | // |
1119 | | // PolicyQualifierInfo ::= SEQUENCE { |
1120 | | // policyQualifierId PolicyQualifierId, |
1121 | | // qualifier ANY DEFINED BY policyQualifierId } |
1122 | | // |
1123 | | // -- Implementations that recognize additional policy qualifiers MUST |
1124 | | // -- augment the following definition for PolicyQualifierId |
1125 | | // |
1126 | | // PolicyQualifierId ::= OBJECT IDENTIFIER ( id-qt-cps | id-qt-unotice ) |
1127 | 0 | pub(super) fn parse_certificatepolicies( |
1128 | 0 | i: &[u8], |
1129 | 0 | ) -> IResult<&[u8], Vec<PolicyInformation>, BerError> { |
1130 | 0 | fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> { |
1131 | 0 | parse_der_sequence_defined_g(|content, _| { |
1132 | 0 | let (rem, policy_qualifier_id) = Oid::from_der(content)?; |
1133 | 0 | let info = PolicyQualifierInfo { |
1134 | 0 | policy_qualifier_id, |
1135 | 0 | qualifier: rem, |
1136 | 0 | }; |
1137 | 0 | Ok((&[], info)) |
1138 | 0 | })(i) |
1139 | 0 | } |
1140 | 0 | fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> { |
1141 | 0 | parse_der_sequence_defined_g(|content, _| { |
1142 | 0 | let (rem, policy_id) = Oid::from_der(content)?; |
1143 | 0 | let (rem, policy_qualifiers) = |
1144 | 0 | opt(complete(parse_der_sequence_defined_g(|content, _| { |
1145 | 0 | many1(complete(parse_policy_qualifier_info))(content) |
1146 | 0 | })))(rem)?; |
1147 | 0 | let info = PolicyInformation { |
1148 | 0 | policy_id, |
1149 | 0 | policy_qualifiers, |
1150 | 0 | }; |
1151 | 0 | Ok((rem, info)) |
1152 | 0 | })(i) |
1153 | 0 | } |
1154 | 0 | parse_der_sequence_of_v(parse_policy_information)(i) |
1155 | 0 | } |
1156 | | |
1157 | 0 | fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1158 | 0 | map( |
1159 | 0 | parse_certificatepolicies, |
1160 | 0 | ParsedExtension::CertificatePolicies, |
1161 | 0 | )(i) |
1162 | 0 | } |
1163 | | |
1164 | | // CRLReason ::= ENUMERATED { ... |
1165 | 0 | fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1166 | 0 | let (rest, obj) = parse_der_enum(i)?; |
1167 | 0 | let code = obj |
1168 | 0 | .content |
1169 | 0 | .as_u32() |
1170 | 0 | .or(Err(Err::Error(BerError::BerValueError)))?; |
1171 | 0 | if code > 10 { |
1172 | 0 | return Err(Err::Error(BerError::BerValueError)); |
1173 | 0 | } |
1174 | 0 | let ret = ParsedExtension::ReasonCode(ReasonCode(code as u8)); |
1175 | 0 | Ok((rest, ret)) |
1176 | 0 | } |
1177 | | |
1178 | | // invalidityDate ::= GeneralizedTime |
1179 | 0 | fn parse_invalidity_date(i: &[u8]) -> ParseResult<ParsedExtension> { |
1180 | 0 | let (rest, t) = GeneralizedTime::from_der(i)?; |
1181 | 0 | let dt = t.utc_datetime()?; |
1182 | 0 | Ok((rest, ParsedExtension::InvalidityDate(ASN1Time::new(dt)))) |
1183 | 0 | } |
1184 | | |
1185 | | // CRLNumber ::= INTEGER (0..MAX) |
1186 | | // Note from RFC 3280: "CRL verifiers MUST be able to handle CRLNumber values up to 20 octets." |
1187 | 0 | fn parse_crl_number(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1188 | 0 | let (rest, num) = map_res(parse_der_integer, |obj| obj.as_biguint())(i)?; |
1189 | 0 | Ok((rest, ParsedExtension::CRLNumber(num))) |
1190 | 0 | } |
1191 | | |
1192 | 0 | fn parse_sct_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> { |
1193 | 0 | map( |
1194 | 0 | parse_ct_signed_certificate_timestamp_list, |
1195 | 0 | ParsedExtension::SCT, |
1196 | 0 | )(i) |
1197 | 0 | } |
1198 | | } |
1199 | | |
1200 | | /// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension |
1201 | 0 | pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> { |
1202 | 0 | parse_der_sequence_defined_g(|a, _| all_consuming(many0(complete(X509Extension::from_der)))(a))( |
1203 | 0 | i, |
1204 | | ) |
1205 | 0 | } |
1206 | | |
1207 | 0 | pub(crate) fn parse_extensions(i: &[u8], explicit_tag: Tag) -> X509Result<Vec<X509Extension>> { |
1208 | 0 | if i.is_empty() { |
1209 | 0 | return Ok((i, Vec::new())); |
1210 | 0 | } |
1211 | | |
1212 | 0 | match der_read_element_header(i) { |
1213 | 0 | Ok((rem, hdr)) => { |
1214 | 0 | if hdr.tag() != explicit_tag { |
1215 | 0 | return Err(Err::Error(X509Error::InvalidExtensions)); |
1216 | 0 | } |
1217 | 0 | all_consuming(parse_extension_sequence)(rem) |
1218 | | } |
1219 | 0 | Err(_) => Err(X509Error::InvalidExtensions.into()), |
1220 | | } |
1221 | 0 | } |
1222 | | |
1223 | | /// Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension |
1224 | 0 | pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> { |
1225 | 0 | let parser = X509ExtensionParser::new().with_deep_parse_extensions(false); |
1226 | | |
1227 | 0 | parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i) |
1228 | 0 | } |
1229 | | |
1230 | 0 | pub(crate) fn parse_extensions_envelope( |
1231 | 0 | i: &[u8], |
1232 | 0 | explicit_tag: Tag, |
1233 | 0 | ) -> X509Result<Vec<X509Extension>> { |
1234 | 0 | if i.is_empty() { |
1235 | 0 | return Ok((i, Vec::new())); |
1236 | 0 | } |
1237 | | |
1238 | 0 | match der_read_element_header(i) { |
1239 | 0 | Ok((rem, hdr)) => { |
1240 | 0 | if hdr.tag() != explicit_tag { |
1241 | 0 | return Err(Err::Error(X509Error::InvalidExtensions)); |
1242 | 0 | } |
1243 | 0 | all_consuming(parse_extension_envelope_sequence)(rem) |
1244 | | } |
1245 | 0 | Err(_) => Err(X509Error::InvalidExtensions.into()), |
1246 | | } |
1247 | 0 | } |
1248 | | |
1249 | 0 | fn der_read_critical(i: &[u8]) -> BerResult<bool> { |
1250 | | // Some certificates do not respect the DER BOOLEAN constraint (true must be encoded as 0xff) |
1251 | | // so we attempt to parse as BER |
1252 | 0 | let (rem, obj) = opt(parse_ber_bool)(i)?; |
1253 | 0 | let value = obj |
1254 | 0 | .map(|o| o.as_bool().unwrap_or_default()) // unwrap cannot fail, we just read a bool |
1255 | 0 | .unwrap_or(false) // default critical value |
1256 | | ; |
1257 | 0 | Ok((rem, value)) |
1258 | 0 | } |
1259 | | |
1260 | | #[cfg(test)] |
1261 | | mod tests { |
1262 | | use super::*; |
1263 | | |
1264 | | #[test] |
1265 | | fn test_keyusage_flags() { |
1266 | | let ku = KeyUsage { flags: 98 }; |
1267 | | assert!(!ku.digital_signature()); |
1268 | | assert!(ku.non_repudiation()); |
1269 | | assert!(!ku.key_encipherment()); |
1270 | | assert!(!ku.data_encipherment()); |
1271 | | assert!(!ku.key_agreement()); |
1272 | | assert!(ku.key_cert_sign()); |
1273 | | assert!(ku.crl_sign()); |
1274 | | assert!(!ku.encipher_only()); |
1275 | | assert!(!ku.decipher_only()); |
1276 | | } |
1277 | | |
1278 | | #[test] |
1279 | | fn test_extensions1() { |
1280 | | use der_parser::oid; |
1281 | | let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension1.der")) |
1282 | | .unwrap() |
1283 | | .1; |
1284 | | let tbs = &crt.tbs_certificate; |
1285 | | let bc = crt |
1286 | | .basic_constraints() |
1287 | | .expect("could not get basic constraints") |
1288 | | .expect("no basic constraints found"); |
1289 | | assert_eq!( |
1290 | | bc.value, |
1291 | | &BasicConstraints { |
1292 | | ca: true, |
1293 | | path_len_constraint: Some(1) |
1294 | | } |
1295 | | ); |
1296 | | { |
1297 | | let ku = tbs |
1298 | | .key_usage() |
1299 | | .expect("could not get key usage") |
1300 | | .expect("no key usage found") |
1301 | | .value; |
1302 | | assert!(ku.digital_signature()); |
1303 | | assert!(!ku.non_repudiation()); |
1304 | | assert!(ku.key_encipherment()); |
1305 | | assert!(ku.data_encipherment()); |
1306 | | assert!(ku.key_agreement()); |
1307 | | assert!(!ku.key_cert_sign()); |
1308 | | assert!(!ku.crl_sign()); |
1309 | | assert!(ku.encipher_only()); |
1310 | | assert!(ku.decipher_only()); |
1311 | | } |
1312 | | { |
1313 | | let eku = tbs |
1314 | | .extended_key_usage() |
1315 | | .expect("could not get extended key usage") |
1316 | | .expect("no extended key usage found") |
1317 | | .value; |
1318 | | assert!(!eku.any); |
1319 | | assert!(eku.server_auth); |
1320 | | assert!(!eku.client_auth); |
1321 | | assert!(eku.code_signing); |
1322 | | assert!(!eku.email_protection); |
1323 | | assert!(eku.time_stamping); |
1324 | | assert!(!eku.ocsp_signing); |
1325 | | assert_eq!(eku.other, vec![oid!(1.2.3 .4 .0 .42)]); |
1326 | | } |
1327 | | assert_eq!( |
1328 | | tbs.policy_constraints() |
1329 | | .expect("could not get policy constraints") |
1330 | | .expect("no policy constraints found") |
1331 | | .value, |
1332 | | &PolicyConstraints { |
1333 | | require_explicit_policy: None, |
1334 | | inhibit_policy_mapping: Some(10) |
1335 | | } |
1336 | | ); |
1337 | | let val = tbs |
1338 | | .inhibit_anypolicy() |
1339 | | .expect("could not get inhibit_anypolicy") |
1340 | | .expect("no inhibit_anypolicy found") |
1341 | | .value; |
1342 | | assert_eq!(val, &InhibitAnyPolicy { skip_certs: 2 }); |
1343 | | { |
1344 | | let alt_names = &tbs |
1345 | | .subject_alternative_name() |
1346 | | .expect("could not get subject alt names") |
1347 | | .expect("no subject alt names found") |
1348 | | .value |
1349 | | .general_names; |
1350 | | assert_eq!(alt_names[0], GeneralName::RFC822Name("foo@example.com")); |
1351 | | assert_eq!(alt_names[1], GeneralName::URI("http://my.url.here/")); |
1352 | | assert_eq!( |
1353 | | alt_names[2], |
1354 | | GeneralName::IPAddress([192, 168, 7, 1].as_ref()) |
1355 | | ); |
1356 | | assert_eq!( |
1357 | | format!( |
1358 | | "{}", |
1359 | | match alt_names[3] { |
1360 | | GeneralName::DirectoryName(ref dn) => dn, |
1361 | | _ => unreachable!(), |
1362 | | } |
1363 | | ), |
1364 | | "C=UK, O=My Organization, OU=My Unit, CN=My Name" |
1365 | | ); |
1366 | | assert_eq!(alt_names[4], GeneralName::DNSName("localhost")); |
1367 | | assert_eq!(alt_names[5], GeneralName::RegisteredID(oid!(1.2.90 .0))); |
1368 | | assert_eq!( |
1369 | | alt_names[6], |
1370 | | GeneralName::OtherName(oid!(1.2.3 .4), b"\xA0\x17\x0C\x15some other identifier") |
1371 | | ); |
1372 | | } |
1373 | | |
1374 | | { |
1375 | | let name_constraints = &tbs |
1376 | | .name_constraints() |
1377 | | .expect("could not get name constraints") |
1378 | | .expect("no name constraints found") |
1379 | | .value; |
1380 | | assert_eq!(name_constraints.permitted_subtrees, None); |
1381 | | assert_eq!( |
1382 | | name_constraints.excluded_subtrees, |
1383 | | Some(vec![ |
1384 | | GeneralSubtree { |
1385 | | base: GeneralName::IPAddress([192, 168, 0, 0, 255, 255, 0, 0].as_ref()) |
1386 | | }, |
1387 | | GeneralSubtree { |
1388 | | base: GeneralName::RFC822Name("foo.com") |
1389 | | }, |
1390 | | ]) |
1391 | | ); |
1392 | | } |
1393 | | } |
1394 | | |
1395 | | #[test] |
1396 | | fn test_extensions2() { |
1397 | | use der_parser::oid; |
1398 | | let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension2.der")) |
1399 | | .unwrap() |
1400 | | .1; |
1401 | | let tbs = crt.tbs_certificate; |
1402 | | assert_eq!( |
1403 | | tbs.policy_constraints() |
1404 | | .expect("could not get policy constraints") |
1405 | | .expect("no policy constraints found") |
1406 | | .value, |
1407 | | &PolicyConstraints { |
1408 | | require_explicit_policy: Some(5000), |
1409 | | inhibit_policy_mapping: None |
1410 | | } |
1411 | | ); |
1412 | | { |
1413 | | let pm = tbs |
1414 | | .policy_mappings() |
1415 | | .expect("could not get policy_mappings") |
1416 | | .expect("no policy_mappings found") |
1417 | | .value |
1418 | | .clone() |
1419 | | .into_hashmap(); |
1420 | | let mut pm_ref = HashMap::new(); |
1421 | | pm_ref.insert(oid!(2.34.23), vec![oid!(2.2)]); |
1422 | | pm_ref.insert(oid!(1.1), vec![oid!(0.0.4)]); |
1423 | | pm_ref.insert(oid!(2.2), vec![oid!(2.2.1), oid!(2.2.3)]); |
1424 | | assert_eq!(pm, pm_ref); |
1425 | | } |
1426 | | } |
1427 | | |
1428 | | #[test] |
1429 | | fn test_extensions_crl_distribution_points() { |
1430 | | // Extension not present |
1431 | | { |
1432 | | let crt = crate::parse_x509_certificate(include_bytes!( |
1433 | | "../../assets/crl-ext/crl-no-crl.der" |
1434 | | )) |
1435 | | .unwrap() |
1436 | | .1; |
1437 | | assert!(!crt |
1438 | | .tbs_certificate |
1439 | | .extensions_map() |
1440 | | .unwrap() |
1441 | | .contains_key(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)); |
1442 | | } |
1443 | | // CRLDistributionPoints has 1 entry with 1 URI |
1444 | | { |
1445 | | let crt = crate::parse_x509_certificate(include_bytes!( |
1446 | | "../../assets/crl-ext/crl-simple.der" |
1447 | | )) |
1448 | | .unwrap() |
1449 | | .1; |
1450 | | let crl = crt |
1451 | | .tbs_certificate |
1452 | | .extensions_map() |
1453 | | .unwrap() |
1454 | | .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) |
1455 | | .unwrap() |
1456 | | .parsed_extension(); |
1457 | | assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_))); |
1458 | | if let ParsedExtension::CRLDistributionPoints(crl) = crl { |
1459 | | assert_eq!(crl.len(), 1); |
1460 | | assert!(crl[0].reasons.is_none()); |
1461 | | assert!(crl[0].crl_issuer.is_none()); |
1462 | | let distribution_point = crl[0].distribution_point.as_ref().unwrap(); |
1463 | | assert!(matches!( |
1464 | | distribution_point, |
1465 | | DistributionPointName::FullName(_) |
1466 | | )); |
1467 | | if let DistributionPointName::FullName(names) = distribution_point { |
1468 | | assert_eq!(names.len(), 1); |
1469 | | assert!(matches!(names[0], GeneralName::URI(_))); |
1470 | | if let GeneralName::URI(uri) = names[0] { |
1471 | | assert_eq!(uri, "http://example.com/myca.crl") |
1472 | | } |
1473 | | } |
1474 | | } |
1475 | | } |
1476 | | // CRLDistributionPoints has 2 entries |
1477 | | { |
1478 | | let crt = crate::parse_x509_certificate(include_bytes!( |
1479 | | "../../assets/crl-ext/crl-complex.der" |
1480 | | )) |
1481 | | .unwrap() |
1482 | | .1; |
1483 | | let crl = crt |
1484 | | .tbs_certificate |
1485 | | .extensions_map() |
1486 | | .unwrap() |
1487 | | .get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS) |
1488 | | .unwrap() |
1489 | | .parsed_extension(); |
1490 | | assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_))); |
1491 | | if let ParsedExtension::CRLDistributionPoints(crl) = crl { |
1492 | | assert_eq!(crl.len(), 2); |
1493 | | // First CRL Distribution point |
1494 | | let reasons = crl[0].reasons.as_ref().unwrap(); |
1495 | | assert!(reasons.key_compromise()); |
1496 | | assert!(reasons.ca_compromise()); |
1497 | | assert!(!reasons.affilation_changed()); |
1498 | | assert!(!reasons.superseded()); |
1499 | | assert!(!reasons.cessation_of_operation()); |
1500 | | assert!(!reasons.certificate_hold()); |
1501 | | assert!(!reasons.privelege_withdrawn()); |
1502 | | assert!(reasons.aa_compromise()); |
1503 | | assert_eq!( |
1504 | | format!("{}", reasons), |
1505 | | "Key Compromise, CA Compromise, AA Compromise" |
1506 | | ); |
1507 | | let issuers = crl[0].crl_issuer.as_ref().unwrap(); |
1508 | | assert_eq!(issuers.len(), 1); |
1509 | | assert!(matches!(issuers[0], GeneralName::DirectoryName(_))); |
1510 | | if let GeneralName::DirectoryName(name) = &issuers[0] { |
1511 | | assert_eq!(name.to_string(), "C=US, O=Organisation, CN=Some Name"); |
1512 | | } |
1513 | | let distribution_point = crl[0].distribution_point.as_ref().unwrap(); |
1514 | | assert!(matches!( |
1515 | | distribution_point, |
1516 | | DistributionPointName::FullName(_) |
1517 | | )); |
1518 | | if let DistributionPointName::FullName(names) = distribution_point { |
1519 | | assert_eq!(names.len(), 1); |
1520 | | assert!(matches!(names[0], GeneralName::URI(_))); |
1521 | | if let GeneralName::URI(uri) = names[0] { |
1522 | | assert_eq!(uri, "http://example.com/myca.crl") |
1523 | | } |
1524 | | } |
1525 | | // Second CRL Distribution point |
1526 | | let reasons = crl[1].reasons.as_ref().unwrap(); |
1527 | | assert!(reasons.key_compromise()); |
1528 | | assert!(reasons.ca_compromise()); |
1529 | | assert!(!reasons.affilation_changed()); |
1530 | | assert!(!reasons.superseded()); |
1531 | | assert!(!reasons.cessation_of_operation()); |
1532 | | assert!(!reasons.certificate_hold()); |
1533 | | assert!(!reasons.privelege_withdrawn()); |
1534 | | assert!(!reasons.aa_compromise()); |
1535 | | assert_eq!(format!("{}", reasons), "Key Compromise, CA Compromise"); |
1536 | | assert!(crl[1].crl_issuer.is_none()); |
1537 | | let distribution_point = crl[1].distribution_point.as_ref().unwrap(); |
1538 | | assert!(matches!( |
1539 | | distribution_point, |
1540 | | DistributionPointName::FullName(_) |
1541 | | )); |
1542 | | if let DistributionPointName::FullName(names) = distribution_point { |
1543 | | assert_eq!(names.len(), 1); |
1544 | | assert!(matches!(names[0], GeneralName::URI(_))); |
1545 | | if let GeneralName::URI(uri) = names[0] { |
1546 | | assert_eq!(uri, "http://example.com/myca2.crl") |
1547 | | } |
1548 | | } |
1549 | | } |
1550 | | } |
1551 | | } |
1552 | | |
1553 | | // Test cases for: |
1554 | | // - parsing SubjectAlternativeName |
1555 | | // - parsing NameConstraints |
1556 | | } |