use crate::error::{X509Error, X509Result}; use crate::prelude::format_serial; use crate::x509::X509Name; use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence, Tag}; use core::convert::TryFrom; use nom::combinator::all_consuming; use nom::{Err, IResult}; use std::fmt; #[derive(Clone, Debug, PartialEq)] /// Represents a GeneralName as defined in RFC5280. There /// is no support X.400 addresses and EDIPartyName. /// /// String formats are not validated (except for valid UTF-8). pub enum GeneralName<'a> { OtherName(Oid<'a>, &'a [u8]), /// More or less an e-mail, the format is not checked. RFC822Name(&'a str), /// A hostname, format is not checked. DNSName(&'a str), /// X400Address, X400Address(Any<'a>), /// RFC5280 defines several string types, we always try to parse as utf-8 /// which is more or less a superset of the string types. DirectoryName(X509Name<'a>), /// EDIPartyName EDIPartyName(Any<'a>), /// An uniform resource identifier. The format is not checked. URI(&'a str), /// An ip address, provided as encoded. IPAddress(&'a [u8]), RegisteredID(Oid<'a>), /// Invalid data (for ex. invalid UTF-8 data in DNSName entry) Invalid(Tag, &'a [u8]), } impl<'a> TryFrom> for GeneralName<'a> { type Error = Error; fn try_from(any: Any<'a>) -> Result { any.class().assert_eq(Class::ContextSpecific)?; let name = match parse_generalname_entry(any.clone()) { Ok(name) => name, Err(_) => GeneralName::Invalid(any.tag(), any.data), }; Ok(name) } } fn parse_generalname_entry( any: Any<'_>, ) -> Result, as TryFrom>>::Error> { Ok(match any.tag().0 { 0 => { // otherName SEQUENCE { OID, [0] explicit any defined by oid } let (rest, oid) = Oid::from_der(any.data)?; GeneralName::OtherName(oid, rest) } 1 => GeneralName::RFC822Name(ia5str(any)?), 2 => GeneralName::DNSName(ia5str(any)?), 3 => { // XXX Not yet implemented GeneralName::X400Address(any) } 4 => { // directoryName, name let (_, name) = all_consuming(X509Name::from_der)(any.data) .or(Err(Error::Unsupported)) // XXX remove me ?; GeneralName::DirectoryName(name) } 5 => { // XXX Not yet implemented GeneralName::EDIPartyName(any) } 6 => GeneralName::URI(ia5str(any)?), 7 => { // IPAddress, OctetString GeneralName::IPAddress(any.data) } 8 => { let oid = Oid::new(any.data.into()); GeneralName::RegisteredID(oid) } _ => return Err(Error::unexpected_tag(None, any.tag())), }) } impl CheckDerConstraints for GeneralName<'_> { fn check_constraints(any: &Any) -> asn1_rs::Result<()> { Sequence::check_constraints(any) } } impl<'a> FromDer<'a, X509Error> for GeneralName<'a> { fn from_der(i: &'a [u8]) -> X509Result<'a, Self> { parse_generalname(i).map_err(Err::convert) } } impl fmt::Display for GeneralName<'_> { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { GeneralName::OtherName(oid, _) => write!(f, "OtherName({oid}, [...])"), GeneralName::RFC822Name(s) => write!(f, "RFC822Name({s})"), GeneralName::DNSName(s) => write!(f, "DNSName({s})"), GeneralName::X400Address(_) => write!(f, "X400Address()"), GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({dn})"), GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName()"), GeneralName::URI(s) => write!(f, "URI({s})"), GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)), GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({oid})"), GeneralName::Invalid(tag, b) => { write!(f, "Invalid(tag={}, data={})", tag, format_serial(b)) } } } } fn ia5str(any: Any<'_>) -> Result<&str, Err> { // Relax constraints from RFC here: we are expecting an IA5String, but many certificates // are using unicode characters std::str::from_utf8(any.data).map_err(|_| Err::Failure(Error::BerValueError)) } pub(crate) fn parse_generalname(i: &[u8]) -> IResult<&[u8], GeneralName<'_>, Error> { let (rest, any) = Any::from_der(i)?; let gn = GeneralName::try_from(any)?; Ok((rest, gn)) }