use crate::algorithm::{self, AlgorithmIdentifier};
use crate::error::{Error, ErrorCode, Located, Result};
use crate::extension::{self, Extensions};
use crate::name::{self, Name};
use crate::path::{Verified, Verify, WorkingKey};
use crate::time::{self, Time};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{BitString, Element, Integer, Parser, Reader, Span, Tag, universal};
mod scope;
pub use scope::{CertificateKind, IssuingDistributionPoint, Reasons, ScopeFailure, ScopeOutcome};
mod entry;
pub use entry::EntryLookup;
pub use entry::Reason;
pub(crate) use entry::reason_element;
mod delta;
pub use delta::{
BoundDelta, CombinedEntry, DeltaBinding, DeltaFailure, EntryOrigin, MissingDeltaField,
};
mod freshness;
pub use freshness::Freshness;
pub(crate) use freshness::update_window;
mod evidence;
pub use evidence::{
AbsenceEvidence, CompleteEvidence, CompleteFacts, Coverage, CoverageAdmission, DeltaEvidence,
DeltaFacts, Evaluation, EvidenceSource, SourceOrigin,
};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Version {
V1,
V2,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthenticationFailure {
KeyBinding,
IssuerName,
KeyUsage,
Signature,
}
#[derive(Debug, Clone, Copy)]
pub struct Authenticated<'r, 'a> {
crl: &'r CertificateList<'a>,
issuer: &'r crate::Certificate<'a>,
key: WorkingKey<'a>,
}
impl<'r, 'a> Authenticated<'r, 'a> {
#[must_use]
pub fn crl(self) -> &'r CertificateList<'a> {
self.crl
}
#[must_use]
pub fn issuer(self) -> &'r crate::Certificate<'a> {
self.issuer
}
#[must_use]
pub fn key(self) -> WorkingKey<'a> {
self.key
}
}
#[derive(Debug)]
pub enum Authentication<'r, 'a> {
Authenticated(Authenticated<'r, 'a>),
Rejected(AuthenticationFailure),
Unsupported,
}
#[derive(Debug)]
pub struct RevokedCertificate<'a> {
serial: Integer<'a>,
revocation_date: Time,
revocation_date_offset: u64,
extensions: Extensions<'a>,
span: Span,
}
impl<'a> RevokedCertificate<'a> {
#[must_use]
pub fn serial(&self) -> Integer<'a> {
self.serial
}
#[must_use]
pub fn revocation_date(&self) -> Time {
self.revocation_date
}
pub fn time_relation<W: Work>(
&self,
now: crate::temporal::Reading,
utc: &crate::temporal::LeapTable<'_>,
execution: &W,
) -> Result<crate::time::Relation> {
crate::time::relation_in(
self.revocation_date.instant(),
now,
utc,
execution,
(
"certificateList.revokedCertificates.revocationDate",
self.revocation_date_offset,
),
)
}
#[must_use]
pub fn extensions(&self) -> &Extensions<'a> {
&self.extensions
}
#[must_use]
pub fn span(&self) -> Span {
self.span
}
}
#[derive(Debug)]
pub struct TbsCertList<'a> {
version: Version,
signature: AlgorithmIdentifier<'a>,
issuer: Name<'a>,
this_update: Time,
next_update: Option<Time>,
revoked: OwnedVec<RevokedCertificate<'a>>,
extensions: Extensions<'a>,
bytes: &'a [u8],
span: Span,
}
impl<'a> TbsCertList<'a> {
#[must_use]
pub fn version(&self) -> Version {
self.version
}
#[must_use]
pub fn signature(&self) -> AlgorithmIdentifier<'a> {
self.signature
}
#[must_use]
pub fn issuer(&self) -> &Name<'a> {
&self.issuer
}
#[must_use]
pub fn this_update(&self) -> Time {
self.this_update
}
#[must_use]
pub fn next_update(&self) -> Option<Time> {
self.next_update
}
#[must_use]
pub fn revoked_certificates(&self) -> &[RevokedCertificate<'a>] {
&self.revoked
}
#[must_use]
pub fn extensions(&self) -> &Extensions<'a> {
&self.extensions
}
#[must_use]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
#[must_use]
pub fn span(&self) -> Span {
self.span
}
}
#[derive(Debug)]
pub struct CertificateList<'a> {
tbs: TbsCertList<'a>,
signature_algorithm: AlgorithmIdentifier<'a>,
signature: BitString<'a>,
bytes: &'a [u8],
span: Span,
}
impl<'a> CertificateList<'a> {
pub fn authenticate<'r, V: Verify<E>, E: Work>(
&'r self,
issuer: &'r crate::Certificate<'a>,
key: WorkingKey<'a>,
verifier: &V,
execution: &E,
) -> crate::error::CallbackResult<Authentication<'r, 'a>, V::Error> {
use crate::error::CallbackError;
let encoded_key = key.key().as_bytes();
let issuer_key = issuer.tbs().public_key().as_bytes();
let work = (encoded_key.len().max(issuer_key.len()) as u64)
.checked_mul(2)
.and_then(|n| n.checked_add(1))
.ok_or_else(|| {
Error::resource(
structured_asn1::memory::MemoryError::WorkExceeded,
FIELD,
self.span.start,
)
})?;
execution
.charge_work(work)
.map_err(|reason| Error::resource(reason, FIELD, self.span.start))?;
if encoded_key != issuer_key {
return Ok(Authentication::Rejected(AuthenticationFailure::KeyBinding));
}
if !self.tbs.issuer.matches(issuer.tbs().subject(), execution)? {
return Ok(Authentication::Rejected(AuthenticationFailure::IssuerName));
}
let usage = issuer.tbs().extensions().key_usage(execution)?;
if usage.is_some_and(|usage| !usage.crl_sign())
|| (usage.is_none() && issuer.tbs().version() == crate::Version::V3)
{
return Ok(Authentication::Rejected(AuthenticationFailure::KeyUsage));
}
match verifier
.verify(
&self.tbs.signature,
key,
self.signed_bytes(),
self.signature,
execution,
)
.map_err(CallbackError::Callback)?
{
Verified::Yes => Ok(Authentication::Authenticated(Authenticated {
crl: self,
issuer,
key,
})),
Verified::No => Ok(Authentication::Rejected(AuthenticationFailure::Signature)),
Verified::Unsupported => Ok(Authentication::Unsupported),
}
}
pub fn read(parser: &'a Parser, bytes: &'a [u8], base: u64) -> Result<Self> {
let mut reader = parser.read(bytes, base).field(FIELD)?;
let element = required(&mut reader, FIELD, base)?;
let crl = Self::read_element(&reader, &element)?;
if !reader.is_empty() {
return Err(Error::new(
ErrorCode::TrailingData,
FIELD,
element.span().end(),
));
}
Ok(crl)
}
pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
let mut outer = parent
.reread(element, structured_asn1::Encoding::Der)
.field(FIELD)?;
let root = outer
.expect(Tag::constructed(universal::SEQUENCE))
.field(FIELD)?;
let mut inside = outer.enter(&root).field(FIELD)?;
let tbs_element = required(&mut inside, TBS, root.span().start)?;
let tbs = read_tbs(&inside, &tbs_element)?;
let algorithm_element = required(&mut inside, ALGORITHM, root.span().start)?;
let signature_algorithm = algorithm::read(&inside, &algorithm_element, ALGORITHM)?;
let signature = inside.read_bit_string().field(SIGNATURE)?;
end(&inside, FIELD, root.span().start)?;
if !signature_algorithm.matches_in(&tbs.signature, &inside)? {
return Err(Error::new(
ErrorCode::AlgorithmMismatch,
ALGORITHM,
algorithm_element.span().start,
));
}
Ok(Self {
tbs,
signature_algorithm,
signature,
bytes: root.as_bytes(),
span: root.span(),
})
}
#[must_use]
pub fn tbs(&self) -> &TbsCertList<'a> {
&self.tbs
}
#[must_use]
pub fn signature_algorithm(&self) -> AlgorithmIdentifier<'a> {
self.signature_algorithm
}
#[must_use]
pub fn signature(&self) -> BitString<'a> {
self.signature
}
#[must_use]
pub fn signed_bytes(&self) -> &'a [u8] {
self.tbs.bytes
}
#[must_use]
pub fn as_bytes(&self) -> &'a [u8] {
self.bytes
}
#[must_use]
pub fn span(&self) -> Span {
self.span
}
}
const FIELD: &str = "certificateList";
const TBS: &str = "certificateList.tbsCertList";
const ALGORITHM: &str = "certificateList.signatureAlgorithm";
const SIGNATURE: &str = "certificateList.signature";
const ISSUER: &str = "certificateList.tbsCertList.issuer";
const THIS_UPDATE: &str = "certificateList.tbsCertList.thisUpdate";
const NEXT_UPDATE: &str = "certificateList.tbsCertList.nextUpdate";
const ENTRIES: &str = "certificateList.tbsCertList.revokedCertificates";
const EXTENSIONS: &str = "certificateList.tbsCertList.crlExtensions";
fn required<'a>(reader: &mut Reader<'a>, field: &'static str, at: u64) -> Result<Element<'a>> {
reader
.next_element()
.field(field)?
.ok_or_else(|| Error::new(ErrorCode::MissingField, field, at))
}
fn end(reader: &structured_asn1::ScopedReader<'_, '_>, field: &'static str, at: u64) -> Result<()> {
if reader.is_empty() {
return Ok(());
}
Err(Error::new(ErrorCode::TrailingData, field, at))
}
fn is_time(element: &Element<'_>) -> bool {
element.tag() == Tag::primitive(universal::UTC_TIME)
|| element.tag() == Tag::primitive(universal::GENERALIZED_TIME)
}
fn read_tbs<'a>(parent: &Reader<'a>, element: &Element<'a>) -> Result<TbsCertList<'a>> {
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::new(
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
TBS,
element.span().start,
));
}
let mut inside = parent.enter(element).field(TBS)?;
let mut next = required(&mut inside, TBS, element.span().start)?;
let version = if next.tag() == Tag::primitive(universal::INTEGER) {
inside.charge(9).field(TBS)?;
if inside.integer(&next).field(TBS)?.as_u64() != Some(1) {
return Err(Error::new(
ErrorCode::UnsupportedVersion,
TBS,
next.span().start,
));
}
next = required(&mut inside, TBS, element.span().start)?;
Version::V2
} else {
Version::V1
};
let signature = algorithm::read(&inside, &next, TBS)?;
let issuer_element = required(&mut inside, ISSUER, element.span().start)?;
let issuer = name::read(&inside, &issuer_element, ISSUER)?;
if issuer.is_empty() {
return Err(Error::new(
ErrorCode::EmptySequence,
ISSUER,
issuer_element.span().start,
));
}
let this = required(&mut inside, THIS_UPDATE, element.span().start)?;
let this_update = time::read_in(&inside, &this, THIS_UPDATE)?;
let mut next = inside.next_element().field(TBS)?;
let next_update = match next {
Some(value) if is_time(&value) => {
let time = time::read_in(&inside, &value, NEXT_UPDATE)?;
next = inside.next_element().field(TBS)?;
Some(time)
}
_ => None,
};
let work = parent.execution();
let mut revoked = work.vector();
if let Some(sequence) =
next.filter(|value| value.tag() == Tag::constructed(universal::SEQUENCE))
{
let mut entries = inside.enter(&sequence).field(ENTRIES)?;
while let Some(entry) = entries.next_element().field(ENTRIES)? {
let value = read_entry(&entries, &entry, version)?;
revoked
.try_push(value)
.map_err(|reason| Error::resource(reason, ENTRIES, entry.span().start))?;
}
if revoked.is_empty() {
return Err(Error::new(
ErrorCode::EmptySequence,
ENTRIES,
sequence.span().start,
));
}
next = inside.next_element().field(TBS)?;
}
let mut extensions = Extensions::empty(inside.memory());
if let Some(tagged) = next {
if tagged.tag() != Tag::context(true, 0) || version != Version::V2 {
return Err(Error::new(
ErrorCode::UnexpectedField,
EXTENSIONS,
tagged.span().start,
));
}
let mut wrapper = inside.enter(&tagged).field(EXTENSIONS)?;
let sequence = required(&mut wrapper, EXTENSIONS, tagged.span().start)?;
extensions = extension::read(&wrapper, &sequence, EXTENSIONS)?;
end(&wrapper, EXTENSIONS, tagged.span().start)?;
}
end(&inside, TBS, element.span().start)?;
Ok(TbsCertList {
version,
signature,
issuer,
this_update,
next_update,
revoked: revoked.into_owned(),
extensions,
bytes: element.as_bytes(),
span: element.span(),
})
}
fn read_entry<'a>(
parent: &Reader<'a>,
element: &Element<'a>,
version: Version,
) -> Result<RevokedCertificate<'a>> {
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::new(
ErrorCode::Encoding(structured_asn1::ErrorCode::UnexpectedTag),
ENTRIES,
element.span().start,
));
}
let mut inside = parent.enter(element).field(ENTRIES)?;
let serial = inside.read_integer().field(ENTRIES)?;
let date = required(&mut inside, ENTRIES, element.span().start)?;
let revocation_date = time::read_in(&inside, &date, ENTRIES)?;
let extensions = match inside.next_element().field(ENTRIES)? {
Some(value) => {
if version != Version::V2 {
return Err(Error::new(
ErrorCode::UnexpectedField,
ENTRIES,
value.span().start,
));
}
extension::read(&inside, &value, ENTRIES)?
}
None => Extensions::empty(inside.memory()),
};
end(&inside, ENTRIES, element.span().start)?;
Ok(RevokedCertificate {
serial,
revocation_date,
revocation_date_offset: date.span().start,
extensions,
span: element.span(),
})
}