use crate::error::{X509Error, X509Result};
use crate::objects::*;
use crate::public_key::*;
use asn1_rs::{Any, BitString, DerSequence, FromBer, FromDer, Oid, ParseResult};
use data_encoding::HEXUPPER;
use der_parser::ber::MAX_OBJECT_SIZE;
use der_parser::der::*;
use der_parser::error::*;
use der_parser::num_bigint::BigUint;
use der_parser::*;
use nom::branch::alt;
use nom::bytes::complete::take;
use nom::combinator::{complete, map};
use nom::multi::{many0, many1};
use nom::{Err, Offset};
use oid_registry::*;
use rusticata_macros::newtype_enum;
use std::fmt;
use std::iter::FromIterator;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct X509Version(pub u32);
impl X509Version {
pub(crate) fn from_der_required(i: &[u8]) -> X509Result<X509Version> {
let (rem, hdr) =
der_read_element_header(i).or(Err(Err::Error(X509Error::InvalidVersion)))?;
match hdr.tag().0 {
0 => {
map(parse_der_u32, X509Version)(rem).or(Err(Err::Error(X509Error::InvalidVersion)))
}
_ => Ok((&rem[1..], X509Version::V1)),
}
}
}
impl<'a> FromDer<'a, X509Error> for X509Version {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
let (rem, hdr) =
der_read_element_header(i).or(Err(Err::Error(X509Error::InvalidVersion)))?;
match hdr.tag().0 {
0 => {
map(parse_der_u32, X509Version)(rem).or(Err(Err::Error(X509Error::InvalidVersion)))
}
_ => Ok((i, X509Version::V1)),
}
}
}
newtype_enum! {
impl display X509Version {
V1 = 0,
V2 = 1,
V3 = 2,
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AttributeTypeAndValue<'a> {
attr_type: Oid<'a>,
attr_value: Any<'a>, }
impl<'a> AttributeTypeAndValue<'a> {
#[inline]
pub const fn new(attr_type: Oid<'a>, attr_value: Any<'a>) -> Self {
AttributeTypeAndValue {
attr_type,
attr_value,
}
}
#[inline]
pub const fn attr_type(&self) -> &Oid {
&self.attr_type
}
#[inline]
pub const fn attr_value(&self) -> &Any {
&self.attr_value
}
#[inline]
pub fn as_str(&'a self) -> Result<&'a str, X509Error> {
match self.attr_value.tag() {
Tag::NumericString | Tag::PrintableString | Tag::Utf8String | Tag::Ia5String => {
let s = core::str::from_utf8(self.attr_value.data)
.map_err(|_| X509Error::InvalidAttributes)?;
Ok(s)
}
t => Err(X509Error::Der(Error::unexpected_tag(None, t))),
}
}
#[inline]
pub fn as_slice(&'a self) -> &'a [u8] {
self.attr_value.as_bytes()
}
}
impl<'a, 'b> core::convert::TryFrom<&'a AttributeTypeAndValue<'b>> for &'a str {
type Error = X509Error;
fn try_from(value: &'a AttributeTypeAndValue<'b>) -> Result<Self, Self::Error> {
value.attr_value.as_str().map_err(|e| e.into())
}
}
impl<'a, 'b> From<&'a AttributeTypeAndValue<'b>> for &'a [u8] {
fn from(value: &'a AttributeTypeAndValue<'b>) -> Self {
value.as_slice()
}
}
impl<'a> FromDer<'a, X509Error> for AttributeTypeAndValue<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_der_sequence_defined_g(|i, _| {
let (i, attr_type) = Oid::from_der(i).or(Err(X509Error::InvalidX509Name))?;
let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?;
let attr = AttributeTypeAndValue::new(attr_type, attr_value);
Ok((i, attr))
})(i)
}
}
#[inline]
fn parse_attribute_value(i: &[u8]) -> ParseResult<Any, Error> {
alt((Any::from_der, parse_malformed_string))(i)
}
fn parse_malformed_string(i: &[u8]) -> ParseResult<Any, Error> {
let (rem, hdr) = Header::from_der(i)?;
let len = hdr.length().definite()?;
if len > MAX_OBJECT_SIZE {
return Err(nom::Err::Error(Error::InvalidLength));
}
match hdr.tag() {
Tag::PrintableString => {
let (rem, data) = take(len as usize)(rem)?;
let _ = std::str::from_utf8(data).map_err(|_| Error::BerValueError)?;
let obj = Any::new(hdr, data);
Ok((rem, obj))
}
t => Err(nom::Err::Error(Error::unexpected_tag(
Some(Tag::PrintableString),
t,
))),
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct RelativeDistinguishedName<'a> {
set: Vec<AttributeTypeAndValue<'a>>,
}
impl<'a> RelativeDistinguishedName<'a> {
#[inline]
pub const fn new(set: Vec<AttributeTypeAndValue<'a>>) -> Self {
RelativeDistinguishedName { set }
}
pub fn iter(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.set.iter()
}
}
impl<'a> FromIterator<AttributeTypeAndValue<'a>> for RelativeDistinguishedName<'a> {
fn from_iter<T: IntoIterator<Item = AttributeTypeAndValue<'a>>>(iter: T) -> Self {
let set = iter.into_iter().collect();
RelativeDistinguishedName { set }
}
}
impl<'a> FromDer<'a, X509Error> for RelativeDistinguishedName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
parse_der_set_defined_g(|i, _| {
let (i, set) = many1(complete(AttributeTypeAndValue::from_der))(i)?;
let rdn = RelativeDistinguishedName { set };
Ok((i, rdn))
})(i)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SubjectPublicKeyInfo<'a> {
pub algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key: BitString<'a>,
pub raw: &'a [u8],
}
impl<'a> SubjectPublicKeyInfo<'a> {
pub fn parsed(&self) -> Result<PublicKey, X509Error> {
let b = &self.subject_public_key.data;
if self.algorithm.algorithm == OID_PKCS1_RSAENCRYPTION {
let (_, key) = RSAPublicKey::from_der(b).map_err(|_| X509Error::InvalidSPKI)?;
Ok(PublicKey::RSA(key))
} else if self.algorithm.algorithm == OID_KEY_TYPE_EC_PUBLIC_KEY {
let key = ECPoint::from(b.as_ref());
Ok(PublicKey::EC(key))
} else if self.algorithm.algorithm == OID_KEY_TYPE_DSA {
let s = parse_der_integer(b)
.and_then(|(_, obj)| obj.as_slice().map_err(Err::Error))
.or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::DSA(s))
} else if self.algorithm.algorithm == OID_GOST_R3410_2001 {
let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::GostR3410(s))
} else if self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_256
|| self.algorithm.algorithm == OID_KEY_TYPE_GOST_R3410_2012_512
{
let (_, s) = <&[u8]>::from_der(b).or(Err(X509Error::InvalidSPKI))?;
Ok(PublicKey::GostR3410_2012(s))
} else {
Ok(PublicKey::Unknown(b))
}
}
}
impl<'a> FromDer<'a, X509Error> for SubjectPublicKeyInfo<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, algorithm) = AlgorithmIdentifier::from_der(i)?;
let (i, subject_public_key) = BitString::from_der(i).or(Err(X509Error::InvalidSPKI))?;
let len = start_i.offset(i);
let raw = &start_i[..len];
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key,
raw,
};
Ok((i, spki))
})(i)
}
}
#[derive(Clone, Debug, PartialEq, DerSequence)]
#[error(X509Error)]
pub struct AlgorithmIdentifier<'a> {
#[map_err(|_| X509Error::InvalidAlgorithmIdentifier)]
pub algorithm: Oid<'a>,
#[optional]
pub parameters: Option<Any<'a>>,
}
impl<'a> AlgorithmIdentifier<'a> {
pub const fn new(algorithm: Oid<'a>, parameters: Option<Any<'a>>) -> Self {
Self {
algorithm,
parameters,
}
}
pub const fn oid(&'a self) -> &'a Oid {
&self.algorithm
}
pub const fn parameters(&'a self) -> Option<&'a Any> {
self.parameters.as_ref()
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct X509Name<'a> {
pub(crate) rdn_seq: Vec<RelativeDistinguishedName<'a>>,
pub(crate) raw: &'a [u8],
}
impl<'a> fmt::Display for X509Name<'a> {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
match x509name_to_string(&self.rdn_seq, oid_registry()) {
Ok(o) => write!(f, "{}", o),
Err(_) => write!(f, "<X509Error: Invalid X.509 name>"),
}
}
}
impl<'a> X509Name<'a> {
#[inline]
pub const fn new(rdn_seq: Vec<RelativeDistinguishedName<'a>>, raw: &'a [u8]) -> Self {
X509Name { rdn_seq, raw }
}
pub fn to_string_with_registry(&self, oid_registry: &OidRegistry) -> Result<String, X509Error> {
x509name_to_string(&self.rdn_seq, oid_registry)
}
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
pub fn iter(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> {
self.rdn_seq.iter()
}
pub fn iter_rdn(&self) -> impl Iterator<Item = &RelativeDistinguishedName<'a>> {
self.rdn_seq.iter()
}
pub fn iter_attributes(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.rdn_seq.iter().flat_map(|rdn| rdn.set.iter())
}
pub fn iter_by_oid(&self, oid: &Oid<'a>) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
let oid = oid.clone();
self.iter_attributes()
.filter(move |obj| obj.attr_type == oid)
}
pub fn iter_common_name(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_COMMON_NAME)
}
pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_COUNTRY_NAME)
}
pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_ORGANIZATION_NAME)
}
pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_ORGANIZATIONAL_UNIT)
}
pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_STATE_OR_PROVINCE_NAME)
}
pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_X509_LOCALITY_NAME)
}
pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue<'a>> {
self.iter_by_oid(&OID_PKCS9_EMAIL_ADDRESS)
}
}
impl<'a> FromIterator<RelativeDistinguishedName<'a>> for X509Name<'a> {
fn from_iter<T: IntoIterator<Item = RelativeDistinguishedName<'a>>>(iter: T) -> Self {
let rdn_seq = iter.into_iter().collect();
X509Name { rdn_seq, raw: &[] }
}
}
impl<'a> From<X509Name<'a>> for Vec<RelativeDistinguishedName<'a>> {
fn from(name: X509Name<'a>) -> Self {
name.rdn_seq
}
}
impl<'a> FromDer<'a, X509Error> for X509Name<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
let start_i = i;
parse_der_sequence_defined_g(move |i, _| {
let (i, rdn_seq) = many0(complete(RelativeDistinguishedName::from_der))(i)?;
let len = start_i.offset(i);
let name = X509Name {
rdn_seq,
raw: &start_i[..len],
};
Ok((i, name))
})(i)
}
}
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct ReasonCode(pub u8);
newtype_enum! {
impl display ReasonCode {
Unspecified = 0,
KeyCompromise = 1,
CACompromise = 2,
AffiliationChanged = 3,
Superseded = 4,
CessationOfOperation = 5,
CertificateHold = 6,
RemoveFromCRL = 8,
PrivilegeWithdrawn = 9,
AACompromise = 10,
}
}
impl Default for ReasonCode {
fn default() -> Self {
ReasonCode::Unspecified
}
}
fn attribute_value_to_string(attr: &Any, _attr_type: &Oid) -> Result<String, X509Error> {
match attr.tag() {
Tag::NumericString
| Tag::BmpString
| Tag::VisibleString
| Tag::PrintableString
| Tag::GeneralString
| Tag::ObjectDescriptor
| Tag::GraphicString
| Tag::T61String
| Tag::VideotexString
| Tag::Utf8String
| Tag::Ia5String => {
let s = core::str::from_utf8(attr.data).map_err(|_| X509Error::InvalidAttributes)?;
Ok(s.to_owned())
}
_ => {
Ok(HEXUPPER.encode(attr.as_bytes()))
}
}
}
fn x509name_to_string(
rdn_seq: &[RelativeDistinguishedName],
oid_registry: &OidRegistry,
) -> Result<String, X509Error> {
rdn_seq.iter().fold(Ok(String::new()), |acc, rdn| {
acc.and_then(|mut _vec| {
rdn.set
.iter()
.fold(Ok(String::new()), |acc2, attr| {
acc2.and_then(|mut _vec2| {
let val_str = attribute_value_to_string(&attr.attr_value, &attr.attr_type)?;
let abbrev = match oid2abbrev(&attr.attr_type, oid_registry) {
Ok(s) => String::from(s),
_ => format!("{:?}", attr.attr_type),
};
let rdn = format!("{}={}", abbrev, val_str);
match _vec2.len() {
0 => Ok(rdn),
_ => Ok(_vec2 + " + " + &rdn),
}
})
})
.map(|v| match _vec.len() {
0 => v,
_ => _vec + ", " + &v,
})
})
})
}
pub(crate) fn parse_signature_value(i: &[u8]) -> X509Result<BitString> {
BitString::from_der(i).or(Err(Err::Error(X509Error::InvalidSignatureValue)))
}
pub(crate) fn parse_serial(i: &[u8]) -> X509Result<(&[u8], BigUint)> {
let (rem, any) = Any::from_ber(i).map_err(|_| X509Error::InvalidSerial)?;
any.tag()
.assert_eq(Tag::Integer)
.map_err(|_| X509Error::InvalidSerial)?;
let slice = any.data;
let big = BigUint::from_bytes_be(slice);
Ok((rem, (slice, big)))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_x509_name() {
let name = X509Name {
rdn_seq: vec![
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.6}, attr_value: Any::from_tag_and_data(Tag::PrintableString, b"FR"),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.8}, attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Some-State"),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid! {2.5.4.10}, attr_value: Any::from_tag_and_data(
Tag::PrintableString,
b"Internet Widgits Pty Ltd",
),
}],
},
RelativeDistinguishedName {
set: vec![
AttributeTypeAndValue {
attr_type: oid! {2.5.4.3}, attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test1"),
},
AttributeTypeAndValue {
attr_type: oid! {2.5.4.3}, attr_value: Any::from_tag_and_data(Tag::PrintableString, b"Test2"),
},
],
},
],
raw: &[], };
assert_eq!(
name.to_string(),
"C=FR, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Test1 + CN=Test2"
);
}
}