x509_cert/name.rs
1//! Name-related definitions as defined in X.501 (and updated by RFC 5280).
2
3use crate::{attr::AttributeTypeAndValue, ext::pkix::name::DirectoryString};
4use alloc::vec::Vec;
5use const_oid::{
6 ObjectIdentifier,
7 db::{rfc3280, rfc4519},
8};
9use core::{cmp::Ordering, fmt, str::FromStr};
10use der::{
11 DecodeValue, Encode, EncodeValue, FixedTag, Header, Length, Reader, Tag, ValueOrd, Writer,
12 asn1::{Any, Ia5StringRef, PrintableStringRef, SetOfVec},
13};
14
15/// X.501 Name as defined in [RFC 5280 Section 4.1.2.4]. X.501 Name is used to represent distinguished names.
16///
17/// ```text
18/// Name ::= CHOICE { rdnSequence RDNSequence }
19/// ```
20///
21/// To build name, the syntax described in [RFC 4514 Section 3] is expected.
22///
23/// The following attribute names are recognized:
24/// ```text
25/// String X.500 AttributeType
26/// ------ --------------------------------------------
27/// CN commonName (2.5.4.3)
28/// L localityName (2.5.4.7)
29/// ST stateOrProvinceName (2.5.4.8)
30/// O organizationName (2.5.4.10)
31/// OU organizationalUnitName (2.5.4.11)
32/// C countryName (2.5.4.6)
33/// STREET streetAddress (2.5.4.9)
34/// DC domainComponent (0.9.2342.19200300.100.1.25)
35/// UID userId (0.9.2342.19200300.100.1.1)
36///
37/// ```
38///
39/// # Example
40///
41/// ```
42/// use std::str::FromStr;
43/// use x509_cert::name::Name;
44///
45/// // Multiple syntaxes are supported by `from_str`:
46/// let subject = Name::from_str("CN=example.com").unwrap();
47/// let subject = Name::from_str("C=US; ST=California; L=Los Angeles; O=InternetCorporationforAssignedNamesandNumbers; CN=www.example.org").unwrap();
48/// let subject = Name::from_str("C=US,ST=California,L=Los Angeles,O=InternetCorporationforAssignedNamesandNumbers,CN=www.example.org").unwrap();
49/// let subject = Name::from_str("C=US/ST=California/L=Los Angeles/O=InternetCorporationforAssignedNamesandNumbers/CN=www.example.org").unwrap();
50/// let subject = Name::from_str("UID=jsmith,DC=example,DC=net").unwrap();
51/// let subject = Name::from_str("OU=Sales+CN=J. Smith,DC=example,DC=net").unwrap();
52/// let subject = Name::from_str(r#"CN=James \"Jim\" Smith\, III,DC=example,DC=net"#).unwrap();
53/// let subject = Name::from_str(r#"CN=Before\0dAfter,DC=example,DC=net"#).unwrap();
54/// let subject = Name::from_str("1.3.6.1.4.1.1466.0=#04024869").unwrap();
55/// ```
56///
57/// [RFC 4514 Section 3]: https://www.rfc-editor.org/rfc/rfc4514#section-3
58/// [RFC 5280 Section 4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
59#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
60#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
61pub struct Name(pub(crate) RdnSequence);
62
63impl Name {
64 /// Build a name from an [`RdnSequence`].
65 ///
66 ///
67 /// This is provided as an escape hatch (see [RFC 5280 Section 4.1.2.4]) to build
68 /// names from `bmpString`, `TeletexString`, or `UniversalString`:
69 /// ```text
70 /// When CAs have previously issued certificates with issuer fields with
71 /// attributes encoded using TeletexString, BMPString, or
72 /// UniversalString, then the CA MAY continue to use these encodings of
73 /// the DirectoryString to preserve backward compatibility.
74 /// ```
75 ///
76 /// # Safety
77 ///
78 /// As the name implies, this is a dangerous helper. You are responsible for ensuring the
79 /// [`RdnSequence`] complies with the RFC.
80 ///
81 /// [RFC 5280 Section 4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
82 #[cfg(feature = "hazmat")]
83 pub fn hazmat_from_rdn_sequence(value: RdnSequence) -> Self {
84 Self(value)
85 }
86}
87
88impl From<Name> for RdnSequence {
89 #[inline]
90 fn from(value: Name) -> Self {
91 value.0
92 }
93}
94
95impl AsRef<RdnSequence> for Name {
96 #[inline]
97 fn as_ref(&self) -> &RdnSequence {
98 &self.0
99 }
100}
101
102impl FixedTag for Name {
103 const TAG: Tag = <RdnSequence as FixedTag>::TAG;
104}
105
106impl<'a> DecodeValue<'a> for Name {
107 type Error = der::Error;
108
109 fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
110 Ok(Self(RdnSequence::decode_value(decoder, header)?))
111 }
112}
113
114impl EncodeValue for Name {
115 fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
116 self.0.encode_value(encoder)
117 }
118
119 fn value_len(&self) -> der::Result<Length> {
120 self.0.value_len()
121 }
122}
123
124impl ValueOrd for Name {
125 fn value_cmp(&self, other: &Self) -> der::Result<Ordering> {
126 self.0.value_cmp(&other.0)
127 }
128}
129
130impl Name {
131 /// Is this [`Name`] empty?
132 #[inline]
133 pub fn is_empty(&self) -> bool {
134 self.0.is_empty()
135 }
136
137 /// Returns the number of [`RelativeDistinguishedName`] elements in this [`Name`].
138 pub fn len(&self) -> usize {
139 self.0.0.len()
140 }
141
142 /// Returns an iterator over the inner [`AttributeTypeAndValue`]s.
143 ///
144 /// This iterator does not expose which attributes are grouped together as
145 /// [`RelativeDistinguishedName`]s. If you need this, use [`Self::iter_rdn`].
146 #[inline]
147 pub fn iter(&self) -> impl Iterator<Item = &'_ AttributeTypeAndValue> + '_ {
148 self.0.0.iter().flat_map(move |rdn| rdn.0.as_slice())
149 }
150
151 /// Returns an iterator over the inner [`RelativeDistinguishedName`]s.
152 #[inline]
153 pub fn iter_rdn(&self) -> impl Iterator<Item = &'_ RelativeDistinguishedName> + '_ {
154 self.0.0.iter()
155 }
156}
157
158impl Name {
159 /// Returns the element found in the name identified by `oid`
160 ///
161 /// This will return `Ok(None)` if no such element is present.
162 ///
163 /// If more than one attribute is present with the specified OID, only the first attribute is
164 /// returned. Later elements should be fetched using [`Name::iter`].
165 ///
166 /// # Errors
167 ///
168 /// This will return [`der::Error`] if the content is not serialized as expected
169 pub fn by_oid<'a, T>(&'a self, oid: ObjectIdentifier) -> der::Result<Option<T>>
170 where
171 T: TryFrom<&'a Any, Error = der::Error>,
172 T: fmt::Debug,
173 {
174 self.iter()
175 .filter(|atav| atav.oid == oid)
176 .map(|atav| T::try_from(&atav.value))
177 .next()
178 .transpose()
179 }
180
181 /// Returns the Common Name (CN) found in the name.
182 ///
183 /// This will return `Ok(None)` if no CN is found.
184 ///
185 /// If more than one value is present, only the first is returned.
186 /// Later elements should be fetched using [`Name::iter`].
187 ///
188 /// # Errors
189 ///
190 /// This will return [`der::Error`] if the content is not serialized as a string.
191 pub fn common_name(&self) -> der::Result<Option<DirectoryString>> {
192 self.by_oid(rfc4519::COMMON_NAME)
193 }
194
195 /// Returns the Country (C) found in the name.
196 ///
197 /// This will return `Ok(None)` if no Country is found.
198 ///
199 /// If more than one value is present, only the first is returned.
200 /// Later elements should be fetched using [`Name::iter`].
201 ///
202 /// # Errors
203 ///
204 /// This will return [`der::Error`] if the content is not serialized as a printableString.
205 pub fn country(&self) -> der::Result<Option<PrintableStringRef<'_>>> {
206 self.by_oid(rfc4519::COUNTRY_NAME)
207 }
208
209 /// Returns the State or Province (ST) found in the name.
210 ///
211 /// This will return `Ok(None)` if no State or Province is found.
212 ///
213 /// If more than one value is present, only the first is returned.
214 /// Later elements should be fetched using [`Name::iter`].
215 ///
216 /// # Errors
217 ///
218 /// This will return [`der::Error`] if the content is not serialized as a string.
219 pub fn state_or_province(&self) -> der::Result<Option<DirectoryString>> {
220 self.by_oid(rfc4519::ST)
221 }
222
223 /// Returns the Locality (L) found in the name.
224 ///
225 /// This will return `Ok(None)` if no Locality is found.
226 ///
227 /// If more than one value is present, only the first is returned.
228 /// Later elements should be fetched using [`Name::iter`].
229 ///
230 /// # Errors
231 ///
232 /// This will return [`der::Error`] if the content is not serialized as a string.
233 pub fn locality(&self) -> der::Result<Option<DirectoryString>> {
234 self.by_oid(rfc4519::LOCALITY_NAME)
235 }
236
237 /// Returns the Organization (O) found in the name.
238 ///
239 /// This will return `Ok(None)` if no Organization is found.
240 ///
241 /// If more than one value is present, only the first is returned.
242 /// Later elements should be fetched using [`Name::iter`].
243 ///
244 /// # Errors
245 ///
246 /// This will return [`der::Error`] if the content is not serialized as a string.
247 pub fn organization(&self) -> der::Result<Option<DirectoryString>> {
248 self.by_oid(rfc4519::ORGANIZATION_NAME)
249 }
250
251 /// Returns the Organization Unit (OU) found in the name.
252 ///
253 /// This will return `Ok(None)` if no Organization Unit is found.
254 ///
255 /// If more than one value is present, only the first is returned.
256 /// Later elements should be fetched using [`Name::iter`].
257 ///
258 /// # Errors
259 ///
260 /// This will return [`der::Error`] if the content is not serialized as a string.
261 pub fn organization_unit(&self) -> der::Result<Option<DirectoryString>> {
262 self.by_oid(rfc4519::ORGANIZATIONAL_UNIT_NAME)
263 }
264
265 /// Returns the Email Address (emailAddress) found in the name.
266 ///
267 /// This will return `Ok(None)` if no email address is found.
268 ///
269 /// If more than one value is present, only the first is returned.
270 /// Later elements should be fetched using [`Name::iter`].
271 ///
272 /// # Errors
273 ///
274 /// This will return [`der::Error`] if the content is not serialized as an ia5String.
275 pub fn email_address(&self) -> der::Result<Option<Ia5StringRef<'_>>> {
276 self.by_oid(rfc3280::EMAIL_ADDRESS)
277 }
278}
279
280/// Parse a [`Name`] string.
281///
282/// Follows the rules in [RFC 4514].
283///
284/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
285impl FromStr for Name {
286 type Err = der::Error;
287
288 fn from_str(s: &str) -> der::Result<Self> {
289 Ok(Self(RdnSequence::from_str(s)?))
290 }
291}
292
293/// Serializes the name according to the rules in [RFC 4514].
294///
295/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
296impl fmt::Display for Name {
297 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
298 self.0.fmt(f)
299 }
300}
301
302/// X.501 RDNSequence as defined in [RFC 5280 Section 4.1.2.4].
303///
304/// ```text
305/// RDNSequence ::= SEQUENCE OF RelativeDistinguishedName
306/// ```
307///
308/// [RFC 5280 Section 4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
309#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
310#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
311pub struct RdnSequence(Vec<RelativeDistinguishedName>);
312
313impl RdnSequence {
314 /// Converts an `RDNSequence` string into an encoded `RDNSequence`.
315 #[deprecated(since = "0.2.1", note = "use RdnSequence::from_str(...)?.to_der()")]
316 pub fn encode_from_string(s: &str) -> Result<Vec<u8>, der::Error> {
317 Self::from_str(s)?.to_der()
318 }
319
320 /// Is this [`RdnSequence`] empty?
321 pub fn is_empty(&self) -> bool {
322 self.0.is_empty()
323 }
324
325 /// Iterate over this [`RdnSequence`].
326 pub fn iter(&self) -> impl Iterator<Item = &RelativeDistinguishedName> {
327 self.0.iter()
328 }
329
330 /// Length of this [`RdnSequence`].
331 pub fn len(&self) -> usize {
332 self.0.len()
333 }
334
335 /// Push a [`RelativeDistinguishedName`] onto this [`RdnSequence`].
336 pub fn push(&mut self, name: RelativeDistinguishedName) {
337 self.0.push(name)
338 }
339}
340
341/// Parse an [`RdnSequence`] string.
342///
343/// Follows the rules in [RFC 4514].
344///
345/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
346impl FromStr for RdnSequence {
347 type Err = der::Error;
348
349 fn from_str(s: &str) -> der::Result<Self> {
350 let mut parts = split(s, b',')
351 .map(RelativeDistinguishedName::from_str)
352 .collect::<der::Result<Vec<_>>>()?;
353 parts.reverse();
354 Ok(Self(parts))
355 }
356}
357
358/// Serializes the structure according to the rules in [RFC 4514].
359///
360/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
361impl fmt::Display for RdnSequence {
362 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
363 // As per RFC 4514 Section 2.1, the elements are reversed
364 for (i, atv) in self.0.iter().rev().enumerate() {
365 match i {
366 0 => write!(f, "{atv}")?,
367 _ => write!(f, ",{atv}")?,
368 }
369 }
370
371 Ok(())
372 }
373}
374
375impl_newtype!(RdnSequence, Vec<RelativeDistinguishedName>);
376
377/// Find the indices of all non-escaped separators.
378fn find(s: &str, b: u8) -> impl '_ + Iterator<Item = usize> {
379 (0..s.len())
380 .filter(move |i| s.as_bytes()[*i] == b)
381 .filter(|i| {
382 let x = i
383 .checked_sub(2)
384 .map(|i| s.as_bytes()[i])
385 .unwrap_or_default();
386
387 let y = i
388 .checked_sub(1)
389 .map(|i| s.as_bytes()[i])
390 .unwrap_or_default();
391
392 y != b'\\' || x == b'\\'
393 })
394}
395
396/// Split a string at all non-escaped separators.
397fn split(s: &str, b: u8) -> impl '_ + Iterator<Item = &'_ str> {
398 let mut prev = 0;
399 find(s, b).chain([s.len()]).map(move |i| {
400 let x = &s[prev..i];
401 prev = i + 1;
402 x
403 })
404}
405
406/// X.501 DistinguishedName as defined in [RFC 5280 Section 4.1.2.4].
407///
408/// ```text
409/// DistinguishedName ::= RDNSequence
410/// ```
411///
412/// [RFC 5280 Section 4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
413pub type DistinguishedName = RdnSequence;
414
415/// RelativeDistinguishedName as defined in [RFC 5280 Section 4.1.2.4].
416///
417/// ```text
418/// RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndValue
419/// ```
420///
421/// Note that we follow the more common definition above. This technically
422/// differs from the definition in X.501, which is:
423///
424/// ```text
425/// RelativeDistinguishedName ::= SET SIZE (1..MAX) OF AttributeTypeAndDistinguishedValue
426///
427/// AttributeTypeAndDistinguishedValue ::= SEQUENCE {
428/// type ATTRIBUTE.&id ({SupportedAttributes}),
429/// value ATTRIBUTE.&Type({SupportedAttributes}{@type}),
430/// primaryDistinguished BOOLEAN DEFAULT TRUE,
431/// valuesWithContext SET SIZE (1..MAX) OF SEQUENCE {
432/// distingAttrValue [0] ATTRIBUTE.&Type ({SupportedAttributes}{@type}) OPTIONAL,
433/// contextList SET SIZE (1..MAX) OF Context
434/// } OPTIONAL
435/// }
436/// ```
437///
438/// [RFC 5280 Section 4.1.2.4]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.1.2.4
439#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
440#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
441pub struct RelativeDistinguishedName(pub(crate) SetOfVec<AttributeTypeAndValue>);
442
443impl RelativeDistinguishedName {
444 /// Is this [`RelativeDistinguishedName`] empty?
445 pub fn is_empty(&self) -> bool {
446 self.0.is_empty()
447 }
448
449 /// Iterate over this [`RelativeDistinguishedName`].
450 pub fn iter(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
451 self.0.iter()
452 }
453
454 /// Length of this [`RelativeDistinguishedName`].
455 pub fn len(&self) -> usize {
456 self.0.len()
457 }
458
459 /// Insert an [`AttributeTypeAndValue`] into this [`RelativeDistinguishedName`]. Must be unique.
460 pub fn insert(&mut self, item: AttributeTypeAndValue) -> Result<(), der::Error> {
461 self.0.insert(item)
462 }
463}
464
465/// Parse a [`RelativeDistinguishedName`] string.
466///
467/// This function follows the rules in [RFC 4514].
468///
469/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
470impl FromStr for RelativeDistinguishedName {
471 type Err = der::Error;
472
473 fn from_str(s: &str) -> der::Result<Self> {
474 split(s, b'+')
475 .map(AttributeTypeAndValue::from_str)
476 .collect::<der::Result<Vec<_>>>()?
477 .try_into()
478 .map(Self)
479 }
480}
481
482impl TryFrom<Vec<AttributeTypeAndValue>> for RelativeDistinguishedName {
483 type Error = der::Error;
484
485 fn try_from(vec: Vec<AttributeTypeAndValue>) -> der::Result<RelativeDistinguishedName> {
486 Ok(RelativeDistinguishedName(SetOfVec::try_from(vec)?))
487 }
488}
489
490/// Serializes the structure according to the rules in [RFC 4514].
491///
492/// [RFC 4514]: https://datatracker.ietf.org/doc/html/rfc4514
493impl fmt::Display for RelativeDistinguishedName {
494 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
495 for (i, atv) in self.0.iter().enumerate() {
496 match i {
497 0 => write!(f, "{atv}")?,
498 _ => write!(f, "+{atv}")?,
499 }
500 }
501
502 Ok(())
503 }
504}
505
506impl_newtype!(RelativeDistinguishedName, SetOfVec<AttributeTypeAndValue>);