use super::{Coverage, CoverageError};
use crate::pki::CheckedPath;
use crate::source::{Location, Query, ScopeError, Snapshot};
use crate::timestamp::{Purpose, Verified};
use core::hash::Hash;
use core::marker::PhantomData;
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 struct ArtifactKey<'source> {
pub(super) kind: Kind,
pub(super) bytes: &'source [u8],
}
impl HashWork for ArtifactKey<'_> {
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 struct CarriedOrigin {
pub token: usize,
pub choice: usize,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArtifactSource {
Material(usize),
TokenCertificate {
token: usize,
certificate: usize,
},
TokenRevocation(CarriedOrigin),
}
#[derive(Debug, Clone, Copy)]
pub struct Occurrence<N, D, G, P> {
source: ArtifactSource,
location: Location<N, D, G>,
placement: P,
embedded_certificate: Option<usize>,
}
impl<N: Copy, D: Copy, G: Copy, P: Copy> Occurrence<N, D, G, P> {
#[must_use]
pub fn new(source: ArtifactSource, location: Location<N, D, G>, placement: P) -> Self {
Self {
source,
location,
placement,
embedded_certificate: None,
}
}
#[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) -> P {
self.placement
}
#[must_use]
pub fn embedded_certificate(&self) -> Option<usize> {
self.embedded_certificate
}
}
struct Entry<N, D, G, P> {
value: Occurrence<N, D, G, P>,
next: Option<usize>,
}
pub struct Index<'source, N, D, G, P, O, W: Work, I> {
snapshot: Snapshot<D, G>,
signature: N,
keys: OwnedMap<ArtifactKey<'source>, (usize, usize), W>,
entries: OwnedVec<Entry<N, D, G, P>, W>,
open: &'source [O],
issuer: PhantomData<I>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum IndexError<N> {
Scope(ScopeError<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> {
#[must_use]
pub fn identity(self) -> ArtifactKey<'source> {
match self {
Self::Certificate(value) => ArtifactKey {
kind: Kind::Certificate,
bytes: value.as_bytes(),
},
Self::Crl(value) => ArtifactKey {
kind: Kind::Crl,
bytes: value.as_bytes(),
},
Self::Ocsp(value) => ArtifactKey {
kind: Kind::Basic,
bytes: value.as_bytes(),
},
}
}
}
#[derive(Debug)]
pub enum Archived<N, D, G, P> {
Missing,
Uncovered,
Present(Occurrence<N, D, G, P>),
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum PathCertificate {
Anchor,
Member(usize),
}
#[derive(Debug)]
pub struct ArchivedCertificate<N, D, G, P> {
pub role: PathCertificate,
pub availability: Archived<N, D, G, P>,
}
pub struct PathCertificates<'path, 'document, N, D, G, P> {
path: &'path CheckedPath<'document>,
archive: Location<N, D, G>,
entries: OwnedVec<ArchivedCertificate<N, D, G, P>>,
all_covered: bool,
}
impl<'path, 'document, N: Copy, D: Copy, G: Copy, P>
PathCertificates<'path, 'document, N, D, G, P>
{
#[must_use]
pub fn path(&self) -> &'path 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, P>] {
&self.entries
}
#[must_use]
pub fn all_certificates_covered(&self) -> bool {
self.all_covered
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ArchiveError<N> {
Index(IndexError<N>),
Coverage(CoverageError<N>),
NotArchive {
property: N,
},
}
impl<'source, N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, P: Copy, O, W: Work, I>
Index<'source, N, D, G, P, O, W, I>
{
pub fn new<Q: Query<Id = N, Document = D, Generation = G, Work = W>>(
_issuer: I,
signature: N,
open: &'source [O],
query: &Q,
) -> Result<Self, IndexError<N>> {
query.admit(signature, 1).map_err(IndexError::Scope)?;
let execution = query.execution();
Ok(Self {
snapshot: query.snapshot(),
signature,
keys: execution.map(),
entries: execution.vector(),
open,
issuer: PhantomData,
})
}
pub fn insert<Q: Query<Id = N, Document = D, Generation = G, Work = W>>(
&mut self,
_issuer: I,
artifact: ArtifactRef<'_, 'source>,
occurrence: Occurrence<N, D, G, P>,
query: &Q,
) -> Result<(), IndexError<N>> {
if occurrence.location.snapshot != self.snapshot || query.snapshot() != self.snapshot {
return Err(IndexError::Foreign {
signature: self.signature,
});
}
self.insert_key(artifact.identity(), occurrence, query)?;
if let ArtifactRef::Ocsp(response) = artifact {
for certificate in 0..response.certificates().len() {
self.admit(query)?;
self.insert_key(
ArtifactKey {
kind: Kind::Certificate,
bytes: response.certificates()[certificate].as_bytes(),
},
Occurrence {
embedded_certificate: Some(certificate),
..occurrence
},
query,
)?;
}
}
Ok(())
}
#[must_use]
pub fn open(&self) -> &'source [O] {
self.open
}
#[must_use]
pub fn signature(&self) -> N {
self.signature
}
#[must_use]
pub fn snapshot(&self) -> Snapshot<D, G> {
self.snapshot
}
pub fn certificate<'view, 'query, Q: Query<Id = N, Document = D, Generation = G>>(
&'view self,
value: &Certificate<'_>,
query: &'query Q,
) -> Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> {
self.find(Kind::Certificate, value.as_bytes(), query)
}
pub fn crl<'view, 'query, Q: Query<Id = N, Document = D, Generation = G>>(
&'view self,
value: &CertificateList<'_>,
query: &'query Q,
) -> Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> {
self.find(Kind::Crl, value.as_bytes(), query)
}
pub fn ocsp<'view, 'query, Q: Query<Id = N, Document = D, Generation = G>>(
&'view self,
value: &BasicResponse<'_>,
query: &'query Q,
) -> Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> {
self.find(Kind::Basic, value.as_bytes(), query)
}
pub fn artifact<'view, 'query, Q: Query<Id = N, Document = D, Generation = G>>(
&'view self,
artifact: ArtifactRef<'_, '_>,
query: &'query Q,
) -> Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> {
let key = artifact.identity();
self.find(key.kind, key.bytes, query)
}
pub fn require_signature<Q: Query<Id = N, Document = D, Generation = G>>(
&self,
signature: N,
query: &Q,
) -> Result<(), IndexError<N>> {
self.admit(query)?;
let same =
query
.equal(self.signature, signature)
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
if !same {
return Err(IndexError::Foreign { signature });
}
Ok(())
}
pub fn archived<Q: Query<Id = N, Document = D, Generation = G>, C: Work>(
&self,
artifact: ArtifactRef<'_, '_>,
coverage: &Coverage<N, D, G, C, I>,
evidence: &Verified<N, D, G, I>,
query: &Q,
) -> Result<Archived<N, D, G, P>, ArchiveError<N>>
where
N: HashWork,
{
if evidence.purpose() != Purpose::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(), query)
.map_err(ArchiveError::Index)?;
coverage
.require_evidence(evidence, evidence.property(), query)
.map_err(ArchiveError::Coverage)?;
let key = artifact.identity();
let copies = self
.find(key.kind, key.bytes, query)
.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, query)
.map_err(ArchiveError::Coverage)?
{
return Ok(Archived::Present(copy));
}
}
Ok(if found {
Archived::Uncovered
} else {
Archived::Missing
})
}
pub fn archived_path<'path, 'document, Q, C: Work>(
&self,
path: &'path CheckedPath<'document>,
coverage: &Coverage<N, D, G, C, I>,
evidence: &Verified<N, D, G, I>,
query: &Q,
) -> Result<PathCertificates<'path, 'document, N, D, G, P>, ArchiveError<N>>
where
N: HashWork,
Q: Query<Id = N, Document = D, Generation = G>,
{
query
.admit(self.signature, 1)
.map_err(|cause| ArchiveError::Index(IndexError::Scope(cause)))?;
let mut entries = query.execution().vector();
let mut all_covered = true;
let mut record = |certificate, role| {
let availability = self.archived(
ArtifactRef::Certificate(certificate),
coverage,
evidence,
query,
)?;
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(query).map_err(ArchiveError::Index)?;
record(member.certificate(), PathCertificate::Member(ordinal))?;
}
Ok(PathCertificates {
path,
archive: evidence.token(),
entries: entries.into_owned(),
all_covered,
})
}
pub fn admit<Q: Query<Id = N, Document = D, Generation = G>>(
&self,
query: &Q,
) -> Result<(), IndexError<N>> {
if self.snapshot != query.snapshot() {
return Err(IndexError::Foreign {
signature: self.signature,
});
}
query.admit(self.signature, 2).map_err(IndexError::Scope)
}
fn insert_key<Q: Query<Id = N, Document = D, Generation = G, Work = W>>(
&mut self,
key: ArtifactKey<'source>,
value: Occurrence<N, D, G, P>,
query: &Q,
) -> Result<(), IndexError<N>> {
query.admit(self.signature, 1).map_err(IndexError::Scope)?;
let resource = |cause| IndexError::Resource {
signature: self.signature,
cause,
};
let previous = self
.keys
.get(&key, query.execution())
.map(Option::<&(usize, usize)>::copied)
.map_err(resource)?;
let next = self.entries.len();
self.entries
.try_push(Entry { value, next: None })
.map_err(resource)?;
let head = previous.map_or(next, |(head, _)| head);
self.keys.try_insert(key, (head, next)).map_err(resource)?;
if let Some((_, tail)) = previous {
self.entries[tail].next = Some(next);
}
Ok(())
}
fn find<'view, 'query, Q: Query<Id = N, Document = D, Generation = G>>(
&'view self,
kind: Kind,
bytes: &[u8],
query: &'query Q,
) -> Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> {
self.admit(query)?;
query.admit(self.signature, 1).map_err(IndexError::Scope)?;
let next = self
.keys
.get(&ArtifactKey { kind, bytes }, query.execution())
.map(|value| value.map(|&(head, _)| head))
.map_err(|cause| IndexError::Resource {
signature: self.signature,
cause,
})?;
Ok(Occurrences {
index: self,
query,
next,
})
}
}
pub type Lookup<'view, 'query, 'source, N, D, G, P, O, W, Q, I> =
Result<Occurrences<'view, 'query, 'source, N, D, G, P, O, W, Q, I>, IndexError<N>>;
pub struct Occurrences<'view, 'query, 'source, N, D, G, P, O, W: Work, Q, I> {
index: &'view Index<'source, N, D, G, P, O, W, I>,
query: &'query Q,
next: Option<usize>,
}
impl<N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, P: Copy, O, W: Work, Q, I> Iterator
for Occurrences<'_, '_, '_, N, D, G, P, O, W, Q, I>
where
Q: Query<Id = N, Document = D, Generation = G>,
{
type Item = Result<Occurrence<N, D, G, P>, IndexError<N>>;
fn next(&mut self) -> Option<Self::Item> {
let next = self.next.take()?;
if let Err(error) = self.index.admit(self.query) {
return Some(Err(error));
}
let entry = &self.index.entries[next];
self.next = entry.next;
Some(Ok(entry.value))
}
}
impl<N: Copy + Eq, D: Copy + Eq, G: Copy + Eq, P: Copy, O, W: Work, Q, I> core::iter::FusedIterator
for Occurrences<'_, '_, '_, N, D, G, P, O, W, Q, I>
where
Q: Query<Id = N, Document = D, Generation = G>,
{
}