1use super::{
4 Digest, ExtensionProcessing, Lookup, NonceBinding, RequestNonce, ScopedResponse,
5 SingleResponse, UpdateWindow,
6};
7use crate::error::{CallbackResult, Result};
8use crate::temporal::{LeapTable, Reading};
9use crate::{Certificate, Error};
10use structured_asn1::memory::{Memory, MemoryError};
11use structured_asn1::{Oid, Span};
12
13pub struct Request<'p, 'a> {
15 pub target: &'p Certificate<'a>,
17 pub nonce: Option<RequestNonce<'p>>,
19 pub intended_signer: Option<&'p Certificate<'a>>,
22}
23
24pub struct Evaluation<'p> {
26 pub now: Reading,
28 pub utc: &'p LeapTable<'p>,
30 pub execution: &'p Memory,
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum RecipientBinding {
37 NotSpecified,
39 Matches,
41}
42
43#[derive(Debug, Clone, Copy)]
47pub struct Evidence<'r, 'a> {
48 source: ScopedResponse<'r, 'a>,
49 assertion: &'r SingleResponse<'a>,
50 window: UpdateWindow,
51 nonce: NonceBinding,
52 recipient: RecipientBinding,
53 revocation_time: Option<crate::time::Relation>,
54}
55
56impl<'r, 'a> Evidence<'r, 'a> {
57 #[must_use]
59 pub fn source(self) -> ScopedResponse<'r, 'a> {
60 self.source
61 }
62 #[must_use]
64 pub fn assertion(self) -> &'r SingleResponse<'a> {
65 self.assertion
66 }
67 #[must_use]
69 pub fn window(self) -> UpdateWindow {
70 self.window
71 }
72 #[must_use]
74 pub fn nonce(self) -> NonceBinding {
75 self.nonce
76 }
77 #[must_use]
79 pub fn recipient(self) -> RecipientBinding {
80 self.recipient
81 }
82
83 #[must_use]
85 pub fn revocation_time(self) -> Option<crate::time::Relation> {
86 self.revocation_time
87 }
88}
89
90#[derive(Debug, Clone, Copy)]
92pub enum TargetEvidence<'r, 'a> {
93 Evidence(Evidence<'r, 'a>),
95 UnexpectedSigner,
97 Lookup(Lookup<'r, 'a>),
99 UnsupportedCritical {
101 kind: Oid<'a>,
103 span: Span,
105 },
106}
107
108fn exact_signer(
109 left: &Certificate<'_>,
110 right: &Certificate<'_>,
111 execution: &Memory,
112 at: u64,
113) -> Result<bool> {
114 let left = left.as_bytes();
115 let right = right.as_bytes();
116 let work = u64::try_from(left.len().max(right.len()))
117 .ok()
118 .and_then(|n| n.checked_mul(2))
119 .and_then(|n| n.checked_add(1))
120 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.recipient", at))?;
121 execution
122 .charge_work(work)
123 .map_err(|reason| Error::resource(reason, "ocspResponse.recipient", at))?;
124 Ok(left == right)
125}
126
127impl<'r, 'a> ScopedResponse<'r, 'a> {
128 pub fn evaluate<D: Digest>(
133 self,
134 request: &Request<'_, '_>,
135 time: &Evaluation<'_>,
136 digest: &D,
137 ) -> CallbackResult<TargetEvidence<'r, 'a>, D::Error> {
138 let execution = time.execution;
139 let response = self.signed().response();
140 let recipient = match request.intended_signer {
141 None => RecipientBinding::NotSpecified,
142 Some(expected) => {
143 if !exact_signer(
144 self.signed().signer(),
145 expected,
146 execution,
147 response.span().start,
148 )? {
149 return Ok(TargetEvidence::UnexpectedSigner);
150 }
151 RecipientBinding::Matches
152 }
153 };
154 let processed = response.process_extensions(execution)?;
155 if let ExtensionProcessing::Unsupported { kind, span } = processed.processing {
156 return Ok(TargetEvidence::UnsupportedCritical { kind, span });
157 }
158 let assertion = match self.find(request.target, digest, execution)? {
159 Lookup::Found(assertion) => assertion,
160 outcome => return Ok(TargetEvidence::Lookup(outcome)),
161 };
162 if let ExtensionProcessing::Unsupported { kind, span } =
163 assertion.check_extensions(execution)?
164 {
165 return Ok(TargetEvidence::UnsupportedCritical { kind, span });
166 }
167 let nonce = super::nonce::bind(
168 request.nonce,
169 processed.nonce,
170 execution,
171 response.span().start,
172 )?;
173 let window = assertion.update_window(time.now, time.utc, execution)?;
174 let revocation_time = assertion.revocation_relation(time.now, time.utc, execution)?;
175 Ok(TargetEvidence::Evidence(Evidence {
176 source: self,
177 assertion,
178 nonce,
179 window,
180 recipient,
181 revocation_time,
182 }))
183 }
184}
185
186#[cfg(test)]
187mod tests;