Coverage Report

Created: 2026-02-14 06:42

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}