1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! Applicable assertions are selected without discarding duplicate evidence.
use super::{BasicResponse, CertId, CertMatch, SignedResponse, SingleResponse};
use crate::algorithm::digest::AsnDigest;
use crate::error::{CallbackResult, Error};
use crate::{Certificate, revocation::RevocationTarget};
use structured_asn1::memory::Work;
/// Identity lookup within an authenticated response, not a complete verdict.
#[derive(Debug, Clone, Copy)]
pub enum Lookup<'r, 'a> {
/// No assertion matches the target and actual issuer.
Missing,
/// One matching original assertion. Time/extensions/authorization still
/// have to be checked before interpreting its revocation status.
Found(&'r SingleResponse<'a>),
/// Multiple matching assertions are retained, including identical repeats.
/// Selection must not silently pick first or last.
Ambiguous {
/// First matching assertion in wire order.
first: &'r SingleResponse<'a>,
/// Next matching assertion in wire order.
second: &'r SingleResponse<'a>,
},
/// A candidate identity uses a hash capability unavailable to this caller.
UnsupportedHash(&'r SingleResponse<'a>),
/// Known fixed-output hash width contradicts the original `CertID` bytes.
MalformedHashWidth(&'r SingleResponse<'a>),
/// A candidate's hash identifier writes parameters its algorithm does not take.
MalformedHashAlgorithm(&'r SingleResponse<'a>),
}
impl<'r, 'a> SignedResponse<'r, 'a> {
/// Locate the target's assertion using actual target/issuer bytes and the
/// selected provider. RFC6960 ยง3.2(1) requires binding to the requested
/// certificate. The caller binds these inputs to its actual request and
/// checks nonce/intended recipient separately; identity alone is insufficient.
/// <https://www.rfc-editor.org/rfc/rfc6960#section-3.2>
pub fn find<'t, 'd, T: RevocationTarget<'t>, D: AsnDigest<'d> + ?Sized, E: Work>(
self,
target: &T,
issuer: &Certificate<'_>,
digest: &'d D,
execution: &E,
) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
locate(self.response(), execution, |id| {
id.matches(target, issuer, digest, execution)
})
}
/// Locate an actual sent wire `CertID` without rehashing certificates or
/// losing duplicates. Full digest identity/width checks use the same
/// source-aware comparison as the complete request batch binding.
pub fn find_id<'d, D: AsnDigest<'d> + ?Sized, E: Work>(
self,
requested: &CertId<'_>,
digest: &'d D,
execution: &E,
) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
locate(self.response(), execution, |id| {
id.matches_id(requested, digest, execution)
})
}
}
fn locate<'r, 'a, E, W: Work>(
original: &'r BasicResponse<'a>,
execution: &W,
mut compare: impl FnMut(CertId<'a>) -> CallbackResult<CertMatch, E>,
) -> CallbackResult<Lookup<'r, 'a>, E> {
let execution = &original.signature_algorithm().query_in(execution);
let mut first = None;
for response in original.responses() {
execution.charge_work(1).map_err(|reason| {
Error::resource(reason, "ocspResponse.responses", response.span().start)
})?;
match compare(response.id())? {
CertMatch::Differs => {}
CertMatch::UnsupportedHash => return Ok(Lookup::UnsupportedHash(response)),
CertMatch::MalformedHashWidth => return Ok(Lookup::MalformedHashWidth(response)),
CertMatch::MalformedHashAlgorithm => {
return Ok(Lookup::MalformedHashAlgorithm(response));
}
CertMatch::Matches => match first {
None => first = Some(response),
Some(first) => {
return Ok(Lookup::Ambiguous {
first,
second: response,
});
}
},
}
}
Ok(match first {
Some(response) => Lookup::Found(response),
None => Lookup::Missing,
})
}