use super::{
Digest, ExtensionProcessing, Lookup, NonceBinding, RequestNonce, ScopedResponse,
SingleResponse, UpdateWindow,
};
use crate::error::{CallbackResult, Result};
use crate::temporal::{LeapTable, Reading};
use crate::{Certificate, Error};
use structured_asn1::memory::{Memory, MemoryError};
use structured_asn1::{Oid, Span};
pub struct Request<'p, 'a> {
pub target: &'p Certificate<'a>,
pub nonce: Option<RequestNonce<'p>>,
pub intended_signer: Option<&'p Certificate<'a>>,
}
pub struct Evaluation<'p> {
pub now: Reading,
pub utc: &'p LeapTable<'p>,
pub execution: &'p Memory,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RecipientBinding {
NotSpecified,
Matches,
}
#[derive(Debug, Clone, Copy)]
pub struct Evidence<'r, 'a> {
source: ScopedResponse<'r, 'a>,
assertion: &'r SingleResponse<'a>,
window: UpdateWindow,
nonce: NonceBinding,
recipient: RecipientBinding,
revocation_time: Option<crate::time::Relation>,
}
impl<'r, 'a> Evidence<'r, 'a> {
#[must_use]
pub fn source(self) -> ScopedResponse<'r, 'a> {
self.source
}
#[must_use]
pub fn assertion(self) -> &'r SingleResponse<'a> {
self.assertion
}
#[must_use]
pub fn window(self) -> UpdateWindow {
self.window
}
#[must_use]
pub fn nonce(self) -> NonceBinding {
self.nonce
}
#[must_use]
pub fn recipient(self) -> RecipientBinding {
self.recipient
}
#[must_use]
pub fn revocation_time(self) -> Option<crate::time::Relation> {
self.revocation_time
}
}
#[derive(Debug, Clone, Copy)]
pub enum TargetEvidence<'r, 'a> {
Evidence(Evidence<'r, 'a>),
UnexpectedSigner,
Lookup(Lookup<'r, 'a>),
UnsupportedCritical {
kind: Oid<'a>,
span: Span,
},
}
fn exact_signer(
left: &Certificate<'_>,
right: &Certificate<'_>,
execution: &Memory,
at: u64,
) -> Result<bool> {
let left = left.as_bytes();
let right = right.as_bytes();
let work = u64::try_from(left.len().max(right.len()))
.ok()
.and_then(|n| n.checked_mul(2))
.and_then(|n| n.checked_add(1))
.ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.recipient", at))?;
execution
.charge_work(work)
.map_err(|reason| Error::resource(reason, "ocspResponse.recipient", at))?;
Ok(left == right)
}
impl<'r, 'a> ScopedResponse<'r, 'a> {
pub fn evaluate<D: Digest>(
self,
request: &Request<'_, '_>,
time: &Evaluation<'_>,
digest: &D,
) -> CallbackResult<TargetEvidence<'r, 'a>, D::Error> {
let execution = time.execution;
let response = self.signed().response();
let recipient = match request.intended_signer {
None => RecipientBinding::NotSpecified,
Some(expected) => {
if !exact_signer(
self.signed().signer(),
expected,
execution,
response.span().start,
)? {
return Ok(TargetEvidence::UnexpectedSigner);
}
RecipientBinding::Matches
}
};
let processed = response.process_extensions(execution)?;
if let ExtensionProcessing::Unsupported { kind, span } = processed.processing {
return Ok(TargetEvidence::UnsupportedCritical { kind, span });
}
let assertion = match self.find(request.target, digest, execution)? {
Lookup::Found(assertion) => assertion,
outcome => return Ok(TargetEvidence::Lookup(outcome)),
};
if let ExtensionProcessing::Unsupported { kind, span } =
assertion.check_extensions(execution)?
{
return Ok(TargetEvidence::UnsupportedCritical { kind, span });
}
let nonce = super::nonce::bind(
request.nonce,
processed.nonce,
execution,
response.span().start,
)?;
let window = assertion.update_window(time.now, time.utc, execution)?;
let revocation_time = assertion.revocation_relation(time.now, time.utc, execution)?;
Ok(TargetEvidence::Evidence(Evidence {
source: self,
assertion,
nonce,
window,
recipient,
revocation_time,
}))
}
}
#[cfg(test)]
mod tests;