1use super::{DelegateFacts, Lookup, ResponderRevocation, SignedResponse};
4use crate::Certificate;
5use crate::algorithm::digest::AsnDigest;
6use crate::error::{CallbackResult, Error, Result};
7use crate::path::WorkingKey;
8use structured_asn1::memory::{MemoryError, Work};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum AuthoritySource {
13 Issuer,
15 Delegate {
17 revocation: ResponderRevocation,
19 },
20 Configured,
23}
24
25#[derive(Debug, Clone, Copy, PartialEq, Eq)]
27pub enum AuthorityFailure {
28 IssuerKeyBinding,
30 Signer,
32 EffectiveKey,
34 KeyUsage,
36}
37
38#[derive(Debug, Clone, Copy)]
41pub struct ScopedResponse<'r, 'a> {
42 signed: SignedResponse<'r, 'a>,
43 issuer: &'r Certificate<'a>,
44 issuing_key: Option<WorkingKey<'a>>,
48 source: AuthoritySource,
49}
50
51impl<'r, 'a> ScopedResponse<'r, 'a> {
52 #[must_use]
54 pub fn signed(self) -> SignedResponse<'r, 'a> {
55 self.signed
56 }
57 #[must_use]
59 pub fn issuer(self) -> &'r Certificate<'a> {
60 self.issuer
61 }
62 #[must_use]
66 pub fn issuing_key(self) -> Option<WorkingKey<'a>> {
67 self.issuing_key
68 }
69 #[must_use]
71 pub fn source(self) -> AuthoritySource {
72 self.source
73 }
74 pub fn find<
76 't,
77 'd,
78 T: crate::revocation::RevocationTarget<'t>,
79 D: AsnDigest<'d> + ?Sized,
80 E: Work,
81 >(
82 self,
83 target: &T,
84 digest: &'d D,
85 execution: &E,
86 ) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
87 self.signed.find(target, self.issuer, digest, execution)
88 }
89}
90
91#[derive(Debug, Clone, Copy)]
93#[expect(
94 clippy::large_enum_variant,
95 reason = "return the original response and actual issuing key in-place without a new allocation during authentication"
96)]
97pub enum AuthorityBinding<'r, 'a> {
98 Bound(ScopedResponse<'r, 'a>),
100 Rejected(AuthorityFailure),
102}
103
104fn equal_bytes<E: Work>(left: &[u8], right: &[u8], execution: &E, at: u64) -> Result<bool> {
105 let work = u64::try_from(left.len().max(right.len()))
106 .ok()
107 .and_then(|n| n.checked_mul(2))
108 .and_then(|n| n.checked_add(1))
109 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.authority", at))?;
110 execution
111 .charge_work(work)
112 .map_err(|reason| Error::resource(reason, "ocspResponse.authority", at))?;
113 Ok(left == right)
114}
115
116fn equal_key<E: Work>(
117 left: WorkingKey<'_>,
118 right: WorkingKey<'_>,
119 execution: &E,
120 at: u64,
121) -> Result<bool> {
122 if !equal_bytes(left.key().as_bytes(), right.key().as_bytes(), execution, at)? {
123 return Ok(false);
124 }
125 match (left.parameters(), right.parameters()) {
126 (None, None) => Ok(true),
127 (Some(left), Some(right)) => equal_bytes(left, right, execution, at),
128 _ => Ok(false),
129 }
130}
131
132pub(super) fn signing_usage<E: Work>(certificate: &Certificate<'_>, execution: &E) -> Result<bool> {
133 Ok(certificate
137 .tbs()
138 .extensions()
139 .key_usage(execution)?
140 .is_none_or(|usage| usage.digital_signature() || usage.content_commitment()))
141}
142
143impl<'r, 'a> SignedResponse<'r, 'a> {
144 pub fn bind_issuer<E: Work>(
149 self,
150 issuer: &'r Certificate<'a>,
151 issuing_key: WorkingKey<'a>,
152 execution: &E,
153 ) -> Result<AuthorityBinding<'r, 'a>> {
154 let at = self.response().span().start;
155 if !equal_bytes(
156 issuer.tbs().public_key().as_bytes(),
157 issuing_key.key().as_bytes(),
158 execution,
159 at,
160 )? {
161 return Ok(AuthorityBinding::Rejected(
162 AuthorityFailure::IssuerKeyBinding,
163 ));
164 }
165 if !equal_bytes(
166 self.signer().tbs().public_key().as_bytes(),
167 issuer.tbs().public_key().as_bytes(),
168 execution,
169 at,
170 )? || !self
171 .signer()
172 .tbs()
173 .subject()
174 .matches(issuer.tbs().subject(), execution)?
175 {
176 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
177 }
178 if !equal_key(self.key(), issuing_key, execution, at)? {
179 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
180 }
181 if !signing_usage(issuer, execution)? {
182 return Ok(AuthorityBinding::Rejected(AuthorityFailure::KeyUsage));
183 }
184 Ok(AuthorityBinding::Bound(ScopedResponse {
185 signed: self,
186 issuer,
187 issuing_key: Some(issuing_key),
188 source: AuthoritySource::Issuer,
189 }))
190 }
191
192 pub fn bind_delegate<E: Work>(
195 self,
196 facts: DelegateFacts<'a>,
197 execution: &E,
198 ) -> Result<AuthorityBinding<'r, 'a>>
199 where
200 'a: 'r,
201 {
202 let at = self.response().span().start;
203 if !equal_bytes(
204 self.signer().as_bytes(),
205 facts.responder().as_bytes(),
206 execution,
207 at,
208 )? {
209 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
210 }
211 if !equal_key(self.key(), facts.key(), execution, at)? {
212 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
213 }
214 Ok(AuthorityBinding::Bound(ScopedResponse {
215 signed: self,
216 issuer: facts.issuer(),
217 issuing_key: Some(facts.issuing_key()),
218 source: AuthoritySource::Delegate {
219 revocation: facts.revocation(),
220 },
221 }))
222 }
223
224 #[must_use]
229 pub fn trust_for_issuer(self, issuer: &'r Certificate<'a>) -> ScopedResponse<'r, 'a> {
230 ScopedResponse {
231 signed: self,
232 issuer,
233 issuing_key: None,
234 source: AuthoritySource::Configured,
235 }
236 }
237}