use super::{Placement, Value, View};
use crate::binary::Location;
use crate::timestamp::{
CarriedMaterial, CarriedOrigin, CoverageError, ElementCoverage, Role, Verified,
};
use crate::xml::{Access, AccessError, Snapshot, Xml};
use core::hash::Hash;
use structured_asn1::memory::{HashWork, MemoryError, OwnedMap, OwnedVec, Work};
use structured_pkix::{Certificate, crl::CertificateList, ocsp::BasicResponse};
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(super) enum Kind {
Certificate,
Crl,
Basic,
}
#[derive(Clone, Copy, PartialEq, Eq, Hash)]
pub(super) struct Key<'source> {
pub(super) kind: Kind,
pub(super) bytes: &'source [u8],
}
impl HashWork for Key<'_> {
fn hash_work(&self) -> Result<u64, MemoryError> {
u64::try_from(self.bytes.len())
.map_err(|_| MemoryError::WorkExceeded)?
.checked_add(2)
.ok_or(MemoryError::WorkExceeded)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArtifactSource {
Xml(usize),
TokenCertificate {
token: usize,
certificate: usize,
},
TokenRevocation(CarriedOrigin),
}
#[derive(Debug, Clone, Copy)]
pub struct Occurrence<N, D, G> {
source: ArtifactSource,
location: Location<N, D, G>,
placement: Placement<N>,
embedded_certificate: Option<usize>,
}
impl<N: Copy, D: Copy, G: Copy> Occurrence<N, D, G> {
#[must_use]
pub fn source(&self) -> ArtifactSource {
self.source
}
#[must_use]
pub fn location(&self) -> Location<N, D, G> {
self.location
}
#[must_use]
pub fn placement(&self) -> Placement<N> {
self.placement
}
#[must_use]
pub fn embedded_certificate(&self) -> Option<usize> {
self.embedded_certificate
}
}
struct Entry<N, D, G> {
value: Occurrence<N, D, G>,
next: Option<usize>,
}
pub struct Index<'source, N, D, G, W: Work> {
snapshot: Snapshot<D, G>,
signature: N,
keys: OwnedMap<Key<'source>, (usize, usize), W>,
entries: OwnedVec<Entry<N, D, G>, W>,
pub(super) opaque: &'source [super::Opaque<N, D, G>],
}
#[derive(Debug)]
pub enum IndexError<E, N> {
Access(AccessError<E, N>),
Foreign {
signature: N,
},
Resource {
signature: N,
cause: MemoryError,
},
}
#[derive(Debug, Clone, Copy)]
pub enum ArtifactRef<'view, 'source> {
Certificate(&'view Certificate<'source>),
Crl(&'view CertificateList<'source>),
Ocsp(&'view BasicResponse<'source>),
}
impl<'source> ArtifactRef<'_, 'source> {
pub(super) fn identity(self) -> Key<'source> {
match self {
Self::Certificate(value) => Key {
kind: Kind::Certificate,
bytes: value.as_bytes(),
},
Self::Crl(value) => Key {
kind: Kind::Crl,
bytes: value.as_bytes(),
},
Self::Ocsp(value) => Key {
kind: Kind::Basic,
bytes: value.as_bytes(),
},
}
}
}
#[derive(Debug)]
pub enum Archived<N, D, G> {
Missing,
Uncovered,
Present(Occurrence<N, D, G>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathCertificate {
Anchor,
Member(usize),
}
#[derive(Debug)]
pub struct ArchivedCertificate<N, D, G> {
pub role: PathCertificate,
pub availability: Archived<N, D, G>,
}
pub struct PathCertificates<'path, 'document, N, D, G> {
path: &'path crate::pki::CheckedPath<'document>,
archive: Location<N, D, G>,
entries: OwnedVec<ArchivedCertificate<N, D, G>>,
all_covered: bool,
}
impl<'path, 'document, N: Copy, D: Copy, G: Copy> PathCertificates<'path, 'document, N, D, G> {
#[must_use]
pub fn path(&self) -> &'path crate::pki::CheckedPath<'document> {
self.path
}
#[must_use]
pub fn archive(&self) -> Location<N, D, G> {
self.archive
}
#[must_use]
pub fn entries(&self) -> &[ArchivedCertificate<N, D, G>] {
&self.entries
}
#[must_use]
pub fn all_certificates_covered(&self) -> bool {
self.all_covered
}
}
#[derive(Debug)]
pub enum ArchiveError<E, N> {
Index(IndexError<E, N>),
Coverage(CoverageError<E, N>),
NotArchive {
property: N,
},
}
type IndexResult<T, X> = Result<T, IndexError<<X as Xml>::Error, <X as Xml>::Id>>;
impl<'source, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, W: Work> Index<'source, N, D, G, W> {
pub fn build<X: Xml<Id = N, Document = D, Generation = G>>(
view: &View<'source, N, D, G>,
access: &Access<'_, X, W>,
) -> IndexResult<Self, X> {
let signature = view.signature;
if view.snapshot != access.snapshot() {
return Err(IndexError::Foreign { signature });
}
let (keys, entries) = access
.query(signature, |_, _, work| Ok((work.map(), work.vector())))
.map_err(IndexError::Access)?;
let mut index = Self {
snapshot: view.snapshot,
signature,
keys,
entries,
opaque: view.opaque(),
};
let mut next_basic = 0;
for ordinal in 0..view.values.len() {
index.admit(access)?;
let value = &view.values[ordinal];
let placement = view.sources[ordinal].placement();
let (kind, bytes) = match &value.value {
Value::Certificate(value) => (Kind::Certificate, value.as_bytes()),
Value::Crl(value) => (Kind::Crl, value.as_bytes()),
Value::Ocsp(_) => {
if let Some(basic) = view.basic.get(next_basic)
&& basic.source_index() == ordinal
{
index.insert_basic(basic, placement, access)?;
next_basic += 1;
}
continue;
}
};
index.insert(
Key { kind, bytes },
Occurrence {
source: ArtifactSource::Xml(ordinal),
location: value.location,
placement,
embedded_certificate: None,
},
access,
)?;
}
index.admit(access)?;
Ok(index)
}
pub fn build_with_tokens<X: Xml<Id = N, Document = D, Generation = G>>(
view: &View<'source, N, D, G>,
material: &CarriedMaterial<'source, N, D, G>,
access: &Access<'_, X, W>,
) -> IndexResult<Self, X> {
if material.view.snapshot() != view.snapshot {
return Err(IndexError::Foreign {
signature: view.signature,
});
}
if !access
.equal_id(material.view.signature(), view.signature)
.map_err(|cause| IndexError::Resource {
signature: view.signature,
cause,
})?
{
return Err(IndexError::Foreign {
signature: view.signature,
});
}
Self::build(view, access)?.include_bound_token_material(material, access)
}
fn include_bound_token_material<X: Xml<Id = N, Document = D, Generation = G>>(
mut self,
material: &CarriedMaterial<'source, N, D, G>,
access: &Access<'_, X, W>,
) -> IndexResult<Self, X> {
self.admit(access)?;
for token in 0..material.view.readings().len() {
self.admit(access)?;
let source = &material.view.sources()[token];
let certificates = material.view.readings()[token]
.token()
.message()
.certificates();
let mut certificate = 0;
while certificate < certificates.len() {
self.admit(access)?;
self.insert(
Key {
kind: Kind::Certificate,
bytes: certificates[certificate].as_bytes(),
},
Occurrence {
source: ArtifactSource::TokenCertificate { token, certificate },
location: source.location(),
placement: Placement::TimestampToken {
property: source.property(),
token: source.location().node,
role: source.role(),
},
embedded_certificate: None,
},
access,
)?;
certificate += 1;
}
}
for ordinal in 0..material.crls().len() {
self.admit(access)?;
let value = &material.crls()[ordinal];
let occurrence = Self::carried_occurrence(material, value.origin);
self.insert(
Key {
kind: Kind::Crl,
bytes: value.value.as_bytes(),
},
occurrence,
access,
)?;
}
for ordinal in 0..material.basic_responses().len() {
self.admit(access)?;
let value = &material.basic_responses()[ordinal];
let occurrence = Self::carried_occurrence(material, value.origin);
self.insert_response(&value.value, occurrence, access)?;
}
self.admit(access)?;
Ok(self)
}
fn carried_occurrence(
material: &CarriedMaterial<'source, N, D, G>,
origin: CarriedOrigin,
) -> Occurrence<N, D, G> {
let source = &material.view.sources()[origin.token];
Occurrence {
source: ArtifactSource::TokenRevocation(origin),
location: source.location(),
placement: Placement::TimestampToken {
property: source.property(),
token: source.location().node,
role: source.role(),
},
embedded_certificate: None,
}
}
pub fn certificate<'view, 'xml, X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&'view self,
value: &Certificate<'_>,
access: &'view Access<'xml, X, Q>,
) -> IndexResult<Occurrences<'view, 'xml, 'source, X, W, Q>, X> {
self.find(Kind::Certificate, value.as_bytes(), access)
}
pub fn crl<'view, 'xml, X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&'view self,
value: &CertificateList<'_>,
access: &'view Access<'xml, X, Q>,
) -> IndexResult<Occurrences<'view, 'xml, 'source, X, W, Q>, X> {
self.find(Kind::Crl, value.as_bytes(), access)
}
pub fn ocsp<'view, 'xml, X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&'view self,
value: &BasicResponse<'_>,
access: &'view Access<'xml, X, Q>,
) -> IndexResult<Occurrences<'view, 'xml, 'source, X, W, Q>, X> {
self.find(Kind::Basic, value.as_bytes(), access)
}
pub(super) fn artifact<'view, 'xml, X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&'view self,
artifact: ArtifactRef<'_, '_>,
access: &'view Access<'xml, X, Q>,
) -> IndexResult<Occurrences<'view, 'xml, 'source, X, W, Q>, X> {
let key = artifact.identity();
self.find(key.kind, key.bytes, access)
}
pub(super) fn require_signature<X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&self,
signature: N,
access: &Access<'_, X, Q>,
) -> IndexResult<(), X> {
self.admit(access)?;
let same = access
.equal_id(self.signature, signature)
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
if !same {
return Err(IndexError::Foreign { signature });
}
Ok(())
}
pub fn archived<X: Xml<Id = N, Document = D, Generation = G>, Q: Work, C: Work>(
&self,
artifact: ArtifactRef<'_, '_>,
coverage: &ElementCoverage<N, D, G, C>,
evidence: &Verified<N, D, G>,
access: &Access<'_, X, Q>,
) -> Result<Archived<N, D, G>, ArchiveError<X::Error, N>>
where
N: HashWork,
{
if evidence.role() != Role::Archive {
return Err(ArchiveError::NotArchive {
property: evidence.property(),
});
}
if evidence.token().snapshot != self.snapshot {
return Err(ArchiveError::Index(IndexError::Foreign {
signature: self.signature,
}));
}
self.require_signature(evidence.signature(), access)
.map_err(ArchiveError::Index)?;
coverage
.require_evidence(evidence, evidence.property(), access)
.map_err(ArchiveError::Coverage)?;
let key = artifact.identity();
let copies = self
.find(key.kind, key.bytes, access)
.map_err(ArchiveError::Index)?;
let mut found = false;
for copy in copies {
let copy = copy.map_err(ArchiveError::Index)?;
found = true;
if coverage
.selects(copy.location.node, access)
.map_err(ArchiveError::Coverage)?
{
return Ok(Archived::Present(copy));
}
}
Ok(if found {
Archived::Uncovered
} else {
Archived::Missing
})
}
pub fn archived_path<
'path,
'document,
X: Xml<Id = N, Document = D, Generation = G>,
Q: Work,
C: Work,
>(
&self,
path: &'path crate::pki::CheckedPath<'document>,
coverage: &ElementCoverage<N, D, G, C>,
evidence: &Verified<N, D, G>,
access: &Access<'_, X, Q>,
) -> Result<PathCertificates<'path, 'document, N, D, G>, ArchiveError<X::Error, N>>
where
N: HashWork,
{
let mut entries = access
.query(self.signature, |_, _, work| Ok(work.vector()))
.map_err(|cause| ArchiveError::Index(IndexError::Access(cause)))?;
let mut all_covered = true;
let mut record = |certificate, role| {
let availability = self.archived(
ArtifactRef::Certificate(certificate),
coverage,
evidence,
access,
)?;
if !matches!(availability, Archived::Present(_)) {
all_covered = false;
}
entries
.try_push(ArchivedCertificate { role, availability })
.map_err(|cause| {
ArchiveError::Index(IndexError::Resource {
signature: self.signature,
cause,
})
})
};
if let Some(certificate) = path.anchor().certificate() {
record(certificate, PathCertificate::Anchor)?;
}
for (ordinal, member) in path.members().iter().enumerate() {
self.admit(access).map_err(ArchiveError::Index)?;
record(member.certificate(), PathCertificate::Member(ordinal))?;
}
Ok(PathCertificates {
path,
archive: evidence.token(),
entries: entries.into_owned(),
all_covered,
})
}
fn admit<X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&self,
access: &Access<'_, X, Q>,
) -> IndexResult<(), X> {
if self.snapshot != access.snapshot() {
return Err(IndexError::Foreign {
signature: self.signature,
});
}
access
.query(self.signature, |_, _, work| Ok(work.charge_work(1)))
.map_err(IndexError::Access)?
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})
}
fn insert<X: Xml<Id = N, Document = D, Generation = G>>(
&mut self,
key: Key<'source>,
value: Occurrence<N, D, G>,
access: &Access<'_, X, W>,
) -> IndexResult<(), X> {
let previous = access
.query(self.signature, |_, _, work| {
Ok(self
.keys
.get(&key, work)
.map(Option::<&(usize, usize)>::copied))
})
.map_err(IndexError::Access)?
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
let next = self.entries.len();
self.entries
.try_push(Entry { value, next: None })
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
let head = previous.map_or(next, |(head, _)| head);
self.keys
.try_insert(key, (head, next))
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
if let Some((_, tail)) = previous {
self.entries[tail].next = Some(next);
}
Ok(())
}
fn insert_basic<X: Xml<Id = N, Document = D, Generation = G>>(
&mut self,
basic: &super::Basic<'source, N, D, G>,
placement: Placement<N>,
access: &Access<'_, X, W>,
) -> IndexResult<(), X> {
self.insert_response(
basic.response(),
Occurrence {
source: ArtifactSource::Xml(basic.source_index()),
location: basic.location(),
placement,
embedded_certificate: None,
},
access,
)
}
fn insert_response<X: Xml<Id = N, Document = D, Generation = G>>(
&mut self,
response: &BasicResponse<'source>,
occurrence: Occurrence<N, D, G>,
access: &Access<'_, X, W>,
) -> IndexResult<(), X> {
self.insert(
Key {
kind: Kind::Basic,
bytes: response.as_bytes(),
},
occurrence,
access,
)?;
for certificate in 0..response.certificates().len() {
self.admit(access)?;
self.insert(
Key {
kind: Kind::Certificate,
bytes: response.certificates()[certificate].as_bytes(),
},
Occurrence {
embedded_certificate: Some(certificate),
..occurrence
},
access,
)?;
}
Ok(())
}
fn find<'view, 'xml, X: Xml<Id = N, Document = D, Generation = G>, Q: Work>(
&'view self,
kind: Kind,
bytes: &[u8],
access: &'view Access<'xml, X, Q>,
) -> IndexResult<Occurrences<'view, 'xml, 'source, X, W, Q>, X> {
self.admit(access)?;
let next = access
.query(self.signature, |_, _, work| {
Ok(self
.keys
.get(&Key { kind, bytes }, work)
.map(|value| value.map(|&(head, _)| head)))
})
.map_err(IndexError::Access)?
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
Ok(Occurrences {
index: self,
access,
next,
})
}
}
pub struct Occurrences<'view, 'xml, 'source, X: Xml, W: Work, Q: Work> {
index: &'view Index<'source, X::Id, X::Document, X::Generation, W>,
access: &'view Access<'xml, X, Q>,
next: Option<usize>,
}
impl<X: Xml, W: Work, Q: Work> Iterator for Occurrences<'_, '_, '_, X, W, Q> {
type Item = IndexResult<Occurrence<X::Id, X::Document, X::Generation>, X>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.next.take()?;
if let Err(error) = self.index.admit(self.access) {
return Some(Err(error));
}
let entry = &self.index.entries[next];
self.next = entry.next;
Some(Ok(entry.value))
}
}
impl<X: Xml, W: Work, Q: Work> core::iter::FusedIterator for Occurrences<'_, '_, '_, X, W, Q> {}