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
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
//! Explicit issuer scope, separate from request/time/revocation acceptance.
use super::{DelegateFacts, Lookup, ResponderRevocation, SignedResponse};
use crate::Certificate;
use crate::algorithm::digest::AsnDigest;
use crate::error::{CallbackResult, Error, Result};
use crate::path::WorkingKey;
use structured_asn1::memory::{MemoryError, Work};
/// How the caller-bound signer is related to the selected target issuer.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthoritySource {
/// The actual target issuing key itself signed the response.
Issuer,
/// Common local path and explicit purpose established direct delegation.
Delegate {
/// The delegate's revocation exemption or unresolved checking fact.
revocation: ResponderRevocation,
},
/// Caller explicitly selected a locally configured trusted responder for
/// this issuer. This branch is never selected from response content.
Configured,
}
/// Completed source/key-scope rejection, distinct from resource errors.
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthorityFailure {
/// Effective issuing key is not bound to the supplied issuer certificate.
IssuerKeyBinding,
/// Signed response's actual signer does not match the selected authority.
Signer,
/// Signature was checked with a different effective parameter state.
EffectiveKey,
/// Present keyUsage allows neither applicable object-signing use.
KeyUsage,
}
/// Signature and issuer-scope facts. Caller trust provenance, intended recipient,
/// pending delegate revocation, nonce, time and critical demands remain explicit.
#[derive(Debug, Clone, Copy)]
pub struct ScopedResponse<'r, 'a> {
signed: SignedResponse<'r, 'a>,
issuer: &'r Certificate<'a>,
// Keep the actual input SPKI provenance as well as inherited parameters.
// Rebuilding from an equal issuer certificate could change parser/source
// identity and its lazy-reading ceilings, even if its DER bytes agree.
issuing_key: Option<WorkingKey<'a>>,
source: AuthoritySource,
}
impl<'r, 'a> ScopedResponse<'r, 'a> {
/// Original authenticated response/signing source.
#[must_use]
pub fn signed(self) -> SignedResponse<'r, 'a> {
self.signed
}
/// Selected target issuer, never inferred from included certificates.
#[must_use]
pub fn issuer(self) -> &'r Certificate<'a> {
self.issuer
}
/// Actual effective issuer key used by issuer-signature or delegation
/// authentication. Explicit configured-responder trust does not run that
/// operation and honestly returns None, never a fabricated standing key.
#[must_use]
pub fn issuing_key(self) -> Option<WorkingKey<'a>> {
self.issuing_key
}
/// Explicit scope source, including unresolved delegated revocation.
#[must_use]
pub fn source(self) -> AuthoritySource {
self.source
}
/// Select a target assertion using the same issuer as the signer binding.
pub fn find<'d, D: AsnDigest<'d> + ?Sized, E: Work>(
self,
target: &Certificate<'_>,
digest: &'d D,
execution: &E,
) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
self.signed.find(target, self.issuer, digest, execution)
}
}
/// Completed issuer-scope binding outcome.
#[derive(Debug, Clone, Copy)]
#[expect(
clippy::large_enum_variant,
reason = "return the original response and actual issuing key in-place without a new allocation during authentication"
)]
pub enum AuthorityBinding<'r, 'a> {
/// Scope established; this is not the final status-acceptance decision.
Bound(ScopedResponse<'r, 'a>),
/// Completed mismatch.
Rejected(AuthorityFailure),
}
fn equal_bytes<E: Work>(left: &[u8], right: &[u8], execution: &E, at: u64) -> Result<bool> {
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.authority", at))?;
execution
.charge_work(work)
.map_err(|reason| Error::resource(reason, "ocspResponse.authority", at))?;
Ok(left == right)
}
fn equal_key<E: Work>(
left: WorkingKey<'_>,
right: WorkingKey<'_>,
execution: &E,
at: u64,
) -> Result<bool> {
if !equal_bytes(left.key().as_bytes(), right.key().as_bytes(), execution, at)? {
return Ok(false);
}
match (left.parameters(), right.parameters()) {
(None, None) => Ok(true),
(Some(left), Some(right)) => equal_bytes(left, right, execution, at),
_ => Ok(false),
}
}
pub(super) fn signing_usage<E: Work>(certificate: &Certificate<'_>, execution: &E) -> Result<bool> {
// RFC5280 §§4.2.1.3/4.2.1.12: OCSP is neither a certificate nor a CRL
// signature; digitalSignature and/or contentCommitment are applicable.
// <https://www.rfc-editor.org/rfc/rfc5280#section-4.2.1.12>
Ok(certificate
.tbs()
.extensions()
.key_usage(execution)?
.is_none_or(|usage| usage.digital_signature() || usage.content_commitment()))
}
impl<'r, 'a> SignedResponse<'r, 'a> {
/// Bind issuer-signed response to the actual authenticated target issuing
/// key, including inherited parameters. Caller supplies issuer trust and
/// current validity from its target path; a matching DN alone is inadequate.
/// RFC6960 §4.2.2.2: <https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2>
pub fn bind_issuer<E: Work>(
self,
issuer: &'r Certificate<'a>,
issuing_key: WorkingKey<'a>,
execution: &E,
) -> Result<AuthorityBinding<'r, 'a>> {
let at = self.response().span().start;
if !equal_bytes(
issuer.tbs().public_key().as_bytes(),
issuing_key.key().as_bytes(),
execution,
at,
)? {
return Ok(AuthorityBinding::Rejected(
AuthorityFailure::IssuerKeyBinding,
));
}
if !equal_bytes(
self.signer().tbs().public_key().as_bytes(),
issuer.tbs().public_key().as_bytes(),
execution,
at,
)? || !self
.signer()
.tbs()
.subject()
.matches(issuer.tbs().subject(), execution)?
{
return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
}
if !equal_key(self.key(), issuing_key, execution, at)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
}
if !signing_usage(issuer, execution)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::KeyUsage));
}
Ok(AuthorityBinding::Bound(ScopedResponse {
signed: self,
issuer,
issuing_key: Some(issuing_key),
source: AuthoritySource::Issuer,
}))
}
/// Attach completed direct local delegation to this exact actual signer.
/// An unresolved revocation fact remains unresolved in the returned source.
pub fn bind_delegate<E: Work>(
self,
facts: DelegateFacts<'a>,
execution: &E,
) -> Result<AuthorityBinding<'r, 'a>>
where
'a: 'r,
{
let at = self.response().span().start;
if !equal_bytes(
self.signer().as_bytes(),
facts.responder().as_bytes(),
execution,
at,
)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
}
if !equal_key(self.key(), facts.key(), execution, at)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
}
Ok(AuthorityBinding::Bound(ScopedResponse {
signed: self,
issuer: facts.issuer(),
issuing_key: Some(facts.issuing_key()),
source: AuthoritySource::Delegate {
revocation: facts.revocation(),
},
}))
}
/// Explicit caller assertion of locally configured responder trust for this
/// target issuer, the separate RFC6960 §4.2.2.2 branch. Never call this merely
/// because a certificate was included or carried an OCSP EKU. No traversal,
/// allocation, trust discovery or policy selection occurs in this operation.
#[must_use]
pub fn trust_for_issuer(self, issuer: &'r Certificate<'a>) -> ScopedResponse<'r, 'a> {
ScopedResponse {
signed: self,
issuer,
issuing_key: None,
source: AuthoritySource::Configured,
}
}
}