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<'d, D: AsnDigest<'d> + ?Sized, E: Work>(
76 self,
77 target: &Certificate<'_>,
78 digest: &'d D,
79 execution: &E,
80 ) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
81 self.signed.find(target, self.issuer, digest, execution)
82 }
83}
84
85#[derive(Debug, Clone, Copy)]
87#[expect(
88 clippy::large_enum_variant,
89 reason = "return the original response and actual issuing key in-place without a new allocation during authentication"
90)]
91pub enum AuthorityBinding<'r, 'a> {
92 Bound(ScopedResponse<'r, 'a>),
94 Rejected(AuthorityFailure),
96}
97
98fn equal_bytes<E: Work>(left: &[u8], right: &[u8], execution: &E, at: u64) -> Result<bool> {
99 let work = u64::try_from(left.len().max(right.len()))
100 .ok()
101 .and_then(|n| n.checked_mul(2))
102 .and_then(|n| n.checked_add(1))
103 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.authority", at))?;
104 execution
105 .charge_work(work)
106 .map_err(|reason| Error::resource(reason, "ocspResponse.authority", at))?;
107 Ok(left == right)
108}
109
110fn equal_key<E: Work>(
111 left: WorkingKey<'_>,
112 right: WorkingKey<'_>,
113 execution: &E,
114 at: u64,
115) -> Result<bool> {
116 if !equal_bytes(left.key().as_bytes(), right.key().as_bytes(), execution, at)? {
117 return Ok(false);
118 }
119 match (left.parameters(), right.parameters()) {
120 (None, None) => Ok(true),
121 (Some(left), Some(right)) => equal_bytes(left, right, execution, at),
122 _ => Ok(false),
123 }
124}
125
126pub(super) fn signing_usage<E: Work>(certificate: &Certificate<'_>, execution: &E) -> Result<bool> {
127 Ok(certificate
131 .tbs()
132 .extensions()
133 .key_usage(execution)?
134 .is_none_or(|usage| usage.digital_signature() || usage.content_commitment()))
135}
136
137impl<'r, 'a> SignedResponse<'r, 'a> {
138 pub fn bind_issuer<E: Work>(
143 self,
144 issuer: &'r Certificate<'a>,
145 issuing_key: WorkingKey<'a>,
146 execution: &E,
147 ) -> Result<AuthorityBinding<'r, 'a>> {
148 let at = self.response().span().start;
149 if !equal_bytes(
150 issuer.tbs().public_key().as_bytes(),
151 issuing_key.key().as_bytes(),
152 execution,
153 at,
154 )? {
155 return Ok(AuthorityBinding::Rejected(
156 AuthorityFailure::IssuerKeyBinding,
157 ));
158 }
159 if !equal_bytes(
160 self.signer().tbs().public_key().as_bytes(),
161 issuer.tbs().public_key().as_bytes(),
162 execution,
163 at,
164 )? || !self
165 .signer()
166 .tbs()
167 .subject()
168 .matches(issuer.tbs().subject(), execution)?
169 {
170 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
171 }
172 if !equal_key(self.key(), issuing_key, execution, at)? {
173 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
174 }
175 if !signing_usage(issuer, execution)? {
176 return Ok(AuthorityBinding::Rejected(AuthorityFailure::KeyUsage));
177 }
178 Ok(AuthorityBinding::Bound(ScopedResponse {
179 signed: self,
180 issuer,
181 issuing_key: Some(issuing_key),
182 source: AuthoritySource::Issuer,
183 }))
184 }
185
186 pub fn bind_delegate<E: Work>(
189 self,
190 facts: DelegateFacts<'a>,
191 execution: &E,
192 ) -> Result<AuthorityBinding<'r, 'a>>
193 where
194 'a: 'r,
195 {
196 let at = self.response().span().start;
197 if !equal_bytes(
198 self.signer().as_bytes(),
199 facts.responder().as_bytes(),
200 execution,
201 at,
202 )? {
203 return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
204 }
205 if !equal_key(self.key(), facts.key(), execution, at)? {
206 return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
207 }
208 Ok(AuthorityBinding::Bound(ScopedResponse {
209 signed: self,
210 issuer: facts.issuer(),
211 issuing_key: Some(facts.issuing_key()),
212 source: AuthoritySource::Delegate {
213 revocation: facts.revocation(),
214 },
215 }))
216 }
217
218 #[must_use]
223 pub fn trust_for_issuer(self, issuer: &'r Certificate<'a>) -> ScopedResponse<'r, 'a> {
224 ScopedResponse {
225 signed: self,
226 issuer,
227 issuing_key: None,
228 source: AuthoritySource::Configured,
229 }
230 }
231}