1use super::{DelegateFacts, Digest, Lookup, ResponderRevocation, SignedResponse};
4use crate::Certificate;
5use crate::error::{CallbackResult, Error, Result};
6use crate::path::WorkingKey;
7use structured_asn1::memory::{MemoryError, Work};
8
9#[derive(Debug, Clone, Copy, PartialEq, Eq)]
11pub enum AuthoritySource {
12 Issuer,
14 Delegate {
16 revocation: ResponderRevocation,
18 },
19 Configured,
22}
23
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
26pub enum AuthorityFailure {
27 IssuerKeyBinding,
29 Signer,
31 EffectiveKey,
33 KeyUsage,
35}
36
37#[derive(Debug, Clone, Copy)]
40pub struct ScopedResponse<'r, 'a> {
41 signed: SignedResponse<'r, 'a>,
42 issuer: &'r Certificate<'a>,
43 issuing_key: Option<WorkingKey<'a>>,
47 source: AuthoritySource,
48}
49
50impl<'r, 'a> ScopedResponse<'r, 'a> {
51 #[must_use]
53 pub fn signed(self) -> SignedResponse<'r, 'a> {
54 self.signed
55 }
56 #[must_use]
58 pub fn issuer(self) -> &'r Certificate<'a> {
59 self.issuer
60 }
61 #[must_use]
65 pub fn issuing_key(self) -> Option<WorkingKey<'a>> {
66 self.issuing_key
67 }
68 #[must_use]
70 pub fn source(self) -> AuthoritySource {
71 self.source
72 }
73 pub fn find<D: Digest<E>, E: Work>(
75 self,
76 target: &Certificate<'_>,
77 digest: &D,
78 execution: &E,
79 ) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
80 self.signed.find(target, self.issuer, digest, execution)
81 }
82}
83
84#[derive(Debug, Clone, Copy)]
86#[expect(
87 clippy::large_enum_variant,
88 reason = "return the original response and actual issuing key in-place without a new allocation during authentication"
89)]
90pub enum AuthorityBinding<'r, 'a> {
91 Bound(ScopedResponse<'r, 'a>),
93 Rejected(AuthorityFailure),
95}
96
97fn equal_bytes<E: Work>(left: &[u8], right: &[u8], execution: &E, at: u64) -> Result<bool> {
98 let work = u64::try_from(left.len().max(right.len()))
99 .ok()
100 .and_then(|n| n.checked_mul(2))
101 .and_then(|n| n.checked_add(1))
102 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.authority", at))?;
103 execution
104 .charge_work(work)
105 .map_err(|reason| Error::resource(reason, "ocspResponse.authority", at))?;
106 Ok(left == right)
107}
108
109fn equal_key<E: Work>(
110 left: WorkingKey<'_>,
111 right: WorkingKey<'_>,
112 execution: &E,
113 at: u64,
114) -> Result<bool> {
115 if !equal_bytes(left.key().as_bytes(), right.key().as_bytes(), execution, at)? {
116 return Ok(false);
117 }
118 match (left.parameters(), right.parameters()) {
119 (None, None) => Ok(true),
120 (Some(left), Some(right)) => equal_bytes(left, right, execution, at),
121 _ => Ok(false),
122 }
123}
124
125pub(super) fn signing_usage<E: Work>(certificate: &Certificate<'_>, execution: &E) -> Result<bool> {
126 Ok(certificate
130 .tbs()
131 .extensions()
132 .key_usage(execution)?
133 .is_none_or(|usage| usage.digital_signature() || usage.content_commitment()))
134}
135
136impl<'r, 'a> SignedResponse<'r, 'a> {
137 pub fn bind_issuer<E: Work>(
142 self,
143 issuer: &'r Certificate<'a>,
144 issuing_key: WorkingKey<'a>,
145 execution: &E,
146 ) -> Result<AuthorityBinding<'r, 'a>> {
147 let at = self.response().span().start;
148 if !equal_bytes(
149 issuer.tbs().public_key().as_bytes(),
150 issuing_key.key().as_bytes(),
151 execution,
152 at,
153 )? {
154 return Ok(AuthorityBinding::Rejected(
155 AuthorityFailure::IssuerKeyBinding,
156 ));
157 }
158 if !equal_bytes(
159 self.signer().tbs().public_key().as_bytes(),
160 issuer.tbs().public_key().as_bytes(),
161 execution,
162 at,
163 )? || !self
164 .signer()
165 .tbs()
166 .subject()
167 .matches(issuer.tbs().subject(), execution)?
168 {
169 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
170 }
171 if !equal_key(self.key(), issuing_key, execution, at)? {
172 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
173 }
174 if !signing_usage(issuer, execution)? {
175 return Ok(AuthorityBinding::Rejected(AuthorityFailure::KeyUsage));
176 }
177 Ok(AuthorityBinding::Bound(ScopedResponse {
178 signed: self,
179 issuer,
180 issuing_key: Some(issuing_key),
181 source: AuthoritySource::Issuer,
182 }))
183 }
184
185 pub fn bind_delegate<E: Work>(
188 self,
189 facts: DelegateFacts<'a>,
190 execution: &E,
191 ) -> Result<AuthorityBinding<'r, 'a>>
192 where
193 'a: 'r,
194 {
195 let at = self.response().span().start;
196 if !equal_bytes(
197 self.signer().as_bytes(),
198 facts.responder().as_bytes(),
199 execution,
200 at,
201 )? {
202 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
203 }
204 if !equal_key(self.key(), facts.key(), execution, at)? {
205 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
206 }
207 Ok(AuthorityBinding::Bound(ScopedResponse {
208 signed: self,
209 issuer: facts.issuer(),
210 issuing_key: Some(facts.issuing_key()),
211 source: AuthoritySource::Delegate {
212 revocation: facts.revocation(),
213 },
214 }))
215 }
216
217 #[must_use]
222 pub fn trust_for_issuer(self, issuer: &'r Certificate<'a>) -> ScopedResponse<'r, 'a> {
223 ScopedResponse {
224 signed: self,
225 issuer,
226 issuing_key: None,
227 source: AuthoritySource::Configured,
228 }
229 }
230}