use crate::error::{X509Error, X509Result};
use crate::objects::*;
use data_encoding::HEXUPPER;
use der_parser::ber::{BitStringObject, MAX_OBJECT_SIZE};
use der_parser::der::*;
use der_parser::error::*;
use der_parser::num_bigint::BigUint;
use der_parser::oid::Oid;
use der_parser::*;
use nom::branch::alt;
use nom::bytes::complete::take;
use nom::combinator::{complete, map, map_opt, map_res, opt};
use nom::multi::{many0, many1};
use nom::{Err, Offset};
use oid_registry::*;
use rusticata_macros::newtype_enum;
use std::fmt;
#[derive(Debug, PartialEq, Eq, Clone, Copy)]
pub struct X509Version(pub u32);
impl X509Version {
pub(crate) fn from_der(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((i, X509Version::V1)),
}
}
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)),
}
}
}
newtype_enum! {
impl display X509Version {
V1 = 0,
V2 = 1,
V3 = 2,
}
}
#[derive(Debug, PartialEq)]
pub struct AttributeTypeAndValue<'a> {
pub attr_type: Oid<'a>,
pub attr_value: DerObject<'a>,
}
impl<'a> AttributeTypeAndValue<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
parse_der_sequence_defined_g(|i, _| {
let (i, attr_type) = map_res(parse_der_oid, |x: DerObject<'a>| x.as_oid_val())(i)
.or(Err(X509Error::InvalidX509Name))?;
let (i, attr_value) = parse_attribute_value(i).or(Err(X509Error::InvalidX509Name))?;
let attr = AttributeTypeAndValue {
attr_type,
attr_value,
};
Ok((i, attr))
})(i)
}
pub fn as_str(&self) -> Result<&'a str, X509Error> {
self.attr_value.as_str().map_err(|e| e.into())
}
pub fn as_slice(&self) -> Result<&'a [u8], X509Error> {
self.attr_value.as_slice().map_err(|e| e.into())
}
}
#[inline]
fn parse_attribute_value(i: &[u8]) -> DerResult {
alt((parse_der, parse_malformed_string))(i)
}
fn parse_malformed_string(i: &[u8]) -> DerResult {
let (rem, hdr) = der_read_element_header(i)?;
let len = hdr.len.primitive()?;
if len > MAX_OBJECT_SIZE {
return Err(nom::Err::Error(BerError::InvalidLength));
}
match hdr.tag {
DerTag::PrintableString => {
let (rem, data) = take(len as usize)(rem)?;
let s = std::str::from_utf8(data).map_err(|_| BerError::BerValueError)?;
let content = DerObjectContent::PrintableString(s);
let obj = DerObject::from_header_and_content(hdr, content);
Ok((rem, obj))
}
_ => Err(nom::Err::Error(BerError::InvalidTag)),
}
}
#[derive(Debug, PartialEq)]
pub struct RelativeDistinguishedName<'a> {
pub set: Vec<AttributeTypeAndValue<'a>>,
}
impl<'a> 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(Debug, PartialEq)]
pub struct SubjectPublicKeyInfo<'a> {
pub algorithm: AlgorithmIdentifier<'a>,
pub subject_public_key: BitStringObject<'a>,
}
impl<'a> SubjectPublicKeyInfo<'a> {
pub fn from_der(i: &'a [u8]) -> X509Result<Self> {
parse_der_sequence_defined_g(|i, _| {
let (i, algorithm) = AlgorithmIdentifier::from_der(i)?;
let (i, subject_public_key) = map_res(parse_der_bitstring, |x: DerObject<'a>| {
match x.content {
DerObjectContent::BitString(_, ref b) => Ok(b.to_owned()),
_ => Err(BerError::BerTypeError),
}
})(i)
.or(Err(X509Error::InvalidSPKI))?;
let spki = SubjectPublicKeyInfo {
algorithm,
subject_public_key,
};
Ok((i, spki))
})(i)
}
}
#[derive(Debug, PartialEq)]
pub struct AlgorithmIdentifier<'a> {
pub algorithm: Oid<'a>,
pub parameters: Option<DerObject<'a>>,
}
impl<'a> AlgorithmIdentifier<'a> {
#[allow(clippy::needless_lifetimes)]
pub fn from_der(i: &[u8]) -> X509Result<AlgorithmIdentifier> {
parse_der_sequence_defined_g(|i, _| {
let (i, algorithm) = map_res(parse_der_oid, |x| x.as_oid_val())(i)
.or(Err(X509Error::InvalidAlgorithmIdentifier))?;
let (i, parameters) =
opt(complete(parse_der))(i).or(Err(X509Error::InvalidAlgorithmIdentifier))?;
let alg = AlgorithmIdentifier {
algorithm,
parameters,
};
Ok((i, alg))
})(i)
}
}
#[derive(Debug, PartialEq)]
pub struct X509Name<'a> {
pub 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) {
Ok(o) => write!(f, "{}", o),
Err(_) => write!(f, "<X509Error: Invalid X.509 name>"),
}
}
}
impl<'a> X509Name<'a> {
pub 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)
}
pub fn as_raw(&self) -> &'a [u8] {
self.raw
}
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().map(|rdn| rdn.set.iter()).flatten()
}
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> {
self.iter_by_oid(&OID_X509_COMMON_NAME)
}
pub fn iter_country(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_COUNTRY_NAME)
}
pub fn iter_organization(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_ORGANIZATION_NAME)
}
pub fn iter_organizational_unit(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_ORGANIZATIONAL_UNIT)
}
pub fn iter_state_or_province(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_STATE_OR_PROVINCE_NAME)
}
pub fn iter_locality(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_X509_LOCALITY_NAME)
}
pub fn iter_email(&self) -> impl Iterator<Item = &AttributeTypeAndValue> {
self.iter_by_oid(&OID_PKCS9_EMAIL_ADDRESS)
}
}
#[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: &DerObject, _attr_type: &Oid) -> Result<String, X509Error> {
match attr.content {
DerObjectContent::NumericString(s)
| DerObjectContent::PrintableString(s)
| DerObjectContent::UTF8String(s)
| DerObjectContent::IA5String(s) => Ok(s.to_owned()),
_ => {
attr.as_slice()
.map(|s| HEXUPPER.encode(s))
.or(Err(X509Error::InvalidX509Name))
}
}
}
fn x509name_to_string(rdn_seq: &[RelativeDistinguishedName]) -> 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) {
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<BitStringObject> {
map_res(parse_der_bitstring, |x: DerObject| {
match x.content {
DerObjectContent::BitString(_, ref b) => Ok(b.to_owned()),
_ => Err(BerError::BerTypeError),
}
})(i)
.or(Err(Err::Error(X509Error::InvalidSignatureValue)))
}
pub(crate) fn parse_serial(i: &[u8]) -> X509Result<(&[u8], BigUint)> {
map_opt(parse_der_integer, get_serial_info)(i).map_err(|_| X509Error::InvalidSerial.into())
}
fn get_serial_info(o: DerObject) -> Option<(&[u8], BigUint)> {
let big = o.as_biguint()?;
let slice = o.as_slice().ok()?;
Some((slice, big))
}
#[cfg(test)]
mod tests {
use super::*;
use der_parser::ber::BerObjectContent;
use der_parser::oid;
#[test]
fn test_x509_name() {
let name = X509Name {
rdn_seq: vec![
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid!(2.5.4 .6),
attr_value: DerObject::from_obj(BerObjectContent::PrintableString("FR")),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid!(2.5.4 .8),
attr_value: DerObject::from_obj(BerObjectContent::PrintableString(
"Some-State",
)),
}],
},
RelativeDistinguishedName {
set: vec![AttributeTypeAndValue {
attr_type: oid!(2.5.4 .10),
attr_value: DerObject::from_obj(BerObjectContent::PrintableString(
"Internet Widgits Pty Ltd",
)),
}],
},
RelativeDistinguishedName {
set: vec![
AttributeTypeAndValue {
attr_type: oid!(2.5.4 .3),
attr_value: DerObject::from_obj(BerObjectContent::PrintableString(
"Test1",
)),
},
AttributeTypeAndValue {
attr_type: oid!(2.5.4 .3),
attr_value: DerObject::from_obj(BerObjectContent::PrintableString(
"Test2",
)),
},
],
},
],
raw: &[],
};
assert_eq!(
name.to_string(),
"C=FR, ST=Some-State, O=Internet Widgits Pty Ltd, CN=Test1 + CN=Test2"
);
}
}