x509_cert/ext/pkix/name/
general.rs

1//! GeneralNames as defined in [RFC 5280 Section 4.2.1.6].
2
3use super::{EdiPartyName, OtherName};
4use crate::name::Name;
5
6use der::asn1::{Ia5String, ObjectIdentifier, OctetString};
7use der::{Choice, ValueOrd};
8
9/// GeneralNames as defined in [RFC 5280 Section 4.2.1.6].
10///
11/// ```text
12/// GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName
13/// ```
14///
15/// [RFC 5280 Section 4.2.1.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
16pub type GeneralNames = alloc::vec::Vec<GeneralName>;
17
18/// GeneralName as defined in [RFC 5280 Section 4.2.1.6].
19///
20/// ```text
21/// GeneralName ::= CHOICE {
22///     otherName                       [0]     OtherName,
23///     rfc822Name                      [1]     IA5String,
24///     dNSName                         [2]     IA5String,
25///     x400Address                     [3]     ORAddress,
26///     directoryName                   [4]     Name,
27///     ediPartyName                    [5]     EDIPartyName,
28///     uniformResourceIdentifier       [6]     IA5String,
29///     iPAddress                       [7]     OCTET STRING,
30///     registeredID                    [8]     OBJECT IDENTIFIER
31/// }
32/// ```
33///
34/// This implementation does not currently support the `x400Address` choice.
35///
36/// [RFC 5280 Section 4.2.1.6]: https://datatracker.ietf.org/doc/html/rfc5280#section-4.2.1.6
37#[derive(Clone, Debug, Eq, PartialEq, Choice, ValueOrd)]
38#[allow(missing_docs)]
39pub enum GeneralName {
40    #[asn1(context_specific = "0", tag_mode = "IMPLICIT", constructed = "true")]
41    OtherName(OtherName),
42
43    #[asn1(context_specific = "1", tag_mode = "IMPLICIT")]
44    Rfc822Name(Ia5String),
45
46    #[asn1(context_specific = "2", tag_mode = "IMPLICIT")]
47    DnsName(Ia5String),
48
49    #[asn1(context_specific = "4", tag_mode = "EXPLICIT", constructed = "true")]
50    DirectoryName(Name),
51
52    #[asn1(context_specific = "5", tag_mode = "IMPLICIT", constructed = "true")]
53    EdiPartyName(EdiPartyName),
54
55    #[asn1(context_specific = "6", tag_mode = "IMPLICIT")]
56    UniformResourceIdentifier(Ia5String),
57
58    #[asn1(context_specific = "7", tag_mode = "IMPLICIT")]
59    IpAddress(OctetString),
60
61    #[asn1(context_specific = "8", tag_mode = "IMPLICIT")]
62    RegisteredId(ObjectIdentifier),
63}
64
65#[cfg(feature = "std")]
66impl From<std::net::IpAddr> for GeneralName {
67    fn from(ip: std::net::IpAddr) -> Self {
68        // Safety: this is unfailable here, OctetString will issue an error if you go
69        // over 256MiB, here the buffer is at most 16 bytes (ipv6). The two `expect`s
70        // below are safe.
71        let buf = match ip {
72            std::net::IpAddr::V4(v) => {
73                let value = v.octets();
74                OctetString::new(&value[..])
75                    .expect("OctetString is not expected to fail with a 4 bytes long buffer")
76            }
77            std::net::IpAddr::V6(v) => {
78                let value = v.octets();
79                OctetString::new(&value[..])
80                    .expect("OctetString is not expected to fail with a 16 bytes long buffer")
81            }
82        };
83
84        GeneralName::IpAddress(buf)
85    }
86}
87
88#[cfg(all(feature = "std", test))]
89#[allow(clippy::unwrap_used)]
90mod tests {
91    use super::*;
92    use der::Encode;
93
94    #[test]
95    fn test_convert() {
96        use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
97
98        let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
99        let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
100
101        assert_eq!(
102            GeneralName::from(localhost_v4).to_der().unwrap(),
103            &[135, 4, 127, 0, 0, 1][..]
104        );
105        assert_eq!(
106            GeneralName::from(localhost_v6).to_der().unwrap(),
107            &[135, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1][..]
108        );
109    }
110}