Skip to main content
This is unreleased documentation for the main (development) branch of crypto-glue.

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
65impl From<core::net::IpAddr> for GeneralName {
66    fn from(ip: core::net::IpAddr) -> Self {
67        // Safety: this is unfailable here, OctetString will issue an error if you go
68        // over 256MiB, here the buffer is at most 16 bytes (ipv6). The two `expect`s
69        // below are safe.
70        let buf = match ip {
71            core::net::IpAddr::V4(v) => {
72                let value = v.octets();
73                OctetString::new(&value[..])
74                    .expect("OctetString is not expected to fail with a 4 bytes long buffer")
75            }
76            core::net::IpAddr::V6(v) => {
77                let value = v.octets();
78                OctetString::new(&value[..])
79                    .expect("OctetString is not expected to fail with a 16 bytes long buffer")
80            }
81        };
82
83        GeneralName::IpAddress(buf)
84    }
85}
86
87#[cfg(test)]
88#[allow(clippy::unwrap_used)]
89mod tests {
90    use super::*;
91    use der::Encode;
92
93    #[test]
94    fn test_convert() {
95        use core::net::{IpAddr, Ipv4Addr, Ipv6Addr};
96
97        let localhost_v4 = IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1));
98        let localhost_v6 = IpAddr::V6(Ipv6Addr::new(0, 0, 0, 0, 0, 0, 0, 1));
99
100        assert_eq!(
101            GeneralName::from(localhost_v4).to_der().unwrap(),
102            &[135, 4, 127, 0, 0, 1][..]
103        );
104        assert_eq!(
105            GeneralName::from(localhost_v6).to_der().unwrap(),
106            &[135, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1][..]
107        );
108    }
109}