Coverage Report

Created: 2026-08-14 07:34

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/extensions/generalname.rs
Line
Count
Source
1
use crate::error::{X509Error, X509Result};
2
use crate::prelude::format_serial;
3
use crate::x509::X509Name;
4
use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence};
5
use core::convert::TryFrom;
6
use nom::combinator::all_consuming;
7
use nom::{Err, IResult};
8
use std::fmt;
9
10
#[derive(Clone, Debug, PartialEq)]
11
/// Represents a GeneralName as defined in RFC5280. There
12
/// is no support X.400 addresses and EDIPartyName.
13
///
14
/// String formats are not validated.
15
pub enum GeneralName<'a> {
16
    OtherName(Oid<'a>, &'a [u8]),
17
    /// More or less an e-mail, the format is not checked.
18
    RFC822Name(&'a str),
19
    /// A hostname, format is not checked.
20
    DNSName(&'a str),
21
    /// X400Address,
22
    X400Address(Any<'a>),
23
    /// RFC5280 defines several string types, we always try to parse as utf-8
24
    /// which is more or less a superset of the string types.
25
    DirectoryName(X509Name<'a>),
26
    /// EDIPartyName
27
    EDIPartyName(Any<'a>),
28
    /// An uniform resource identifier. The format is not checked.
29
    URI(&'a str),
30
    /// An ip address, provided as encoded.
31
    IPAddress(&'a [u8]),
32
    RegisteredID(Oid<'a>),
33
}
34
35
impl<'a> TryFrom<Any<'a>> for GeneralName<'a> {
36
    type Error = Error;
37
38
26.5k
    fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
39
26.5k
        any.class().assert_eq(Class::ContextSpecific)?;
40
26.5k
        fn ia5str(any: Any) -> Result<&str, Err<Error>> {
41
            // Relax constraints from RFC here: we are expecting an IA5String, but many certificates
42
            // are using unicode characters
43
26.5k
            std::str::from_utf8(any.data).map_err(|_| nom::Err::Failure(Error::BerValueError))
44
26.5k
        }
45
26.5k
        let name = match any.tag().0 {
46
            0 => {
47
                // otherName SEQUENCE { OID, [0] explicit any defined by oid }
48
0
                let (rest, oid) = Oid::from_der(any.data)?;
49
0
                GeneralName::OtherName(oid, rest)
50
            }
51
0
            1 => GeneralName::RFC822Name(ia5str(any)?),
52
19.6k
            2 => GeneralName::DNSName(ia5str(any)?),
53
            3 => {
54
                // XXX Not yet implemented
55
0
                GeneralName::X400Address(any)
56
            }
57
            4 => {
58
                // directoryName, name
59
61
                let (_, name) = all_consuming(X509Name::from_der)(any.data)
60
61
                    .or(Err(Error::Unsupported)) // XXX remove me
61
0
                    ?;
62
61
                GeneralName::DirectoryName(name)
63
            }
64
            5 => {
65
                // XXX Not yet implemented
66
0
                GeneralName::EDIPartyName(any)
67
            }
68
6.83k
            6 => GeneralName::URI(ia5str(any)?),
69
            7 => {
70
                // IPAddress, OctetString
71
0
                GeneralName::IPAddress(any.data)
72
            }
73
            8 => {
74
0
                let oid = Oid::new(any.data.into());
75
0
                GeneralName::RegisteredID(oid)
76
            }
77
0
            _ => return Err(Error::unexpected_tag(None, any.tag())),
78
        };
79
26.5k
        Ok(name)
80
26.5k
    }
81
}
82
83
impl CheckDerConstraints for GeneralName<'_> {
84
0
    fn check_constraints(any: &Any) -> asn1_rs::Result<()> {
85
0
        Sequence::check_constraints(any)
86
0
    }
87
}
88
89
impl<'a> FromDer<'a, X509Error> for GeneralName<'a> {
90
0
    fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
91
0
        parse_generalname(i).map_err(Err::convert)
92
0
    }
93
}
94
95
impl<'a> fmt::Display for GeneralName<'a> {
96
54
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
97
54
        match self {
98
0
            GeneralName::OtherName(oid, _) => write!(f, "OtherName({}, [...])", oid),
99
0
            GeneralName::RFC822Name(s) => write!(f, "RFC822Name({})", s),
100
0
            GeneralName::DNSName(s) => write!(f, "DNSName({})", s),
101
0
            GeneralName::X400Address(_) => write!(f, "X400Address(<unparsed>)"),
102
54
            GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({})", dn),
103
0
            GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName(<unparsed>)"),
104
0
            GeneralName::URI(s) => write!(f, "URI({})", s),
105
0
            GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)),
106
0
            GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({})", oid),
107
        }
108
54
    }
109
}
110
111
31.5k
pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName, Error> {
112
31.5k
    let (rest, any) = Any::from_der(i)?;
113
26.5k
    let gn = GeneralName::try_from(any)?;
114
26.5k
    Ok((rest, gn))
115
31.5k
}