use crate::error::{X509Error, X509Result};
use crate::prelude::format_serial;
use crate::x509::X509Name;
use asn1_rs::{Any, CheckDerConstraints, Class, Error, FromDer, Oid, Sequence};
use core::convert::TryFrom;
use nom::combinator::all_consuming;
use nom::{Err, IResult};
use std::fmt;
#[derive(Clone, Debug, PartialEq)]
pub enum GeneralName<'a> {
OtherName(Oid<'a>, &'a [u8]),
RFC822Name(&'a str),
DNSName(&'a str),
X400Address(Any<'a>),
DirectoryName(X509Name<'a>),
EDIPartyName(Any<'a>),
URI(&'a str),
IPAddress(&'a [u8]),
RegisteredID(Oid<'a>),
}
impl<'a> TryFrom<Any<'a>> for GeneralName<'a> {
type Error = Error;
fn try_from(any: Any<'a>) -> Result<Self, Self::Error> {
any.class().assert_eq(Class::ContextSpecific)?;
fn ia5str(any: Any) -> Result<&str, Err<Error>> {
std::str::from_utf8(any.data).map_err(|_| nom::Err::Failure(Error::BerValueError))
}
let name = match any.tag().0 {
0 => {
let (rest, oid) = Oid::from_der(any.data)?;
GeneralName::OtherName(oid, rest)
}
1 => GeneralName::RFC822Name(ia5str(any)?),
2 => GeneralName::DNSName(ia5str(any)?),
3 => {
GeneralName::X400Address(any)
}
4 => {
let (_, name) = all_consuming(X509Name::from_der)(any.data)
.or(Err(Error::Unsupported)) ?;
GeneralName::DirectoryName(name)
}
5 => {
GeneralName::EDIPartyName(any)
}
6 => GeneralName::URI(ia5str(any)?),
7 => {
GeneralName::IPAddress(any.data)
}
8 => {
let oid = Oid::new(any.data.into());
GeneralName::RegisteredID(oid)
}
_ => return Err(Error::unexpected_tag(None, any.tag())),
};
Ok(name)
}
}
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<'a> fmt::Display for GeneralName<'a> {
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(<unparsed>)"),
GeneralName::DirectoryName(dn) => write!(f, "DirectoryName({})", dn),
GeneralName::EDIPartyName(_) => write!(f, "EDIPartyName(<unparsed>)"),
GeneralName::URI(s) => write!(f, "URI({})", s),
GeneralName::IPAddress(b) => write!(f, "IPAddress({})", format_serial(b)),
GeneralName::RegisteredID(oid) => write!(f, "RegisteredID({})", oid),
}
}
}
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))
}