x509_parser/validate/
extensions.rs1use crate::extensions::*;
2use crate::validate::*;
3use std::collections::HashSet;
4
5const WARN_SHOULD_BE_CRITICAL: bool = false;
8
9macro_rules! test_critical {
10 (MUST $ext:ident, $l:ident, $name:expr) => {
11 if !$ext.critical {
12 $l.err(&format!("Extension {} MUST be critical, but is not", $name));
13 }
14 };
15 (MUST NOT $ext:ident, $l:ident, $name:expr) => {
16 if $ext.critical {
17 $l.err(&format!("Extension {} MUST NOT be critical, but is", $name));
18 }
19 };
20 (SHOULD $ext:ident, $l:ident, $name:expr) => {
21 if WARN_SHOULD_BE_CRITICAL && !$ext.critical {
22 $l.warn(&format!(
23 "Extension {} SHOULD be critical, but is not",
24 $name
25 ));
26 }
27 };
28 (SHOULD NOT $ext:ident, $l:ident, $name:expr) => {
29 if WARN_SHOULD_BE_CRITICAL && $ext.critical {
30 $l.warn(&format!(
31 "Extension {} SHOULD NOT be critical, but is",
32 $name
33 ));
34 }
35 };
36}
37
38#[derive(Debug)]
39pub struct X509ExtensionsValidator;
40
41impl<'a> Validator<'a> for X509ExtensionsValidator {
42 type Item = &'a [X509Extension<'a>];
43
44 fn validate<L: Logger>(&self, item: &'a Self::Item, l: &'_ mut L) -> bool {
45 let mut res = true;
46 {
48 let mut m = HashSet::new();
49 for ext in item.iter() {
50 if m.contains(&ext.oid) {
51 l.err(&format!("Duplicate extension {}", ext.oid));
52 res = false;
53 } else {
54 m.insert(ext.oid.clone());
55 }
56 }
57 }
58
59 for ext in item.iter() {
60 match ext.parsed_extension() {
62 ParsedExtension::AuthorityKeyIdentifier(aki) => {
63 test_critical!(MUST NOT ext, l, "AKI");
65 if aki.authority_cert_issuer.is_some() ^ aki.authority_cert_serial.is_some() {
67 l.warn("AKI: only one of Issuer and Serial is present");
68 }
69 }
70 ParsedExtension::CertificatePolicies(policies) => {
71 let mut policy_oids = HashSet::new();
74 for policy_info in policies {
75 if policy_oids.contains(&policy_info.policy_id) {
76 l.err(&format!(
77 "Certificate Policies: duplicate policy {}",
78 policy_info.policy_id
79 ));
80 res = false;
81 } else {
82 policy_oids.insert(policy_info.policy_id.clone());
83 }
84 }
85 }
86 ParsedExtension::KeyUsage(ku) => {
87 test_critical!(SHOULD ext, l, "KeyUsage");
89 if ku.flags == 0 {
92 l.err("KeyUsage: all flags are set to 0");
93 }
94 }
95 ParsedExtension::SubjectAlternativeName(san) => {
96 test_critical!(SHOULD NOT ext, l, "SubjectAltName");
98 for name in &san.general_names {
99 match name {
100 GeneralName::DNSName(ref s) | GeneralName::RFC822Name(ref s) => {
101 if !s.as_bytes().iter().all(u8::is_ascii) {
103 l.warn(&format!("Invalid charset in 'SAN' entry '{}'", s));
104 }
105 }
106 _ => (),
107 }
108 }
109 }
110 _ => (),
111 }
112 }
113 res
114 }
115}