use crate::error::{X509Error, X509Result};
use crate::time::ASN1Time;
use crate::utils::format_serial;
use crate::x509::{ReasonCode, RelativeDistinguishedName};
use asn1_rs::FromDer;
use der_parser::ber::parse_ber_bool;
use der_parser::der::*;
use der_parser::error::{BerError, BerResult};
use der_parser::num_bigint::BigUint;
use der_parser::oid::Oid;
use nom::combinator::{all_consuming, complete, cut, map, map_res, opt};
use nom::multi::{many0, many1};
use nom::{Err, IResult, Parser};
use oid_registry::*;
use std::collections::HashMap;
use std::fmt::{self, LowerHex};
mod generalname;
mod keyusage;
mod nameconstraints;
mod policymappings;
mod sct;
pub use generalname::*;
pub use keyusage::*;
pub use nameconstraints::*;
pub use policymappings::*;
pub use sct::*;
#[derive(Clone, Debug, PartialEq)]
pub struct X509Extension<'a> {
pub oid: Oid<'a>,
pub critical: bool,
pub value: &'a [u8],
pub(crate) parsed_extension: ParsedExtension<'a>,
}
impl<'a> X509Extension<'a> {
#[inline]
pub const fn new(
oid: Oid<'a>,
critical: bool,
value: &'a [u8],
parsed_extension: ParsedExtension<'a>,
) -> X509Extension<'a> {
X509Extension {
oid,
critical,
value,
parsed_extension,
}
}
#[inline]
pub fn parsed_extension(&self) -> &ParsedExtension<'a> {
&self.parsed_extension
}
}
impl<'a> FromDer<'a, X509Error> for X509Extension<'a> {
fn from_der(i: &'a [u8]) -> X509Result<Self> {
X509ExtensionParser::new().parse(i)
}
}
#[derive(Clone, Copy, Debug)]
pub struct X509ExtensionParser {
deep_parse_extensions: bool,
}
impl X509ExtensionParser {
#[inline]
pub const fn new() -> Self {
X509ExtensionParser {
deep_parse_extensions: true,
}
}
#[inline]
pub const fn with_deep_parse_extensions(self, deep_parse_extensions: bool) -> Self {
X509ExtensionParser {
deep_parse_extensions,
}
}
}
impl<'a> Parser<&'a [u8], X509Extension<'a>, X509Error> for X509ExtensionParser {
fn parse(&mut self, input: &'a [u8]) -> IResult<&'a [u8], X509Extension<'a>, X509Error> {
parse_der_sequence_defined_g(|i, _| {
let (i, oid) = Oid::from_der(i)?;
let (i, critical) = der_read_critical(i)?;
let (i, value) = <&[u8]>::from_der(i)?;
let (i, parsed_extension) = if self.deep_parse_extensions {
parser::parse_extension(i, value, &oid)?
} else {
(&[] as &[_], ParsedExtension::Unparsed)
};
let ext = X509Extension {
oid,
critical,
value,
parsed_extension,
};
Ok((i, ext))
})(input)
.map_err(|_| X509Error::InvalidExtensions.into())
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum ParsedExtension<'a> {
UnsupportedExtension {
oid: Oid<'a>,
},
ParseError {
error: Err<BerError>,
},
AuthorityKeyIdentifier(AuthorityKeyIdentifier<'a>),
SubjectKeyIdentifier(KeyIdentifier<'a>),
KeyUsage(KeyUsage),
CertificatePolicies(CertificatePolicies<'a>),
PolicyMappings(PolicyMappings<'a>),
SubjectAlternativeName(SubjectAlternativeName<'a>),
IssuerAlternativeName(IssuerAlternativeName<'a>),
BasicConstraints(BasicConstraints),
NameConstraints(NameConstraints<'a>),
PolicyConstraints(PolicyConstraints),
ExtendedKeyUsage(ExtendedKeyUsage<'a>),
CRLDistributionPoints(CRLDistributionPoints<'a>),
InhibitAnyPolicy(InhibitAnyPolicy),
AuthorityInfoAccess(AuthorityInfoAccess<'a>),
NSCertType(NSCertType),
NsCertComment(&'a str),
IssuingDistributionPoint(IssuingDistributionPoint<'a>),
CRLNumber(BigUint),
ReasonCode(ReasonCode),
InvalidityDate(ASN1Time),
SCT(Vec<SignedCertificateTimestamp<'a>>),
Unparsed,
}
impl<'a> ParsedExtension<'a> {
pub fn unsupported(&self) -> bool {
matches!(self, &ParsedExtension::UnsupportedExtension { .. })
}
pub fn error(&self) -> Option<&Err<BerError>> {
match self {
ParsedExtension::ParseError { error } => Some(error),
_ => None,
}
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AuthorityKeyIdentifier<'a> {
pub key_identifier: Option<KeyIdentifier<'a>>,
pub authority_cert_issuer: Option<Vec<GeneralName<'a>>>,
pub authority_cert_serial: Option<&'a [u8]>,
}
impl<'a> FromDer<'a, X509Error> for AuthorityKeyIdentifier<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_authoritykeyidentifier(i).map_err(Err::convert)
}
}
pub type CertificatePolicies<'a> = Vec<PolicyInformation<'a>>;
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PolicyInformation<'a> {
pub policy_id: Oid<'a>,
pub policy_qualifiers: Option<Vec<PolicyQualifierInfo<'a>>>,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PolicyQualifierInfo<'a> {
pub policy_qualifier_id: Oid<'a>,
pub qualifier: &'a [u8],
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct BasicConstraints {
pub ca: bool,
pub path_len_constraint: Option<u32>,
}
impl<'a> FromDer<'a, X509Error> for BasicConstraints {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_basicconstraints(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct KeyIdentifier<'a>(pub &'a [u8]);
impl<'a> FromDer<'a, X509Error> for KeyIdentifier<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_keyidentifier(i).map_err(Err::convert)
}
}
impl<'a> LowerHex for KeyIdentifier<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let s = format_serial(self.0);
f.write_str(&s)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct NSCertType(u8);
impl NSCertType {
pub fn ssl_client(&self) -> bool {
self.0 & 0x1 == 1
}
pub fn ssl_server(&self) -> bool {
(self.0 >> 1) & 1 == 1
}
pub fn smime(&self) -> bool {
(self.0 >> 2) & 1 == 1
}
pub fn object_signing(&self) -> bool {
(self.0 >> 3) & 1 == 1
}
pub fn ssl_ca(&self) -> bool {
(self.0 >> 5) & 1 == 1
}
pub fn smime_ca(&self) -> bool {
(self.0 >> 6) & 1 == 1
}
pub fn object_signing_ca(&self) -> bool {
(self.0 >> 7) & 1 == 1
}
}
const NS_CERT_TYPE_FLAGS: &[&str] = &[
"SSL CLient",
"SSL Server",
"S/MIME",
"Object Signing",
"Reserved",
"SSL CA",
"S/MIME CA",
"Object Signing CA",
];
impl fmt::Display for NSCertType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = String::new();
let mut acc = self.0;
for flag_text in NS_CERT_TYPE_FLAGS {
if acc & 1 != 0 {
s = s + flag_text + ", ";
}
acc >>= 1;
}
s.pop();
s.pop();
f.write_str(&s)
}
}
impl<'a> FromDer<'a, X509Error> for NSCertType {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_nscerttype(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AuthorityInfoAccess<'a> {
pub accessdescs: Vec<AccessDescription<'a>>,
}
impl<'a> AuthorityInfoAccess<'a> {
pub fn iter(&self) -> impl Iterator<Item = &AccessDescription<'a>> {
self.accessdescs.iter()
}
pub fn as_hashmap(&self) -> HashMap<Oid<'a>, Vec<&GeneralName<'a>>> {
let mut m: HashMap<Oid, Vec<&GeneralName>> = HashMap::new();
for desc in &self.accessdescs {
let AccessDescription {
access_method: oid,
access_location: gn,
} = desc;
if let Some(general_names) = m.get_mut(oid) {
general_names.push(gn);
} else {
m.insert(oid.clone(), vec![gn]);
}
}
m
}
pub fn into_hashmap(self) -> HashMap<Oid<'a>, Vec<GeneralName<'a>>> {
let mut aia_list = self.accessdescs;
let mut m: HashMap<Oid, Vec<GeneralName>> = HashMap::new();
for desc in aia_list.drain(..) {
let AccessDescription {
access_method: oid,
access_location: gn,
} = desc;
if let Some(general_names) = m.get_mut(&oid) {
general_names.push(gn);
} else {
m.insert(oid, vec![gn]);
}
}
m
}
}
impl<'a> FromDer<'a, X509Error> for AuthorityInfoAccess<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_authorityinfoaccess(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct AccessDescription<'a> {
pub access_method: Oid<'a>,
pub access_location: GeneralName<'a>,
}
impl<'a> AccessDescription<'a> {
pub const fn new(access_method: Oid<'a>, access_location: GeneralName<'a>) -> Self {
AccessDescription {
access_method,
access_location,
}
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct InhibitAnyPolicy {
pub skip_certs: u32,
}
impl<'a> FromDer<'a, X509Error> for InhibitAnyPolicy {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
map(parse_der_u32, |skip_certs| InhibitAnyPolicy { skip_certs })(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct PolicyConstraints {
pub require_explicit_policy: Option<u32>,
pub inhibit_policy_mapping: Option<u32>,
}
impl<'a> FromDer<'a, X509Error> for PolicyConstraints {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_policyconstraints(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct SubjectAlternativeName<'a> {
pub general_names: Vec<GeneralName<'a>>,
}
impl<'a> FromDer<'a, X509Error> for SubjectAlternativeName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_der_sequence_defined_g(|input, _| {
let (i, general_names) =
all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?;
Ok((i, SubjectAlternativeName { general_names }))
})(i)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct IssuerAlternativeName<'a> {
pub general_names: Vec<GeneralName<'a>>,
}
impl<'a> FromDer<'a, X509Error> for IssuerAlternativeName<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parse_der_sequence_defined_g(|input, _| {
let (i, general_names) =
all_consuming(many0(complete(cut(GeneralName::from_der))))(input)?;
Ok((i, IssuerAlternativeName { general_names }))
})(i)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CRLDistributionPoints<'a> {
pub points: Vec<CRLDistributionPoint<'a>>,
}
impl<'a> std::ops::Deref for CRLDistributionPoints<'a> {
type Target = Vec<CRLDistributionPoint<'a>>;
fn deref(&self) -> &Self::Target {
&self.points
}
}
impl<'a> FromDer<'a, X509Error> for CRLDistributionPoints<'a> {
fn from_der(i: &'a [u8]) -> X509Result<'a, Self> {
parser::parse_crldistributionpoints(i).map_err(Err::convert)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct CRLDistributionPoint<'a> {
pub distribution_point: Option<DistributionPointName<'a>>,
pub reasons: Option<ReasonFlags>,
pub crl_issuer: Option<Vec<GeneralName<'a>>>,
}
#[derive(Clone, Debug, PartialEq)]
pub enum DistributionPointName<'a> {
FullName(Vec<GeneralName<'a>>),
NameRelativeToCRLIssuer(RelativeDistinguishedName<'a>),
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct ReasonFlags {
pub flags: u16,
}
impl ReasonFlags {
pub fn key_compromise(&self) -> bool {
(self.flags >> 1) & 1 == 1
}
pub fn ca_compromise(&self) -> bool {
(self.flags >> 2) & 1 == 1
}
pub fn affilation_changed(&self) -> bool {
(self.flags >> 3) & 1 == 1
}
pub fn superseded(&self) -> bool {
(self.flags >> 4) & 1 == 1
}
pub fn cessation_of_operation(&self) -> bool {
(self.flags >> 5) & 1 == 1
}
pub fn certificate_hold(&self) -> bool {
(self.flags >> 6) & 1 == 1
}
pub fn privelege_withdrawn(&self) -> bool {
(self.flags >> 7) & 1 == 1
}
pub fn aa_compromise(&self) -> bool {
(self.flags >> 8) & 1 == 1
}
}
const REASON_FLAGS: &[&str] = &[
"Unused",
"Key Compromise",
"CA Compromise",
"Affiliation Changed",
"Superseded",
"Cessation Of Operation",
"Certificate Hold",
"Privilege Withdrawn",
"AA Compromise",
];
impl fmt::Display for ReasonFlags {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut s = String::new();
let mut acc = self.flags;
for flag_text in REASON_FLAGS {
if acc & 1 != 0 {
s = s + flag_text + ", ";
}
acc >>= 1;
}
s.pop();
s.pop();
f.write_str(&s)
}
}
#[derive(Clone, Debug, PartialEq)]
pub struct IssuingDistributionPoint<'a> {
pub distribution_point: Option<DistributionPointName<'a>>,
pub only_contains_user_certs: bool,
pub only_contains_ca_certs: bool,
pub only_some_reasons: Option<ReasonFlags>,
pub indirect_crl: bool,
pub only_contains_attribute_certs: bool,
}
pub(crate) mod parser {
use crate::extensions::*;
use crate::time::ASN1Time;
use asn1_rs::{GeneralizedTime, ParseResult};
use der_parser::ber::BerObject;
use der_parser::error::BerError;
use der_parser::{oid::Oid, *};
use lazy_static::lazy_static;
use nom::combinator::{cut, map};
use nom::{Err, IResult};
type ExtParser = fn(&[u8]) -> IResult<&[u8], ParsedExtension, BerError>;
lazy_static! {
static ref EXTENSION_PARSERS: HashMap<Oid<'static>, ExtParser> = {
macro_rules! add {
($m:ident, $oid:ident, $p:ident) => {
$m.insert($oid, $p as ExtParser);
};
}
let mut m = HashMap::new();
add!(
m,
OID_X509_EXT_SUBJECT_KEY_IDENTIFIER,
parse_keyidentifier_ext
);
add!(m, OID_X509_EXT_KEY_USAGE, parse_keyusage_ext);
add!(
m,
OID_X509_EXT_SUBJECT_ALT_NAME,
parse_subjectalternativename_ext
);
add!(
m,
OID_X509_EXT_ISSUER_ALT_NAME,
parse_issueralternativename_ext
);
add!(
m,
OID_X509_EXT_BASIC_CONSTRAINTS,
parse_basicconstraints_ext
);
add!(m, OID_X509_EXT_NAME_CONSTRAINTS, parse_nameconstraints_ext);
add!(
m,
OID_X509_EXT_CERTIFICATE_POLICIES,
parse_certificatepolicies_ext
);
add!(m, OID_X509_EXT_POLICY_MAPPINGS, parse_policymappings_ext);
add!(
m,
OID_X509_EXT_POLICY_CONSTRAINTS,
parse_policyconstraints_ext
);
add!(
m,
OID_X509_EXT_EXTENDED_KEY_USAGE,
parse_extendedkeyusage_ext
);
add!(
m,
OID_X509_EXT_CRL_DISTRIBUTION_POINTS,
parse_crldistributionpoints_ext
);
add!(
m,
OID_X509_EXT_INHIBITANT_ANY_POLICY,
parse_inhibitanypolicy_ext
);
add!(
m,
OID_PKIX_AUTHORITY_INFO_ACCESS,
parse_authorityinfoaccess_ext
);
add!(
m,
OID_X509_EXT_AUTHORITY_KEY_IDENTIFIER,
parse_authoritykeyidentifier_ext
);
add!(m, OID_CT_LIST_SCT, parse_sct_ext);
add!(m, OID_X509_EXT_CERT_TYPE, parse_nscerttype_ext);
add!(m, OID_X509_EXT_CERT_COMMENT, parse_nscomment_ext);
add!(m, OID_X509_EXT_CRL_NUMBER, parse_crl_number);
add!(m, OID_X509_EXT_REASON_CODE, parse_reason_code);
add!(m, OID_X509_EXT_INVALIDITY_DATE, parse_invalidity_date);
add!(
m,
OID_X509_EXT_ISSUER_DISTRIBUTION_POINT,
parse_issuingdistributionpoint_ext
);
m
};
}
fn parse_extension0<'a>(
orig_i: &'a [u8],
i: &'a [u8],
oid: &Oid,
) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
if let Some(parser) = EXTENSION_PARSERS.get(oid) {
match parser(i) {
Ok((_, ext)) => Ok((orig_i, ext)),
Err(error) => Ok((orig_i, ParsedExtension::ParseError { error })),
}
} else {
Ok((
orig_i,
ParsedExtension::UnsupportedExtension {
oid: oid.to_owned(),
},
))
}
}
pub(crate) fn parse_extension<'a>(
orig_i: &'a [u8],
i: &'a [u8],
oid: &Oid,
) -> IResult<&'a [u8], ParsedExtension<'a>, BerError> {
parse_extension0(orig_i, i, oid)
}
pub(super) fn parse_basicconstraints(i: &[u8]) -> IResult<&[u8], BasicConstraints, BerError> {
let (rem, obj) = parse_der_sequence(i)?;
if let Ok(seq) = obj.as_sequence() {
let (ca, path_len_constraint) = match seq.len() {
0 => (false, None),
1 => {
if let Ok(b) = seq[0].as_bool() {
(b, None)
} else if let Ok(u) = seq[0].as_u32() {
(false, Some(u))
} else {
return Err(nom::Err::Error(BerError::InvalidTag));
}
}
2 => {
let ca = seq[0]
.as_bool()
.or(Err(nom::Err::Error(BerError::InvalidLength)))?;
let pl = seq[1]
.as_u32()
.or(Err(nom::Err::Error(BerError::InvalidLength)))?;
(ca, Some(pl))
}
_ => return Err(nom::Err::Error(BerError::InvalidLength)),
};
Ok((
rem,
BasicConstraints {
ca,
path_len_constraint,
},
))
} else {
Err(nom::Err::Error(BerError::InvalidLength))
}
}
fn parse_basicconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_basicconstraints, ParsedExtension::BasicConstraints)(i)
}
fn parse_nameconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_nameconstraints, ParsedExtension::NameConstraints)(i)
}
pub(super) fn parse_subjectalternativename_ext(
i: &[u8],
) -> IResult<&[u8], ParsedExtension, BerError> {
parse_der_sequence_defined_g(|input, _| {
let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?;
Ok((
i,
ParsedExtension::SubjectAlternativeName(SubjectAlternativeName { general_names }),
))
})(i)
}
pub(super) fn parse_issueralternativename_ext(
i: &[u8],
) -> IResult<&[u8], ParsedExtension, BerError> {
parse_der_sequence_defined_g(|input, _| {
let (i, general_names) = all_consuming(many0(complete(cut(parse_generalname))))(input)?;
Ok((
i,
ParsedExtension::IssuerAlternativeName(IssuerAlternativeName { general_names }),
))
})(i)
}
pub(super) fn parse_policyconstraints(i: &[u8]) -> IResult<&[u8], PolicyConstraints, BerError> {
parse_der_sequence_defined_g(|input, _| {
let (i, require_explicit_policy) = opt(complete(map_res(
parse_der_tagged_implicit(0, parse_der_content(Tag::Integer)),
|x| x.as_u32(),
)))(input)?;
let (i, inhibit_policy_mapping) = all_consuming(opt(complete(map_res(
parse_der_tagged_implicit(1, parse_der_content(Tag::Integer)),
|x| x.as_u32(),
))))(i)?;
let policy_constraint = PolicyConstraints {
require_explicit_policy,
inhibit_policy_mapping,
};
Ok((i, policy_constraint))
})(i)
}
fn parse_policyconstraints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_policyconstraints, ParsedExtension::PolicyConstraints)(i)
}
fn parse_policymappings_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_policymappings, ParsedExtension::PolicyMappings)(i)
}
fn parse_inhibitanypolicy_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (ret, skip_certs) = parse_der_u32(i)?;
Ok((
ret,
ParsedExtension::InhibitAnyPolicy(InhibitAnyPolicy { skip_certs }),
))
}
fn parse_extendedkeyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_extendedkeyusage, ParsedExtension::ExtendedKeyUsage)(i)
}
fn parse_distributionpointname(i: &[u8]) -> IResult<&[u8], DistributionPointName, BerError> {
let (rem, header) = der_read_element_header(i)?;
match header.tag().0 {
0 => {
let (rem, names) = many1(complete(parse_generalname))(rem)?;
Ok((rem, DistributionPointName::FullName(names)))
}
1 => {
let (rem, rdn) = RelativeDistinguishedName::from_der(rem)
.map_err(|_| BerError::BerValueError)?;
Ok((rem, DistributionPointName::NameRelativeToCRLIssuer(rdn)))
}
_ => Err(Err::Error(BerError::InvalidTag)),
}
}
fn parse_implicit_tagged_reasons(tag: u32) -> impl Fn(&[u8]) -> BerResult<ReasonFlags> {
move |i: &[u8]| {
let (rem, obj) = parse_der_tagged_implicit(tag, parse_der_content(Tag::BitString))(i)?;
parse_reasons(rem, obj)
}
}
fn parse_reasons<'a>(rem: &'a [u8], obj: BerObject<'a>) -> BerResult<'a, ReasonFlags> {
if let DerObjectContent::BitString(_, b) = obj.content {
let flags = b
.data
.iter()
.rev()
.fold(0, |acc, x| acc << 8 | (x.reverse_bits() as u16));
Ok((rem, ReasonFlags { flags }))
} else {
Err(nom::Err::Failure(BerError::InvalidTag))
}
}
fn parse_crlissuer_content(i: &[u8]) -> BerResult<Vec<GeneralName>> {
many1(complete(parse_generalname))(i)
}
pub(super) fn parse_crldistributionpoint(
i: &[u8],
) -> IResult<&[u8], CRLDistributionPoint, BerError> {
parse_der_sequence_defined_g(|content, _| {
let (rem, distribution_point) =
opt(complete(parse_der_tagged_explicit_g(0, |b, _| {
parse_distributionpointname(b)
})))(content)?;
let (rem, reasons) = opt(complete(parse_implicit_tagged_reasons(1)))(rem)?;
let (rem, crl_issuer) = opt(complete(parse_der_tagged_implicit_g(2, |i, _, _| {
parse_crlissuer_content(i)
})))(rem)?;
let crl_dp = CRLDistributionPoint {
distribution_point,
reasons,
crl_issuer,
};
Ok((rem, crl_dp))
})(i)
}
pub(super) fn parse_crldistributionpoints(
i: &[u8],
) -> IResult<&[u8], CRLDistributionPoints, BerError> {
let (ret, crldps) = parse_der_sequence_of_v(parse_crldistributionpoint)(i)?;
Ok((ret, CRLDistributionPoints { points: crldps }))
}
fn parse_crldistributionpoints_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_crldistributionpoints,
ParsedExtension::CRLDistributionPoints,
)(i)
}
pub(super) fn parse_issuingdistributionpoint(
i: &[u8],
) -> IResult<&[u8], IssuingDistributionPoint, BerError> {
parse_der_sequence_defined_g(|content, _| {
let parse_tagged_bool = |tag: u32, rem| -> IResult<&[u8], bool, BerError> {
let (rem, value) = opt(complete(|_| {
parse_der_implicit(rem, tag, parse_der_content(Tag::Boolean))
.map(|(res, ob)| (res, ob.as_bool().unwrap_or(false)))
}))(rem)?;
Ok((rem, value.unwrap_or_default()))
};
let (rem, distribution_point) =
opt(complete(parse_der_tagged_explicit_g(0, |b, _| {
parse_distributionpointname(b)
})))(content)?;
let (rem, only_contains_user_certs) = parse_tagged_bool(1, rem)?;
let (rem, only_contains_ca_certs) = parse_tagged_bool(2, rem)?;
let (rem, only_some_reasons) = opt(complete(parse_implicit_tagged_reasons(3)))(rem)?;
let (rem, indirect_crl) = parse_tagged_bool(4, rem)?;
let (rem, only_contains_attribute_certs) = parse_tagged_bool(5, rem)?;
let crl_idp = IssuingDistributionPoint {
distribution_point,
only_contains_user_certs,
only_contains_ca_certs,
only_some_reasons,
indirect_crl,
only_contains_attribute_certs,
};
Ok((rem, crl_idp))
})(i)
}
fn parse_issuingdistributionpoint_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_issuingdistributionpoint,
ParsedExtension::IssuingDistributionPoint,
)(i)
}
pub(super) fn parse_authorityinfoaccess(
i: &[u8],
) -> IResult<&[u8], AuthorityInfoAccess, BerError> {
fn parse_aia(i: &[u8]) -> IResult<&[u8], AccessDescription, BerError> {
parse_der_sequence_defined_g(|content, _| {
let (gn, oid) = Oid::from_der(content)?;
let (rest, gn) = parse_generalname(gn)?;
Ok((rest, AccessDescription::new(oid, gn)))
})(i)
}
let (ret, accessdescs) = parse_der_sequence_of_v(parse_aia)(i)?;
Ok((ret, AuthorityInfoAccess { accessdescs }))
}
fn parse_authorityinfoaccess_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_authorityinfoaccess,
ParsedExtension::AuthorityInfoAccess,
)(i)
}
fn parse_aki_content<'a>(
i: &'a [u8],
_hdr: Header<'_>,
) -> IResult<&'a [u8], AuthorityKeyIdentifier<'a>, BerError> {
let (i, key_identifier) = opt(complete(parse_der_tagged_implicit_g(0, |d, _, _| {
Ok((&[], KeyIdentifier(d)))
})))(i)?;
let (i, authority_cert_issuer) =
opt(complete(parse_der_tagged_implicit_g(1, |d, _, _| {
many0(complete(parse_generalname))(d)
})))(i)?;
let (i, authority_cert_serial) = opt(complete(parse_der_tagged_implicit(
2,
parse_der_content(Tag::Integer),
)))(i)?;
let authority_cert_serial = authority_cert_serial.and_then(|o| o.as_slice().ok());
let aki = AuthorityKeyIdentifier {
key_identifier,
authority_cert_issuer,
authority_cert_serial,
};
Ok((i, aki))
}
pub(super) fn parse_authoritykeyidentifier(
i: &[u8],
) -> IResult<&[u8], AuthorityKeyIdentifier, BerError> {
let (rem, aki) = parse_der_sequence_defined_g(parse_aki_content)(i)?;
Ok((rem, aki))
}
fn parse_authoritykeyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_authoritykeyidentifier,
ParsedExtension::AuthorityKeyIdentifier,
)(i)
}
pub(super) fn parse_keyidentifier(i: &[u8]) -> IResult<&[u8], KeyIdentifier, BerError> {
let (rest, id) = <&[u8]>::from_der(i)?;
let ki = KeyIdentifier(id);
Ok((rest, ki))
}
fn parse_keyidentifier_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_keyidentifier, ParsedExtension::SubjectKeyIdentifier)(i)
}
fn parse_keyusage_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_keyusage, ParsedExtension::KeyUsage)(i)
}
pub(super) fn parse_nscerttype(i: &[u8]) -> IResult<&[u8], NSCertType, BerError> {
let (rest, obj) = parse_der_bitstring(i)?;
let bitstring = obj
.content
.as_bitstring()
.or(Err(Err::Error(BerError::BerTypeError)))?;
if bitstring.data.len() != 1 {
return Err(Err::Error(BerError::BerValueError));
}
let flags = bitstring.data[0].reverse_bits();
Ok((rest, NSCertType(flags)))
}
fn parse_nscerttype_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(parse_nscerttype, ParsedExtension::NSCertType)(i)
}
fn parse_nscomment_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
match parse_der_ia5string(i) {
Ok((i, obj)) => {
let s = obj.as_str()?;
Ok((i, ParsedExtension::NsCertComment(s)))
}
Err(e) => {
if let Ok(s) = std::str::from_utf8(i) {
Ok((&[], ParsedExtension::NsCertComment(s)))
} else {
Err(e)
}
}
}
}
pub(super) fn parse_certificatepolicies(
i: &[u8],
) -> IResult<&[u8], Vec<PolicyInformation>, BerError> {
fn parse_policy_qualifier_info(i: &[u8]) -> IResult<&[u8], PolicyQualifierInfo, BerError> {
parse_der_sequence_defined_g(|content, _| {
let (rem, policy_qualifier_id) = Oid::from_der(content)?;
let info = PolicyQualifierInfo {
policy_qualifier_id,
qualifier: rem,
};
Ok((&[], info))
})(i)
}
fn parse_policy_information(i: &[u8]) -> IResult<&[u8], PolicyInformation, BerError> {
parse_der_sequence_defined_g(|content, _| {
let (rem, policy_id) = Oid::from_der(content)?;
let (rem, policy_qualifiers) =
opt(complete(parse_der_sequence_defined_g(|content, _| {
many1(complete(parse_policy_qualifier_info))(content)
})))(rem)?;
let info = PolicyInformation {
policy_id,
policy_qualifiers,
};
Ok((rem, info))
})(i)
}
parse_der_sequence_of_v(parse_policy_information)(i)
}
fn parse_certificatepolicies_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_certificatepolicies,
ParsedExtension::CertificatePolicies,
)(i)
}
fn parse_reason_code(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rest, obj) = parse_der_enum(i)?;
let code = obj
.content
.as_u32()
.or(Err(Err::Error(BerError::BerValueError)))?;
if code > 10 {
return Err(Err::Error(BerError::BerValueError));
}
let ret = ParsedExtension::ReasonCode(ReasonCode(code as u8));
Ok((rest, ret))
}
fn parse_invalidity_date(i: &[u8]) -> ParseResult<ParsedExtension> {
let (rest, t) = GeneralizedTime::from_der(i)?;
let dt = t.utc_datetime()?;
Ok((rest, ParsedExtension::InvalidityDate(ASN1Time::new(dt))))
}
fn parse_crl_number(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
let (rest, num) = map_res(parse_der_integer, |obj| obj.as_biguint())(i)?;
Ok((rest, ParsedExtension::CRLNumber(num)))
}
fn parse_sct_ext(i: &[u8]) -> IResult<&[u8], ParsedExtension, BerError> {
map(
parse_ct_signed_certificate_timestamp_list,
ParsedExtension::SCT,
)(i)
}
}
pub(crate) fn parse_extension_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> {
parse_der_sequence_defined_g(|a, _| all_consuming(many0(complete(X509Extension::from_der)))(a))(
i,
)
}
pub(crate) fn parse_extensions(i: &[u8], explicit_tag: Tag) -> X509Result<Vec<X509Extension>> {
if i.is_empty() {
return Ok((i, Vec::new()));
}
match der_read_element_header(i) {
Ok((rem, hdr)) => {
if hdr.tag() != explicit_tag {
return Err(Err::Error(X509Error::InvalidExtensions));
}
all_consuming(parse_extension_sequence)(rem)
}
Err(_) => Err(X509Error::InvalidExtensions.into()),
}
}
pub(crate) fn parse_extension_envelope_sequence(i: &[u8]) -> X509Result<Vec<X509Extension>> {
let parser = X509ExtensionParser::new().with_deep_parse_extensions(false);
parse_der_sequence_defined_g(move |a, _| all_consuming(many0(complete(parser)))(a))(i)
}
pub(crate) fn parse_extensions_envelope(
i: &[u8],
explicit_tag: Tag,
) -> X509Result<Vec<X509Extension>> {
if i.is_empty() {
return Ok((i, Vec::new()));
}
match der_read_element_header(i) {
Ok((rem, hdr)) => {
if hdr.tag() != explicit_tag {
return Err(Err::Error(X509Error::InvalidExtensions));
}
all_consuming(parse_extension_envelope_sequence)(rem)
}
Err(_) => Err(X509Error::InvalidExtensions.into()),
}
}
fn der_read_critical(i: &[u8]) -> BerResult<bool> {
let (rem, obj) = opt(parse_ber_bool)(i)?;
let value = obj
.map(|o| o.as_bool().unwrap_or_default()) .unwrap_or(false) ;
Ok((rem, value))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_keyusage_flags() {
let ku = KeyUsage { flags: 98 };
assert!(!ku.digital_signature());
assert!(ku.non_repudiation());
assert!(!ku.key_encipherment());
assert!(!ku.data_encipherment());
assert!(!ku.key_agreement());
assert!(ku.key_cert_sign());
assert!(ku.crl_sign());
assert!(!ku.encipher_only());
assert!(!ku.decipher_only());
}
#[test]
fn test_extensions1() {
use der_parser::oid;
let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension1.der"))
.unwrap()
.1;
let tbs = &crt.tbs_certificate;
let bc = crt
.basic_constraints()
.expect("could not get basic constraints")
.expect("no basic constraints found");
assert_eq!(
bc.value,
&BasicConstraints {
ca: true,
path_len_constraint: Some(1)
}
);
{
let ku = tbs
.key_usage()
.expect("could not get key usage")
.expect("no key usage found")
.value;
assert!(ku.digital_signature());
assert!(!ku.non_repudiation());
assert!(ku.key_encipherment());
assert!(ku.data_encipherment());
assert!(ku.key_agreement());
assert!(!ku.key_cert_sign());
assert!(!ku.crl_sign());
assert!(ku.encipher_only());
assert!(ku.decipher_only());
}
{
let eku = tbs
.extended_key_usage()
.expect("could not get extended key usage")
.expect("no extended key usage found")
.value;
assert!(!eku.any);
assert!(eku.server_auth);
assert!(!eku.client_auth);
assert!(eku.code_signing);
assert!(!eku.email_protection);
assert!(eku.time_stamping);
assert!(!eku.ocsp_signing);
assert_eq!(eku.other, vec![oid!(1.2.3 .4 .0 .42)]);
}
assert_eq!(
tbs.policy_constraints()
.expect("could not get policy constraints")
.expect("no policy constraints found")
.value,
&PolicyConstraints {
require_explicit_policy: None,
inhibit_policy_mapping: Some(10)
}
);
let val = tbs
.inhibit_anypolicy()
.expect("could not get inhibit_anypolicy")
.expect("no inhibit_anypolicy found")
.value;
assert_eq!(val, &InhibitAnyPolicy { skip_certs: 2 });
{
let alt_names = &tbs
.subject_alternative_name()
.expect("could not get subject alt names")
.expect("no subject alt names found")
.value
.general_names;
assert_eq!(alt_names[0], GeneralName::RFC822Name("foo@example.com"));
assert_eq!(alt_names[1], GeneralName::URI("http://my.url.here/"));
assert_eq!(
alt_names[2],
GeneralName::IPAddress([192, 168, 7, 1].as_ref())
);
assert_eq!(
format!(
"{}",
match alt_names[3] {
GeneralName::DirectoryName(ref dn) => dn,
_ => unreachable!(),
}
),
"C=UK, O=My Organization, OU=My Unit, CN=My Name"
);
assert_eq!(alt_names[4], GeneralName::DNSName("localhost"));
assert_eq!(alt_names[5], GeneralName::RegisteredID(oid!(1.2.90 .0)));
assert_eq!(
alt_names[6],
GeneralName::OtherName(oid!(1.2.3 .4), b"\xA0\x17\x0C\x15some other identifier")
);
}
{
let name_constraints = &tbs
.name_constraints()
.expect("could not get name constraints")
.expect("no name constraints found")
.value;
assert_eq!(name_constraints.permitted_subtrees, None);
assert_eq!(
name_constraints.excluded_subtrees,
Some(vec![
GeneralSubtree {
base: GeneralName::IPAddress([192, 168, 0, 0, 255, 255, 0, 0].as_ref())
},
GeneralSubtree {
base: GeneralName::RFC822Name("foo.com")
},
])
);
}
}
#[test]
fn test_extensions2() {
use der_parser::oid;
let crt = crate::parse_x509_certificate(include_bytes!("../../assets/extension2.der"))
.unwrap()
.1;
let tbs = crt.tbs_certificate;
assert_eq!(
tbs.policy_constraints()
.expect("could not get policy constraints")
.expect("no policy constraints found")
.value,
&PolicyConstraints {
require_explicit_policy: Some(5000),
inhibit_policy_mapping: None
}
);
{
let pm = tbs
.policy_mappings()
.expect("could not get policy_mappings")
.expect("no policy_mappings found")
.value
.clone()
.into_hashmap();
let mut pm_ref = HashMap::new();
pm_ref.insert(oid!(2.34.23), vec![oid!(2.2)]);
pm_ref.insert(oid!(1.1), vec![oid!(0.0.4)]);
pm_ref.insert(oid!(2.2), vec![oid!(2.2.1), oid!(2.2.3)]);
assert_eq!(pm, pm_ref);
}
}
#[test]
fn test_extensions_crl_distribution_points() {
{
let crt = crate::parse_x509_certificate(include_bytes!(
"../../assets/crl-ext/crl-no-crl.der"
))
.unwrap()
.1;
assert!(crt
.tbs_certificate
.extensions_map()
.unwrap()
.get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
.is_none());
}
{
let crt = crate::parse_x509_certificate(include_bytes!(
"../../assets/crl-ext/crl-simple.der"
))
.unwrap()
.1;
let crl = crt
.tbs_certificate
.extensions_map()
.unwrap()
.get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
.unwrap()
.parsed_extension();
assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
if let ParsedExtension::CRLDistributionPoints(crl) = crl {
assert_eq!(crl.len(), 1);
assert!(crl[0].reasons.is_none());
assert!(crl[0].crl_issuer.is_none());
let distribution_point = crl[0].distribution_point.as_ref().unwrap();
assert!(matches!(
distribution_point,
DistributionPointName::FullName(_)
));
if let DistributionPointName::FullName(names) = distribution_point {
assert_eq!(names.len(), 1);
assert!(matches!(names[0], GeneralName::URI(_)));
if let GeneralName::URI(uri) = names[0] {
assert_eq!(uri, "http://example.com/myca.crl")
}
}
}
}
{
let crt = crate::parse_x509_certificate(include_bytes!(
"../../assets/crl-ext/crl-complex.der"
))
.unwrap()
.1;
let crl = crt
.tbs_certificate
.extensions_map()
.unwrap()
.get(&OID_X509_EXT_CRL_DISTRIBUTION_POINTS)
.unwrap()
.parsed_extension();
assert!(matches!(crl, ParsedExtension::CRLDistributionPoints(_)));
if let ParsedExtension::CRLDistributionPoints(crl) = crl {
assert_eq!(crl.len(), 2);
let reasons = crl[0].reasons.as_ref().unwrap();
assert!(reasons.key_compromise());
assert!(reasons.ca_compromise());
assert!(!reasons.affilation_changed());
assert!(!reasons.superseded());
assert!(!reasons.cessation_of_operation());
assert!(!reasons.certificate_hold());
assert!(!reasons.privelege_withdrawn());
assert!(reasons.aa_compromise());
assert_eq!(
format!("{}", reasons),
"Key Compromise, CA Compromise, AA Compromise"
);
let issuers = crl[0].crl_issuer.as_ref().unwrap();
assert_eq!(issuers.len(), 1);
assert!(matches!(issuers[0], GeneralName::DirectoryName(_)));
if let GeneralName::DirectoryName(name) = &issuers[0] {
assert_eq!(name.to_string(), "C=US, O=Organisation, CN=Some Name");
}
let distribution_point = crl[0].distribution_point.as_ref().unwrap();
assert!(matches!(
distribution_point,
DistributionPointName::FullName(_)
));
if let DistributionPointName::FullName(names) = distribution_point {
assert_eq!(names.len(), 1);
assert!(matches!(names[0], GeneralName::URI(_)));
if let GeneralName::URI(uri) = names[0] {
assert_eq!(uri, "http://example.com/myca.crl")
}
}
let reasons = crl[1].reasons.as_ref().unwrap();
assert!(reasons.key_compromise());
assert!(reasons.ca_compromise());
assert!(!reasons.affilation_changed());
assert!(!reasons.superseded());
assert!(!reasons.cessation_of_operation());
assert!(!reasons.certificate_hold());
assert!(!reasons.privelege_withdrawn());
assert!(!reasons.aa_compromise());
assert_eq!(format!("{}", reasons), "Key Compromise, CA Compromise");
assert!(crl[1].crl_issuer.is_none());
let distribution_point = crl[1].distribution_point.as_ref().unwrap();
assert!(matches!(
distribution_point,
DistributionPointName::FullName(_)
));
if let DistributionPointName::FullName(names) = distribution_point {
assert_eq!(names.len(), 1);
assert!(matches!(names[0], GeneralName::URI(_)));
if let GeneralName::URI(uri) = names[0] {
assert_eq!(uri, "http://example.com/myca2.crl")
}
}
}
}
}
}