1 //! Attribute-related definitions as defined in X.501 (and updated by RFC 5280).
2 
3 use alloc::vec::Vec;
4 use const_oid::db::{
5     rfc4519::{COUNTRY_NAME, DOMAIN_COMPONENT, SERIAL_NUMBER},
6     Database, DB,
7 };
8 use core::{
9     fmt::{self, Write},
10     str::FromStr,
11 };
12 use der::{
13     asn1::{
14         Any, Ia5StringRef, ObjectIdentifier, PrintableStringRef, SetOfVec, TeletexStringRef,
15         Utf8StringRef,
16     },
17     Decode, Encode, Error, ErrorKind, Sequence, Tag, Tagged, ValueOrd,
18 };
19 
20 /// X.501 `AttributeType` as defined in [RFC 5280 Appendix A.1].
21 ///
22 /// ```text
23 /// AttributeType           ::= OBJECT IDENTIFIER
24 /// ```
25 ///
26 /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1
27 pub type AttributeType = ObjectIdentifier;
28 
29 /// X.501 `AttributeValue` as defined in [RFC 5280 Appendix A.1].
30 ///
31 /// ```text
32 /// AttributeValue          ::= ANY
33 /// ```
34 ///
35 /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1
36 pub type AttributeValue = Any;
37 
38 /// X.501 `Attribute` as defined in [RFC 5280 Appendix A.1].
39 ///
40 /// ```text
41 /// Attribute               ::= SEQUENCE {
42 ///     type             AttributeType,
43 ///     values    SET OF AttributeValue -- at least one value is required
44 /// }
45 /// ```
46 ///
47 /// Note that [RFC 2986 Section 4] defines a constrained version of this type:
48 ///
49 /// ```text
50 /// Attribute { ATTRIBUTE:IOSet } ::= SEQUENCE {
51 ///     type   ATTRIBUTE.&id({IOSet}),
52 ///     values SET SIZE(1..MAX) OF ATTRIBUTE.&Type({IOSet}{@type})
53 /// }
54 /// ```
55 ///
56 /// The unconstrained version should be preferred.
57 ///
58 /// [RFC 2986 Section 4]: https://datatracker.ietf.org/doc/html/rfc2986#section-4
59 /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1
60 #[derive(Clone, Debug, PartialEq, Eq, Sequence, ValueOrd)]
61 #[allow(missing_docs)]
62 pub struct Attribute {
63     pub oid: AttributeType,
64     pub values: SetOfVec<AttributeValue>,
65 }
66 
67 /// X.501 `Attributes` as defined in [RFC 2986 Section 4].
68 ///
69 /// ```text
70 /// Attributes { ATTRIBUTE:IOSet } ::= SET OF Attribute{{ IOSet }}
71 /// ```
72 ///
73 /// [RFC 2986 Section 4]: https://datatracker.ietf.org/doc/html/rfc2986#section-4
74 pub type Attributes = SetOfVec<Attribute>;
75 
76 /// X.501 `AttributeTypeAndValue` as defined in [RFC 5280 Appendix A.1].
77 ///
78 /// ```text
79 /// AttributeTypeAndValue ::= SEQUENCE {
80 ///   type     AttributeType,
81 ///   value    AttributeValue
82 /// }
83 /// ```
84 ///
85 /// [RFC 5280 Appendix A.1]: https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1
86 #[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
87 #[derive(Clone, Debug, Eq, PartialEq, PartialOrd, Ord, Sequence, ValueOrd)]
88 #[allow(missing_docs)]
89 pub struct AttributeTypeAndValue {
90     pub oid: AttributeType,
91     pub value: AttributeValue,
92 }
93 
94 #[derive(Copy, Clone)]
95 enum Escape {
96     None,
97     Some,
98     Hex(u8),
99 }
100 
101 struct Parser {
102     state: Escape,
103     bytes: Vec<u8>,
104 }
105 
106 impl Parser {
new() -> Self107     pub fn new() -> Self {
108         Self {
109             state: Escape::None,
110             bytes: Vec::new(),
111         }
112     }
113 
push(&mut self, c: u8)114     fn push(&mut self, c: u8) {
115         self.state = Escape::None;
116         self.bytes.push(c);
117     }
118 
add(&mut self, c: u8) -> Result<(), Error>119     pub fn add(&mut self, c: u8) -> Result<(), Error> {
120         match (self.state, c) {
121             (Escape::Hex(p), b'0'..=b'9') => self.push(p | (c - b'0')),
122             (Escape::Hex(p), b'a'..=b'f') => self.push(p | (c - b'a' + 10)),
123             (Escape::Hex(p), b'A'..=b'F') => self.push(p | (c - b'A' + 10)),
124 
125             (Escape::Some, b'0'..=b'9') => self.state = Escape::Hex((c - b'0') << 4),
126             (Escape::Some, b'a'..=b'f') => self.state = Escape::Hex((c - b'a' + 10) << 4),
127             (Escape::Some, b'A'..=b'F') => self.state = Escape::Hex((c - b'A' + 10) << 4),
128 
129             (Escape::Some, b' ' | b'"' | b'#' | b'=' | b'\\') => self.push(c),
130             (Escape::Some, b'+' | b',' | b';' | b'<' | b'>') => self.push(c),
131 
132             (Escape::None, b'\\') => self.state = Escape::Some,
133             (Escape::None, ..) => self.push(c),
134 
135             _ => return Err(ErrorKind::Failed.into()),
136         }
137 
138         Ok(())
139     }
140 
as_bytes(&self) -> &[u8]141     pub fn as_bytes(&self) -> &[u8] {
142         &self.bytes
143     }
144 }
145 
146 impl AttributeTypeAndValue {
147     /// Parses the hex value in the `OID=#HEX` format.
from_hex(oid: ObjectIdentifier, val: &str) -> Result<Self, Error>148     fn from_hex(oid: ObjectIdentifier, val: &str) -> Result<Self, Error> {
149         // Ensure an even number of hex bytes.
150         let mut iter = match val.len() % 2 {
151             0 => [].iter().cloned().chain(val.bytes()),
152             1 => [0u8].iter().cloned().chain(val.bytes()),
153             _ => unreachable!(),
154         };
155 
156         // Decode der bytes from hex.
157         let mut bytes = Vec::with_capacity((val.len() + 1) / 2);
158 
159         while let (Some(h), Some(l)) = (iter.next(), iter.next()) {
160             let mut byte = 0u8;
161 
162             for (half, shift) in [(h, 4), (l, 0)] {
163                 match half {
164                     b'0'..=b'9' => byte |= (half - b'0') << shift,
165                     b'a'..=b'f' => byte |= (half - b'a' + 10) << shift,
166                     b'A'..=b'F' => byte |= (half - b'A' + 10) << shift,
167                     _ => return Err(ErrorKind::Failed.into()),
168                 }
169             }
170 
171             bytes.push(byte);
172         }
173 
174         Ok(Self {
175             oid,
176             value: Any::from_der(&bytes)?,
177         })
178     }
179 
180     /// Parses the string value in the `NAME=STRING` format.
from_delimited_str(oid: ObjectIdentifier, val: &str) -> Result<Self, Error>181     fn from_delimited_str(oid: ObjectIdentifier, val: &str) -> Result<Self, Error> {
182         // Undo escaping.
183         let mut parser = Parser::new();
184         for c in val.bytes() {
185             parser.add(c)?;
186         }
187 
188         let tag = match oid {
189             COUNTRY_NAME => Tag::PrintableString,
190             DOMAIN_COMPONENT => Tag::Ia5String,
191             // Serial numbers are formatted as Printable String as per RFC 5280 Appendix A.1:
192             // https://datatracker.ietf.org/doc/html/rfc5280#appendix-A.1
193             SERIAL_NUMBER => Tag::PrintableString,
194             _ => Tag::Utf8String,
195         };
196 
197         Ok(Self {
198             oid,
199             value: Any::new(tag, parser.as_bytes())?,
200         })
201     }
202 
203     /// Converts an AttributeTypeAndValue string into an encoded AttributeTypeAndValue
204     ///
205     /// This function follows the rules in [RFC 4514].
206     ///
207     /// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
208     #[deprecated(
209         since = "0.2.1",
210         note = "use AttributeTypeAndValue::from_str(...)?.to_der()"
211     )]
encode_from_string(s: &str) -> Result<Vec<u8>, Error>212     pub fn encode_from_string(s: &str) -> Result<Vec<u8>, Error> {
213         Self::from_str(s)?.to_der()
214     }
215 }
216 
217 /// Parse an [`AttributeTypeAndValue`] string.
218 ///
219 /// This function follows the rules in [RFC 4514].
220 ///
221 /// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
222 impl FromStr for AttributeTypeAndValue {
223     type Err = Error;
224 
from_str(s: &str) -> der::Result<Self>225     fn from_str(s: &str) -> der::Result<Self> {
226         let idx = s.find('=').ok_or_else(|| Error::from(ErrorKind::Failed))?;
227         let (key, val) = s.split_at(idx);
228         let val = &val[1..];
229 
230         // Either decode or lookup the OID for the given key.
231         let oid = match DB.by_name(key) {
232             Some(oid) => *oid,
233             None => ObjectIdentifier::new(key)?,
234         };
235 
236         // If the value is hex-encoded DER...
237         match val.strip_prefix('#') {
238             Some(val) => Self::from_hex(oid, val),
239             None => Self::from_delimited_str(oid, val),
240         }
241     }
242 }
243 
244 /// Serializes the structure according to the rules in [RFC 4514].
245 ///
246 /// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
247 impl fmt::Display for AttributeTypeAndValue {
fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result248     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
249         let val = match self.value.tag() {
250             Tag::PrintableString => PrintableStringRef::try_from(&self.value)
251                 .ok()
252                 .map(|s| s.as_str()),
253             Tag::Utf8String => Utf8StringRef::try_from(&self.value)
254                 .ok()
255                 .map(|s| s.as_str()),
256             Tag::Ia5String => Ia5StringRef::try_from(&self.value).ok().map(|s| s.as_str()),
257             Tag::TeletexString => TeletexStringRef::try_from(&self.value)
258                 .ok()
259                 .map(|s| s.as_str()),
260             _ => None,
261         };
262 
263         if let (Some(key), Some(val)) = (DB.shortest_name_by_oid(&self.oid), val) {
264             write!(f, "{}=", key.to_ascii_uppercase())?;
265 
266             let mut iter = val.char_indices().peekable();
267             while let Some((i, c)) = iter.next() {
268                 match c {
269                     '#' if i == 0 => write!(f, "\\#")?,
270                     ' ' if i == 0 || iter.peek().is_none() => write!(f, "\\ ")?,
271                     '"' | '+' | ',' | ';' | '<' | '>' | '\\' => write!(f, "\\{}", c)?,
272                     '\x00'..='\x1f' | '\x7f' => write!(f, "\\{:02x}", c as u8)?,
273                     _ => f.write_char(c)?,
274                 }
275             }
276         } else {
277             let value = self.value.to_der().or(Err(fmt::Error))?;
278 
279             write!(f, "{}=#", self.oid)?;
280             for c in value {
281                 write!(f, "{:02x}", c)?;
282             }
283         }
284 
285         Ok(())
286     }
287 }
288 
289 /// Helper trait to bring shortest name by oid lookups to Database
290 trait ShortestName {
shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&str>291     fn shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&str>;
292 }
293 
294 impl<'a> ShortestName for Database<'a> {
shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&'a str>295     fn shortest_name_by_oid(&self, oid: &ObjectIdentifier) -> Option<&'a str> {
296         let mut best_match: Option<&'a str> = None;
297 
298         for m in self.find_names_for_oid(*oid) {
299             if let Some(previous) = best_match {
300                 if m.len() < previous.len() {
301                     best_match = Some(m);
302                 }
303             } else {
304                 best_match = Some(m);
305             }
306         }
307 
308         best_match
309     }
310 }
311