/rust/registry/src/index.crates.io-1949cf8c6b5b557f/x509-parser-0.16.0/src/x509.rs
Line | Count | Source |
1 | | //! X.509 objects and types |
2 | | //! |
3 | | //! Based on RFC5280 |
4 | | //! |
5 | | |
6 | | use crate::error::{X509Error, X509Result}; |
7 | | use crate::objects::*; |
8 | | use crate::public_key::*; |
9 | | |
10 | | use asn1_rs::{ |
11 | | Any, BitString, BmpString, DerSequence, FromBer, FromDer, Oid, OptTaggedParser, ParseResult, |
12 | | }; |
13 | | use core::convert::TryFrom; |
14 | | use data_encoding::HEXUPPER; |
15 | | use der_parser::ber::MAX_OBJECT_SIZE; |
16 | | use der_parser::der::*; |
17 | | use der_parser::error::*; |
18 | | use der_parser::num_bigint::BigUint; |
19 | | use der_parser::*; |
20 | | use nom::branch::alt; |
21 | | use nom::bytes::complete::take; |
22 | | use nom::combinator::{complete, map}; |
23 | | use nom::multi::{many0, many1}; |
24 | | use nom::{Err, Offset}; |
25 | | use oid_registry::*; |
26 | | use rusticata_macros::newtype_enum; |
27 | | use std::fmt; |
28 | | use std::iter::FromIterator; |
29 | | |
30 | | /// The version of the encoded certificate. |
31 | | /// |
32 | | /// When extensions are used, as expected in this profile, version MUST be 3 |
33 | | /// (value is `2`). If no extensions are present, but a UniqueIdentifier |
34 | | /// is present, the version SHOULD be 2 (value is `1`); however, the |
35 | | /// version MAY be 3. If only basic fields are present, the version |
36 | | /// SHOULD be 1 (the value is omitted from the certificate as the default |
37 | | /// value); however, the version MAY be 2 or 3. |
38 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
39 | | pub struct X509Version(pub u32); |
40 | | |
41 | | impl X509Version { |
42 | | /// Parse [0] EXPLICIT Version DEFAULT v1 |
43 | 17.2k | pub(crate) fn from_der_tagged_0(i: &[u8]) -> X509Result<X509Version> { |
44 | 17.2k | let (rem, opt_version) = OptTaggedParser::from(0) |
45 | 17.2k | .parse_der(i, |_, data| Self::from_der(data)) |
46 | 17.2k | .map_err(Err::convert)?; |
47 | 14.5k | let version = opt_version.unwrap_or(X509Version::V1); |
48 | 14.5k | Ok((rem, version)) |
49 | 17.2k | } |
50 | | } |
51 | | |
52 | | // Version ::= INTEGER { v1(0), v2(1), v3(2) } |
53 | | impl<'a> FromDer<'a, X509Error> for X509Version { |
54 | 6.39k | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
55 | 6.39k | map(<u32>::from_der, X509Version)(i).map_err(|_| Err::Error(X509Error::InvalidVersion)) |
56 | 6.39k | } |
57 | | } |
58 | | |
59 | | newtype_enum! { |
60 | | impl display X509Version { |
61 | | V1 = 0, |
62 | | V2 = 1, |
63 | | V3 = 2, |
64 | | } |
65 | | } |
66 | | |
67 | | /// A generic attribute type and value |
68 | | /// |
69 | | /// These objects are used as [`RelativeDistinguishedName`] components. |
70 | | #[derive(Clone, Debug, PartialEq)] |
71 | | pub struct AttributeTypeAndValue<'a> { |
72 | | attr_type: Oid<'a>, |
73 | | attr_value: Any<'a>, // ANY -- DEFINED BY AttributeType |
74 | | } |
75 | | |
76 | | impl<'a> AttributeTypeAndValue<'a> { |
77 | | /// Builds a new `AttributeTypeAndValue` |
78 | | #[inline] |
79 | 30.1k | pub const fn new(attr_type: Oid<'a>, attr_value: Any<'a>) -> Self { |
80 | 30.1k | AttributeTypeAndValue { |
81 | 30.1k | attr_type, |
82 | 30.1k | attr_value, |
83 | 30.1k | } |
84 | 30.1k | } |
85 | | |
86 | | /// Returns the attribute type |
87 | | #[inline] |
88 | 0 | pub const fn attr_type(&self) -> &Oid<'a> { |
89 | 0 | &self.attr_type |
90 | 0 | } |
91 | | |
92 | | /// Returns the attribute value, as `ANY` |
93 | | #[inline] |
94 | 0 | pub const fn attr_value(&self) -> &Any<'a> { |
95 | 0 | &self.attr_value |
96 | 0 | } |
97 | | |
98 | | /// Attempt to get the content as `str`. |
99 | | /// This can fail if the object does not contain a string type. |
100 | | /// |
101 | | /// Note: the [`TryFrom`] trait is implemented for `&str`, so this is |
102 | | /// equivalent to `attr.try_into()`. |
103 | | /// |
104 | | /// Only NumericString, PrintableString, UTF8String and IA5String |
105 | | /// are considered here. Other string types can be read using `as_slice`. |
106 | | #[inline] |
107 | 0 | pub fn as_str(&self) -> Result<&'a str, X509Error> { |
108 | | // TODO: replace this with helper function, when it is added to asn1-rs |
109 | 0 | match self.attr_value.tag() { |
110 | | Tag::NumericString | Tag::PrintableString | Tag::Utf8String | Tag::Ia5String => { |
111 | 0 | let s = core::str::from_utf8(self.attr_value.data) |
112 | 0 | .map_err(|_| X509Error::InvalidAttributes)?; |
113 | 0 | Ok(s) |
114 | | } |
115 | 0 | t => Err(X509Error::Der(Error::unexpected_tag(None, t))), |
116 | | } |
117 | 0 | } |
118 | | |
119 | | /// Get the content as a slice. |
120 | | #[inline] |
121 | 0 | pub fn as_slice(&self) -> &[u8] { |
122 | 0 | self.attr_value.as_bytes() |
123 | 0 | } |
124 | | } |
125 | | |
126 | | impl<'a, 'b> TryFrom<&'a AttributeTypeAndValue<'b>> for &'a str { |
127 | | type Error = X509Error; |
128 | | |
129 | 0 | fn try_from(value: &'a AttributeTypeAndValue<'b>) -> Result<Self, Self::Error> { |
130 | 0 | value.attr_value.as_str().map_err(|e| e.into()) |
131 | 0 | } |
132 | | } |
133 | | |
134 | | impl<'a, 'b> From<&'a AttributeTypeAndValue<'b>> for &'a [u8] { |
135 | 0 | fn from(value: &'a AttributeTypeAndValue<'b>) -> Self { |
136 | 0 | value.as_slice() |
137 | 0 | } |
138 | | } |
139 | | |
140 | | // AttributeTypeAndValue ::= SEQUENCE { |
141 | | // type AttributeType, |
142 | | // value AttributeValue } |
143 | | impl<'a> FromDer<'a, X509Error> for AttributeTypeAndValue<'a> { |
144 | 61.2k | fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { |
145 | 61.2k | parse_der_sequence_defined_g(|i, _| { |
146 | 30.7k | let (i, attr_type) = Oid::from_der(i).or(Err(X509Error::InvalidX509Name))?; |
147 | 30.4k | let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?; |
148 | 30.1k | let attr = AttributeTypeAndValue::new(attr_type, attr_value); |
149 | 30.1k | Ok((i, attr)) |
150 | 61.2k | })(i) |
151 | 61.2k | } |
152 | | } |
153 | | |
154 | | // AttributeValue ::= ANY -- DEFINED BY AttributeType |
155 | | #[inline] |
156 | 30.4k | fn parse_attribute_value(i: &[u8]) -> ParseResult<Any, Error> { |
157 | 30.4k | alt((Any::from_der, parse_malformed_string))(i) |
158 | 30.4k | } |
159 | | |
160 | 113 | fn parse_malformed_string(i: &[u8]) -> ParseResult<Any, Error> { |
161 | 113 | let (rem, hdr) = Header::from_der(i)?; |
162 | 0 | let len = hdr.length().definite()?; |
163 | 0 | if len > MAX_OBJECT_SIZE { |
164 | 0 | return Err(nom::Err::Error(Error::InvalidLength)); |
165 | 0 | } |
166 | 0 | match hdr.tag() { |
167 | | Tag::PrintableString => { |
168 | | // if we are in this function, the PrintableString could not be validated. |
169 | | // Accept it without validating charset, because some tools do not respect the charset |
170 | | // restrictions (for ex. they use '*' while explicingly disallowed) |
171 | 0 | let (rem, data) = take(len)(rem)?; |
172 | | // check valid encoding |
173 | 0 | let _ = std::str::from_utf8(data).map_err(|_| Error::BerValueError)?; |
174 | 0 | let obj = Any::new(hdr, data); |
175 | 0 | Ok((rem, obj)) |
176 | | } |
177 | 0 | t => Err(nom::Err::Error(Error::unexpected_tag( |
178 | 0 | Some(Tag::PrintableString), |
179 | 0 | t, |
180 | 0 | ))), |
181 | | } |
182 | 113 | } |
183 | | |
184 | | /// A Relative Distinguished Name element. |
185 | | /// |
186 | | /// These objects are used as [`X509Name`] components. |
187 | | #[derive(Clone, Debug, PartialEq)] |
188 | | pub struct RelativeDistinguishedName<'a> { |
189 | | set: Vec<AttributeTypeAndValue<'a>>, |
190 | | } |
191 | | |
192 | | impl<'a> RelativeDistinguishedName<'a> { |
193 | | /// Builds a new `RelativeDistinguishedName` |
194 | | #[inline] |
195 | 0 | pub const fn new(set: Vec<AttributeTypeAndValue<'a>>) -> Self { |
196 | 0 | RelativeDistinguishedName { set } |
197 | 0 | } |
198 | | |
199 | | /// Return an iterator over the components of this object |
200 | 0 | pub fn iter(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
201 | 0 | self.set.iter() |
202 | 0 | } |
203 | | } |
204 | | |
205 | | impl<'a> FromIterator<AttributeTypeAndValue<'a>> for RelativeDistinguishedName<'a> { |
206 | 0 | fn from_iter<T: IntoIterator<Item = AttributeTypeAndValue<'a>>>(iter: T) -> Self { |
207 | 0 | let set = iter.into_iter().collect(); |
208 | 0 | RelativeDistinguishedName { set } |
209 | 0 | } |
210 | | } |
211 | | |
212 | | impl<'a> FromDer<'a, X509Error> for RelativeDistinguishedName<'a> { |
213 | 42.5k | fn from_der(i: &'a [u8]) -> X509Result<Self> { |
214 | 42.5k | parse_der_set_defined_g(|i, _| { |
215 | 31.1k | let (i, set) = many1(complete(AttributeTypeAndValue::from_der))(i)?; |
216 | 30.0k | let rdn = RelativeDistinguishedName { set }; |
217 | 30.0k | Ok((i, rdn)) |
218 | 42.5k | })(i) |
219 | 42.5k | } |
220 | | } |
221 | | |
222 | | #[derive(Clone, Debug, PartialEq)] |
223 | | pub struct SubjectPublicKeyInfo<'a> { |
224 | | pub algorithm: AlgorithmIdentifier<'a>, |
225 | | pub subject_public_key: BitString<'a>, |
226 | | /// A raw unparsed PKIX, ASN.1 DER form (see RFC 5280, Section 4.1). |
227 | | /// |
228 | | /// Note: use the [`Self::parsed()`] function to parse this object. |
229 | | pub raw: &'a [u8], |
230 | | } |
231 | | |
232 | | impl<'a> SubjectPublicKeyInfo<'a> { |
233 | | /// Attempt to parse the public key, and return the parsed version or an error |
234 | 0 | pub fn parsed(&self) -> Result<PublicKey, X509Error> { |
235 | 0 | let b = &self.subject_public_key.data; |
236 | 0 | if self.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION { |
237 | 0 | let (_, key) = RSAPublicKey::from_der(b).map_err(|_| X509Error::InvalidSPKI)?; |
238 | 0 | Ok(PublicKey::RSA(key)) |
239 | 0 | } else if self.algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY { |
240 | 0 | let key = ECPoint::from(b.as_ref()); |
241 | 0 | Ok(PublicKey::EC(key)) |
242 | 0 | } else if self.algorithm.algorithm == OID_KEY_TYPE_DSA { |
243 | 0 | let s = parse_der_integer(b) |
244 | 0 | .and_then(|(_, obj)| obj.as_slice().map_err(Err::Error)) |
245 | 0 | .or(Err(X509Error::InvalidSPKI))?; |
246 | 0 | Ok(PublicKey::DSA(s)) |
247 | 0 | } else if self.algorithm.algorithm == OID_GOST_R3410_2001 { |
248 | 0 | let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?; |
249 | 0 | Ok(PublicKey::GostR3410(s)) |
250 | 0 | } else if self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_256 |
251 | 0 | || self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_512 |
252 | | { |
253 | 0 | let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?; |
254 | 0 | Ok(PublicKey::GostR3410_2012(s)) |
255 | | } else { |
256 | 0 | Ok(PublicKey::Unknown(b)) |
257 | | } |
258 | 0 | } |
259 | | } |
260 | | |
261 | | impl<'a> FromDer<'a, X509Error> for SubjectPublicKeyInfo<'a> { |
262 | | /// Parse the SubjectPublicKeyInfo struct portion of a DER-encoded X.509 Certificate |
263 | 3.49k | fn from_der(i: &'a [u8]) -> X509Result<Self> { |
264 | 3.49k | let start_i = i; |
265 | 3.49k | parse_der_sequence_defined_g(move |i, _| { |
266 | 3.45k | let (i, algorithm) = AlgorithmIdentifier::from_der(i)?; |
267 | 3.43k | let (i, subject_public_key) = BitString::from_der(i).or(Err(X509Error::InvalidSPKI))?; |
268 | 3.42k | let len = start_i.offset(i); |
269 | 3.42k | let raw = &start_i[..len]; |
270 | 3.42k | let spki = SubjectPublicKeyInfo { |
271 | 3.42k | algorithm, |
272 | 3.42k | subject_public_key, |
273 | 3.42k | raw, |
274 | 3.42k | }; |
275 | 3.42k | Ok((i, spki)) |
276 | 3.49k | })(i) |
277 | 3.49k | } |
278 | | } |
279 | | |
280 | | /// Algorithm identifier |
281 | | /// |
282 | | /// An algorithm identifier is defined by the following ASN.1 structure: |
283 | | /// |
284 | | /// <pre> |
285 | | /// AlgorithmIdentifier ::= SEQUENCE { |
286 | | /// algorithm OBJECT IDENTIFIER, |
287 | | /// parameters ANY DEFINED BY algorithm OPTIONAL } |
288 | | /// </pre> |
289 | | /// |
290 | | /// The algorithm identifier is used to identify a cryptographic |
291 | | /// algorithm. The OBJECT IDENTIFIER component identifies the algorithm |
292 | | /// (such as DSA with SHA-1). The contents of the optional parameters |
293 | | /// field will vary according to the algorithm identified. |
294 | 0 | #[derive(Clone, Debug, PartialEq, DerSequence)] |
295 | | #[error(X509Error)] |
296 | | pub struct AlgorithmIdentifier<'a> { |
297 | | #[map_err(|_| X509Error::InvalidAlgorithmIdentifier)] |
298 | | pub algorithm: Oid<'a>, |
299 | | #[optional] |
300 | | pub parameters: Option<Any<'a>>, |
301 | | } |
302 | | |
303 | | impl<'a> AlgorithmIdentifier<'a> { |
304 | | /// Create a new `AlgorithmIdentifier` |
305 | 0 | pub const fn new(algorithm: Oid<'a>, parameters: Option<Any<'a>>) -> Self { |
306 | 0 | Self { |
307 | 0 | algorithm, |
308 | 0 | parameters, |
309 | 0 | } |
310 | 0 | } |
311 | | |
312 | | /// Get the algorithm OID |
313 | 0 | pub const fn oid(&'a self) -> &'a Oid { |
314 | 0 | &self.algorithm |
315 | 0 | } |
316 | | |
317 | | /// Get a reference to the algorithm parameters, if present |
318 | 0 | pub const fn parameters(&'a self) -> Option<&'a Any> { |
319 | 0 | self.parameters.as_ref() |
320 | 0 | } |
321 | | } |
322 | | |
323 | | /// X.509 Name (as used in `Issuer` and `Subject` fields) |
324 | | /// |
325 | | /// The Name describes a hierarchical name composed of attributes, such |
326 | | /// as country name, and corresponding values, such as US. The type of |
327 | | /// the component AttributeValue is determined by the AttributeType; in |
328 | | /// general it will be a DirectoryString. |
329 | | #[derive(Clone, Debug, PartialEq)] |
330 | | pub struct X509Name<'a> { |
331 | | pub(crate) rdn_seq: Vec<RelativeDistinguishedName<'a>>, |
332 | | pub(crate) raw: &'a [u8], |
333 | | } |
334 | | |
335 | | impl<'a> fmt::Display for X509Name<'a> { |
336 | 6.02k | fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { |
337 | 6.02k | match x509name_to_string(&self.rdn_seq, oid_registry()) { |
338 | 6.01k | Ok(o) => write!(f, "{}", o), |
339 | 12 | Err(_) => write!(f, "<X509Error: Invalid X.509 name>"), |
340 | | } |
341 | 6.02k | } |
342 | | } |
343 | | |
344 | | impl<'a> X509Name<'a> { |
345 | | /// Builds a new `X509Name` from the provided elements. |
346 | | #[inline] |
347 | 0 | pub const fn new(rdn_seq: Vec<RelativeDistinguishedName<'a>>, raw: &'a [u8]) -> Self { |
348 | 0 | X509Name { rdn_seq, raw } |
349 | 0 | } |
350 | | |
351 | | /// Attempt to format the current name, using the given registry to convert OIDs to strings. |
352 | | /// |
353 | | /// Note: a default registry is provided with this crate, and is returned by the |
354 | | /// [`oid_registry()`] method. |
355 | 0 | pub fn to_string_with_registry(&self, oid_registry: &OidRegistry) -> Result<String, X509Error> { |
356 | 0 | x509name_to_string(&self.rdn_seq, oid_registry) |
357 | 0 | } |
358 | | |
359 | | // Not using the AsRef trait, as that would not give back the full 'a lifetime |
360 | 0 | pub fn as_raw(&self) -> &'a [u8] { |
361 | 0 | self.raw |
362 | 0 | } |
363 | | |
364 | | /// Return an iterator over the `RelativeDistinguishedName` components of the name |
365 | 0 | pub fn iter(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> { |
366 | 0 | self.rdn_seq.iter() |
367 | 0 | } |
368 | | |
369 | | /// Return an iterator over the `RelativeDistinguishedName` components of the name |
370 | 0 | pub fn iter_rdn(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> { |
371 | 0 | self.rdn_seq.iter() |
372 | 0 | } |
373 | | |
374 | | /// Return an iterator over the attribute types and values of the name |
375 | 0 | pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
376 | 0 | self.rdn_seq.iter().flat_map(|rdn| rdn.set.iter()) |
377 | 0 | } |
378 | | |
379 | | /// Return an iterator over the components identified by the given OID |
380 | | /// |
381 | | /// The type of the component AttributeValue is determined by the AttributeType; in |
382 | | /// general it will be a DirectoryString. |
383 | | /// |
384 | | /// Attributes with same OID may be present multiple times, so the returned object is |
385 | | /// an iterator. |
386 | | /// Expected number of objects in this iterator are |
387 | | /// - 0: not found |
388 | | /// - 1: present once (common case) |
389 | | /// - 2 or more: attribute is present multiple times |
390 | 0 | pub fn iter_by_oid(&self, oid: &Oid<'a>) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
391 | | // this is necessary, otherwise rustc complains |
392 | | // that caller creates a temporary value for reference (for ex. |
393 | | // `self.iter_by_oid(&OID_X509_LOCALITY_NAME)` |
394 | | // ) |
395 | 0 | let oid = oid.clone(); |
396 | 0 | self.iter_attributes() |
397 | 0 | .filter(move |obj| obj.attr_type == oid) |
398 | 0 | } |
399 | | |
400 | | /// Return an iterator over the `CommonName` attributes of the X.509 Name. |
401 | | /// |
402 | | /// Returned iterator can be empty if there are no `CommonName` attributes. |
403 | | /// If you expect only one `CommonName` to be present, then using `next()` will |
404 | | /// get an `Option<&AttributeTypeAndValue>`. |
405 | | /// |
406 | | /// A common operation is to extract the `CommonName` as a string. |
407 | | /// |
408 | | /// ``` |
409 | | /// use x509_parser::x509::X509Name; |
410 | | /// |
411 | | /// fn get_first_cn_as_str<'a>(name: &'a X509Name<'_>) -> Option<&'a str> { |
412 | | /// name.iter_common_name() |
413 | | /// .next() |
414 | | /// .and_then(|cn| cn.as_str().ok()) |
415 | | /// } |
416 | | /// ``` |
417 | | /// |
418 | | /// Note that there are multiple reasons for failure or incorrect behavior, for ex. if |
419 | | /// the attribute is present multiple times, or is not a UTF-8 encoded string (it can be |
420 | | /// UTF-16, or even an OCTETSTRING according to the standard). |
421 | 0 | pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
422 | 0 | self.iter_by_oid(&OID_X509_COMMON_NAME) |
423 | 0 | } |
424 | | |
425 | | /// Return an iterator over the `Country` attributes of the X.509 Name. |
426 | 0 | pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
427 | 0 | self.iter_by_oid(&OID_X509_COUNTRY_NAME) |
428 | 0 | } |
429 | | |
430 | | /// Return an iterator over the `Organization` attributes of the X.509 Name. |
431 | 0 | pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
432 | 0 | self.iter_by_oid(&OID_X509_ORGANIZATION_NAME) |
433 | 0 | } |
434 | | |
435 | | /// Return an iterator over the `OrganizationalUnit` attributes of the X.509 Name. |
436 | 0 | pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
437 | 0 | self.iter_by_oid(&OID_X509_ORGANIZATIONAL_UNIT) |
438 | 0 | } |
439 | | |
440 | | /// Return an iterator over the `StateOrProvinceName` attributes of the X.509 Name. |
441 | 0 | pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
442 | 0 | self.iter_by_oid(&OID_X509_STATE_OR_PROVINCE_NAME) |
443 | 0 | } |
444 | | |
445 | | /// Return an iterator over the `Locality` attributes of the X.509 Name. |
446 | 0 | pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
447 | 0 | self.iter_by_oid(&OID_X509_LOCALITY_NAME) |
448 | 0 | } |
449 | | |
450 | | /// Return an iterator over the `EmailAddress` attributes of the X.509 Name. |
451 | 0 | pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> { |
452 | 0 | self.iter_by_oid(&OID_PKCS9_EMAIL_ADDRESS) |
453 | 0 | } |
454 | | } |
455 | | |
456 | | impl<'a> FromIterator<RelativeDistinguishedName<'a>> for X509Name<'a> { |
457 | 0 | fn from_iter<T: IntoIterator<Item = RelativeDistinguishedName<'a>>>(iter: T) -> Self { |
458 | 0 | let rdn_seq = iter.into_iter().collect(); |
459 | 0 | X509Name { rdn_seq, raw: &[] } |
460 | 0 | } |
461 | | } |
462 | | |
463 | | impl<'a> From<X509Name<'a>> for Vec<RelativeDistinguishedName<'a>> { |
464 | 0 | fn from(name: X509Name<'a>) -> Self { |
465 | 0 | name.rdn_seq |
466 | 0 | } |
467 | | } |
468 | | |
469 | | impl<'a> FromDer<'a, X509Error> for X509Name<'a> { |
470 | | /// Parse the X.501 type Name, used for ex in issuer and subject of a X.509 certificate |
471 | 13.3k | fn from_der(i: &'a [u8]) -> X509Result<Self> { |
472 | 13.3k | let start_i = i; |
473 | 13.3k | parse_der_sequence_defined_g(move |i, _| { |
474 | 12.4k | let (i, rdn_seq) = many0(complete(RelativeDistinguishedName::from_der))(i)?; |
475 | 12.4k | let len = start_i.offset(i); |
476 | 12.4k | let name = X509Name { |
477 | 12.4k | rdn_seq, |
478 | 12.4k | raw: &start_i[..len], |
479 | 12.4k | }; |
480 | 12.4k | Ok((i, name)) |
481 | 13.3k | })(i) |
482 | 13.3k | } |
483 | | } |
484 | | |
485 | | #[derive(Debug, PartialEq, Eq, Clone, Copy)] |
486 | | pub struct ReasonCode(pub u8); |
487 | | |
488 | | newtype_enum! { |
489 | | impl display ReasonCode { |
490 | | Unspecified = 0, |
491 | | KeyCompromise = 1, |
492 | | CACompromise = 2, |
493 | | AffiliationChanged = 3, |
494 | | Superseded = 4, |
495 | | CessationOfOperation = 5, |
496 | | CertificateHold = 6, |
497 | | // value 7 is not used |
498 | | RemoveFromCRL = 8, |
499 | | PrivilegeWithdrawn = 9, |
500 | | AACompromise = 10, |
501 | | } |
502 | | } |
503 | | |
504 | | impl Default for ReasonCode { |
505 | 0 | fn default() -> Self { |
506 | 0 | ReasonCode::Unspecified |
507 | 0 | } |
508 | | } |
509 | | |
510 | | // Attempt to convert attribute to string. If type is not a string, return value is the hex |
511 | | // encoding of the attribute value |
512 | 25.3k | fn attribute_value_to_string(attr: &Any, _attr_type: &Oid) -> Result<String, X509Error> { |
513 | | // TODO: replace this with helper function, when it is added to asn1-rs |
514 | 25.3k | match attr.tag() { |
515 | | Tag::NumericString |
516 | | | Tag::VisibleString |
517 | | | Tag::PrintableString |
518 | | | Tag::GeneralString |
519 | | | Tag::ObjectDescriptor |
520 | | | Tag::GraphicString |
521 | | | Tag::T61String |
522 | | | Tag::VideotexString |
523 | | | Tag::Utf8String |
524 | | | Tag::Ia5String => { |
525 | 25.3k | let s = core::str::from_utf8(attr.data).map_err(|_| X509Error::InvalidAttributes)?; |
526 | 25.3k | Ok(s.to_owned()) |
527 | | } |
528 | | Tag::BmpString => { |
529 | | // TODO: remove this when a new release of asn1-rs removes the need to consume attr in try_from |
530 | 0 | let any = attr.clone(); |
531 | 0 | let s = BmpString::try_from(any).map_err(|_| X509Error::InvalidAttributes)?; |
532 | 0 | Ok(s.string()) |
533 | | } |
534 | | _ => { |
535 | | // type is not a string, get slice and convert it to base64 |
536 | 24 | Ok(HEXUPPER.encode(attr.as_bytes())) |
537 | | } |
538 | | } |
539 | 25.3k | } |
540 | | |
541 | | /// Convert a DER representation of a X.509 name to a human-readable string |
542 | | /// |
543 | | /// RDNs are separated with "," |
544 | | /// Multiple RDNs are separated with "+" |
545 | | /// |
546 | | /// Attributes that cannot be represented by a string are hex-encoded |
547 | 6.02k | fn x509name_to_string( |
548 | 6.02k | rdn_seq: &[RelativeDistinguishedName], |
549 | 6.02k | oid_registry: &OidRegistry, |
550 | 6.02k | ) -> Result<String, X509Error> { |
551 | 25.3k | rdn_seq.iter().try_fold(String::new(), |acc, rdn| { |
552 | 25.3k | rdn.set |
553 | 25.3k | .iter() |
554 | 25.3k | .try_fold(String::new(), |acc2, attr| { |
555 | 25.3k | let val_str = attribute_value_to_string(&attr.attr_value, &attr.attr_type)?; |
556 | | // look ABBREV, and if not found, use shortname |
557 | 25.3k | let abbrev = match oid2abbrev(&attr.attr_type, oid_registry) { |
558 | 25.3k | Ok(s) => String::from(s), |
559 | 28 | _ => format!("{:?}", attr.attr_type), |
560 | | }; |
561 | 25.3k | let rdn = format!("{}={}", abbrev, val_str); |
562 | 25.3k | match acc2.len() { |
563 | 25.3k | 0 => Ok(rdn), |
564 | 0 | _ => Ok(acc2 + " + " + &rdn), |
565 | | } |
566 | 25.3k | }) |
567 | 25.3k | .map(|v| match acc.len() { |
568 | 6.00k | 0 => v, |
569 | 19.3k | _ => acc + ", " + &v, |
570 | 25.3k | }) |
571 | 25.3k | }) |
572 | 6.02k | } |
573 | | |
574 | 2.99k | pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result<BitString> { |
575 | 2.99k | BitString::from_der(i).or(Err(Err::Error(X509Error::InvalidSignatureValue))) |
576 | 2.99k | } |
577 | | |
578 | 14.5k | pub(crate) fn parse_serial(i: &[u8]) -> X509Result<(&[u8], BigUint)> { |
579 | 14.5k | let (rem, any) = Any::from_ber(i).map_err(|_| X509Error::InvalidSerial)?; |
580 | | // RFC 5280 4.1.2.2: "The serial number MUST be a positive integer" |
581 | | // however, many CAs do not respect this and send integers with MSB set, |
582 | | // so we do not use `as_biguint()` |
583 | 13.4k | any.tag() |
584 | 13.4k | .assert_eq(Tag::Integer) |
585 | 13.4k | .map_err(|_| X509Error::InvalidSerial)?; |
586 | 12.8k | let slice = any.data; |
587 | 12.8k | let big = BigUint::from_bytes_be(slice); |
588 | 12.8k | Ok((rem, (slice, big))) |
589 | 14.5k | } |
590 | | |
591 | | #[cfg(test)] |
592 | | mod tests { |
593 | | use super::*; |
594 | | |
595 | | #[test] |
596 | | fn test_x509_version() { |
597 | | // correct version |
598 | | let data: &[u8] = &[0xa0, 0x03, 0x02, 0x01, 0x00]; |
599 | | let r = X509Version::from_der_tagged_0(data); |
600 | | assert!(r.is_ok()); |
601 | | |
602 | | // wrong tag |
603 | | let data: &[u8] = &[0xa1, 0x03, 0x02, 0x01, 0x00]; |
604 | | let r = X509Version::from_der_tagged_0(data); |
605 | | assert!(r.is_ok()); |
606 | | |
607 | | // short read |
608 | | let data: &[u8] = &[0xa0, 0x01]; |
609 | | let r = X509Version::from_der_tagged_0(data); |
610 | | assert!(r.is_err()); |
611 | | |
612 | | // short read with wrong tag |
613 | | let data: &[u8] = &[0xa1, 0x01]; |
614 | | let r = X509Version::from_der_tagged_0(data); |
615 | | assert!(r.is_err()); |
616 | | } |
617 | | |
618 | | #[test] |
619 | | fn test_x509_name() { |
620 | | let name = X509Name { |
621 | | rdn_seq: vec![ |
622 | | RelativeDistinguishedName { |
623 | | set: vec![AttributeTypeAndValue { |
624 | | attr_type: oid! {2.5.4.6}, // countryName |
625 | | attr_value: Any::from_tag_and_data(Tag::PrintableString, b"FR"), |
626 | | }], |
627 | | }, |
628 | | RelativeDistinguishedName { |
629 | | set: vec![AttributeTypeAndValue { |
630 | | attr_type: oid! {2.5.4.8}, // stateOrProvinceName |
631 | | attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Some-State"), |
632 | | }], |
633 | | }, |
634 | | RelativeDistinguishedName { |
635 | | set: vec![AttributeTypeAndValue { |
636 | | attr_type: oid! {2.5.4.10}, // organizationName |
637 | | attr_value: Any::from_tag_and_data( |
638 | | Tag::PrintableString, |
639 | | b"Internet Widgits Pty Ltd", |
640 | | ), |
641 | | }], |
642 | | }, |
643 | | RelativeDistinguishedName { |
644 | | set: vec![ |
645 | | AttributeTypeAndValue { |
646 | | attr_type: oid! {2.5.4.3}, // CN |
647 | | attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test1"), |
648 | | }, |
649 | | AttributeTypeAndValue { |
650 | | attr_type: oid! {2.5.4.3}, // CN |
651 | | attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test2"), |
652 | | }, |
653 | | ], |
654 | | }, |
655 | | ], |
656 | | raw: &[], // incorrect, but enough for testing |
657 | | }; |
658 | | assert_eq!( |
659 | | name.to_string(), |
660 | | "C=FR, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Test1 + CN=Test2" |
661 | | ); |
662 | | } |
663 | | } |