1use super::{BasicResponse, Digest, ResponderMatch};
4use crate::error::CallbackError;
5use crate::path::{Verified, Verify, WorkingKey};
6use crate::{Certificate, Error};
7use core::fmt;
8use structured_asn1::memory::{MemoryError, Work};
9
10#[derive(Debug)]
12pub enum SignatureError<V, D> {
13 Processing(Error),
15 Verification(V),
17 Digest(D),
19}
20
21impl<V, D> From<Error> for SignatureError<V, D> {
22 fn from(error: Error) -> Self {
23 Self::Processing(error)
24 }
25}
26
27impl<V: fmt::Display, D: fmt::Display> fmt::Display for SignatureError<V, D> {
28 fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
29 match self {
30 Self::Processing(error) => error.fmt(formatter),
31 Self::Verification(error) => write!(formatter, "signature verifier: {error}"),
32 Self::Digest(error) => write!(formatter, "digest provider: {error}"),
33 }
34 }
35}
36
37#[cfg(feature = "std")]
38impl<V: std::error::Error + 'static, D: std::error::Error + 'static> std::error::Error
39 for SignatureError<V, D>
40{
41 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
42 match self {
43 Self::Processing(error) => Some(error),
44 Self::Verification(error) => Some(error),
45 Self::Digest(error) => Some(error),
46 }
47 }
48}
49
50#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SignatureFailure {
53 KeyBinding,
55 ResponderIdentity,
57 DigestWidth,
59 Signature,
61}
62
63#[derive(Debug, Clone, Copy)]
66pub struct SignedResponse<'r, 'a> {
67 response: &'r BasicResponse<'a>,
68 signer: &'r Certificate<'a>,
69 key: WorkingKey<'a>,
70}
71
72impl<'r, 'a> SignedResponse<'r, 'a> {
73 #[must_use]
75 pub fn response(self) -> &'r BasicResponse<'a> {
76 self.response
77 }
78 #[must_use]
80 pub fn signer(self) -> &'r Certificate<'a> {
81 self.signer
82 }
83 #[must_use]
85 pub fn key(self) -> WorkingKey<'a> {
86 self.key
87 }
88}
89
90#[derive(Debug)]
92pub enum SignatureAuthentication<'r, 'a> {
93 Signed(SignedResponse<'r, 'a>),
95 Rejected(SignatureFailure),
97 UnsupportedDigest,
99 UnsupportedSignature,
101}
102
103impl<'a> BasicResponse<'a> {
104 pub fn authenticate_signature<'r, V: Verify<E>, D: Digest<E>, E: Work>(
109 &'r self,
110 signer: &'r Certificate<'a>,
111 key: WorkingKey<'a>,
112 verifier: &V,
113 digest: &D,
114 execution: &E,
115 ) -> core::result::Result<SignatureAuthentication<'r, 'a>, SignatureError<V::Error, D::Error>>
116 {
117 const FIELD: &str = "ocspResponse.signature";
118 let charge = |work| {
119 execution
120 .charge_work(work)
121 .map_err(|reason| Error::resource(reason, FIELD, self.span.start))
122 };
123 let encoded = key.key().as_bytes();
124 let actual = signer.tbs().public_key().as_bytes();
125 let work = u64::try_from(encoded.len().max(actual.len()))
126 .ok()
127 .and_then(|n| n.checked_mul(2))
128 .and_then(|n| n.checked_add(1))
129 .ok_or_else(|| Error::resource(MemoryError::WorkExceeded, FIELD, self.span.start))?;
130 charge(work)?;
131 if encoded != actual {
132 return Ok(SignatureAuthentication::Rejected(
133 SignatureFailure::KeyBinding,
134 ));
135 }
136 let identity = self
137 .responder
138 .matches_certificate(signer, digest, execution, FIELD, self.span.start)
139 .map_err(|cause| match cause {
140 CallbackError::Processing(cause) => SignatureError::Processing(cause),
141 CallbackError::Callback(cause) => SignatureError::Digest(cause),
142 })?;
143 match identity {
144 ResponderMatch::Matches => {}
145 ResponderMatch::Differs => {
146 return Ok(SignatureAuthentication::Rejected(
147 SignatureFailure::ResponderIdentity,
148 ));
149 }
150 ResponderMatch::MalformedHashWidth => {
151 return Ok(SignatureAuthentication::Rejected(
152 SignatureFailure::DigestWidth,
153 ));
154 }
155 ResponderMatch::UnsupportedHash => {
156 return Ok(SignatureAuthentication::UnsupportedDigest);
157 }
158 }
159 charge(1)?;
160 match verifier
161 .verify(&self.algorithm, key, self.signed, self.signature, execution)
162 .map_err(SignatureError::Verification)?
163 {
164 Verified::Yes => Ok(SignatureAuthentication::Signed(SignedResponse {
165 response: self,
166 signer,
167 key,
168 })),
169 Verified::No => Ok(SignatureAuthentication::Rejected(
170 SignatureFailure::Signature,
171 )),
172 Verified::Unsupported => Ok(SignatureAuthentication::UnsupportedSignature),
173 }
174 }
175}
176
177#[cfg(test)]
178mod tests {
179 use super::super::DigestAlgorithm;
180 use super::*;
181 use crate::fixtures::{CertificateParts, bits, context, extension, octets, seq, tlv};
182 use core::{cell::Cell, num::NonZeroUsize};
183 use structured_asn1::memory::Memory;
184 use structured_asn1::{BitString, Parser};
185
186 struct Provider<'a> {
187 key: &'a [u8],
188 signed: &'a [u8],
189 hashes: Cell<usize>,
190 signatures: Cell<usize>,
191 width: usize,
192 digest_answer: Option<bool>,
193 signature_answer: Verified,
194 failure: bool,
195 }
196 impl Digest for Provider<'_> {
197 type Handle = ();
198 type Error = &'static str;
199 fn resolve(
200 &self,
201 algorithm: DigestAlgorithm<'_>,
202 _: &Memory,
203 ) -> Result<Option<super::super::ResolvedDigest<()>>, Self::Error> {
204 assert!(matches!(algorithm, DigestAlgorithm::Sha1));
205 if self.failure {
206 return Err("digest refusal");
207 }
208 Ok(self.digest_answer.map(|_| super::super::ResolvedDigest {
209 handle: (),
210 width: NonZeroUsize::new(self.width).unwrap(),
211 }))
212 }
213 fn matches(
214 &self,
215 (): (),
216 input: &[u8],
217 expected: &[u8],
218 _: &Memory,
219 ) -> Result<bool, Self::Error> {
220 self.hashes.set(self.hashes.get() + 1);
221 assert_eq!(input.as_ptr(), self.key.as_ptr());
222 assert_eq!(input, self.key);
223 assert_eq!(expected, &[0x33; 20]);
224 Ok(self.digest_answer.unwrap())
225 }
226 }
227 impl Verify for Provider<'_> {
228 type Error = &'static str;
229 fn verify(
230 &self,
231 _: &crate::AlgorithmIdentifier<'_>,
232 _: WorkingKey<'_>,
233 signed: &[u8],
234 _: BitString<'_>,
235 _: &Memory,
236 ) -> Result<Verified, Self::Error> {
237 self.signatures.set(self.signatures.get() + 1);
238 assert_eq!(signed.as_ptr(), self.signed.as_ptr());
239 assert_eq!(signed, self.signed);
240 Ok(self.signature_answer)
241 }
242 }
243
244 #[test]
245 fn responder_identity_query_reuses_original_key_bits_without_signature_or_output_copy() {
246 use crate::error::CallbackError;
250 use crate::ocsp::{ResponderId, ResponderMatch};
251 let bytes = CertificateParts::default().build();
252 let memory = crate::fixtures::memory();
253 let parser = Parser::der(memory.clone());
254 let certificate = Certificate::read(&parser, &bytes, 100).unwrap();
255 let mut provider = Provider {
256 key: certificate.tbs().public_key().key().as_bytes(),
257 signed: &[],
258 hashes: Cell::new(0),
259 signatures: Cell::new(0),
260 width: 20,
261 digest_answer: Some(true),
262 signature_answer: Verified::Yes,
263 failure: false,
264 };
265 let selector = ResponderId::KeyHash(&[0x33; 20]);
266 let held = memory.used();
267 assert_eq!(
268 selector
269 .matches_certificate(&certificate, &provider, &memory, "original.byKey", 900)
270 .unwrap(),
271 ResponderMatch::Matches
272 );
273 assert_eq!((provider.hashes.get(), provider.signatures.get()), (1, 0));
274 assert_eq!(memory.used(), held);
275 provider.failure = true;
276 assert_eq!(
277 ResponderId::KeyHash(&[0x33; 19])
278 .matches_certificate(&certificate, &provider, &memory, "original.byKey", 900)
279 .unwrap(),
280 ResponderMatch::MalformedHashWidth
281 );
282 assert!(matches!(
283 selector.matches_certificate(&certificate, &provider, &memory, "original.byKey", 900),
284 Err(CallbackError::Callback("digest refusal"))
285 ));
286 let stopped = Memory::new(alloc::sync::Arc::new(
287 structured_asn1::memory::MemoryAccount::new(0, 0),
288 ));
289 assert!(
290 matches!(selector.matches_certificate(&certificate,&provider,&stopped,"original.byKey",900),Err(CallbackError::Processing(error)) if error.is_resource() && error.offset()==900)
291 );
292 assert_eq!((provider.hashes.get(), provider.signatures.get()), (1, 0));
293 assert_eq!(memory.used(), held);
294 }
295
296 #[test]
297 fn issuer_binding_enforces_object_signing_key_usage() {
298 for (usage, permitted) in [
301 (None, true),
302 (Some([7, 0x80]), true),
303 (Some([6, 0x40]), true),
304 (Some([1, 0x06]), false),
305 (Some([5, 0x20]), false),
306 ] {
307 let certificate_bytes = CertificateParts {
308 extensions: usage
309 .map(|value| seq(&[extension(crate::oid::KEY_USAGE, true, &tlv(3, &value))])),
310 ..CertificateParts::default()
311 }
312 .build();
313 let execution = crate::fixtures::memory();
314 let parser = Parser::der(execution.clone());
315 let issuer = Certificate::read(&parser, &certificate_bytes, 100).unwrap();
316 let algorithm = issuer.signature_algorithm().as_bytes();
317 let timestamp = tlv(24, b"20261012120000Z");
318 let id = seq(&[
319 algorithm.to_vec(),
320 octets(&[0x11; 20]),
321 octets(&[0x22; 20]),
322 tlv(2, &[1]),
323 ]);
324 let single = seq(&[id, tlv(0x80, &[]), timestamp.clone()]);
325 let data = seq(&[
326 context(2, &[octets(&[0x33; 20])]),
327 timestamp,
328 seq(&[single]),
329 ]);
330 let bytes = seq(&[data, algorithm.to_vec(), bits(&[0xab])]);
331 let mut reader = parser.read(&bytes, 700).unwrap();
332 let root = reader.next_element().unwrap().unwrap();
333 let response = super::super::read_basic(&reader, &root).unwrap();
334 let key = WorkingKey::standing(issuer.tbs().public_key());
335 let provider = Provider {
336 key: issuer.tbs().public_key().key().as_bytes(),
337 signed: response.signed_bytes(),
338 hashes: Cell::new(0),
339 signatures: Cell::new(0),
340 width: 20,
341 digest_answer: Some(true),
342 signature_answer: Verified::Yes,
343 failure: false,
344 };
345 let SignatureAuthentication::Signed(signed) = response
346 .authenticate_signature(&issuer, key, &provider, &provider, &execution)
347 .unwrap()
348 else {
349 panic!("signature fixture rejected");
350 };
351 let result = signed.bind_issuer(&issuer, key, &execution).unwrap();
352 assert_eq!(
353 matches!(result, super::super::AuthorityBinding::Bound(_)),
354 permitted
355 );
356 if !permitted {
357 assert!(matches!(
358 result,
359 super::super::AuthorityBinding::Rejected(
360 super::super::AuthorityFailure::KeyUsage
361 )
362 ));
363 }
364 assert_eq!(provider.signatures.get(), 1);
365 }
366 }
367
368 #[test]
369 fn actual_signer_precedes_hash_and_original_response_data_verification() {
370 let certificate_bytes = CertificateParts::default().build();
373 let execution = crate::fixtures::memory();
374 let parser = Parser::der(execution.clone());
375 let signer = Certificate::read(&parser, &certificate_bytes, 100).unwrap();
376 let algorithm = signer.signature_algorithm().as_bytes();
377 let timestamp = tlv(24, b"20261012120000Z");
378 let id = seq(&[
379 algorithm.to_vec(),
380 octets(&[0x11; 20]),
381 octets(&[0x22; 20]),
382 tlv(2, &[1]),
383 ]);
384 let single = seq(&[id, tlv(0x80, &[]), timestamp.clone()]);
385 let data = seq(&[
386 context(2, &[octets(&[0x33; 20])]),
387 timestamp,
388 seq(&[single]),
389 ]);
390 let bytes = seq(&[data, algorithm.to_vec(), bits(&[0xab])]);
391 let mut reader = parser.read(&bytes, 700).unwrap();
392 let element = reader.next_element().unwrap().unwrap();
393 let response = super::super::read_basic(&reader, &element).unwrap();
394 let key = WorkingKey::standing(signer.tbs().public_key());
395 let mut provider = Provider {
396 key: signer.tbs().public_key().key().as_bytes(),
397 signed: response.signed_bytes(),
398 hashes: Cell::new(0),
399 signatures: Cell::new(0),
400 width: 20,
401 digest_answer: Some(true),
402 signature_answer: Verified::Yes,
403 failure: false,
404 };
405 let retained = execution.used();
406 let SignatureAuthentication::Signed(proof) = response
407 .authenticate_signature(&signer, key, &provider, &provider, &execution)
408 .unwrap()
409 else {
410 panic!("signature not authenticated");
411 };
412 assert!(core::ptr::eq(proof.response(), &raw const response));
413 assert!(core::ptr::eq(proof.signer(), &raw const signer));
414 assert_eq!(provider.signatures.get(), 1);
415 assert_eq!(execution.used(), retained);
416 provider.signatures.set(0);
417 provider.width = 19;
418 assert!(matches!(
419 response
420 .authenticate_signature(&signer, key, &provider, &provider, &execution)
421 .unwrap(),
422 SignatureAuthentication::Rejected(SignatureFailure::DigestWidth)
423 ));
424 assert_eq!(provider.signatures.get(), 0);
425 provider.width = 20;
426 provider.digest_answer = Some(false);
427 assert!(matches!(
428 response
429 .authenticate_signature(&signer, key, &provider, &provider, &execution)
430 .unwrap(),
431 SignatureAuthentication::Rejected(SignatureFailure::ResponderIdentity)
432 ));
433 assert_eq!(provider.signatures.get(), 0);
434 provider.digest_answer = None;
435 assert!(matches!(
436 response
437 .authenticate_signature(&signer, key, &provider, &provider, &execution)
438 .unwrap(),
439 SignatureAuthentication::UnsupportedDigest
440 ));
441 provider.failure = true;
442 assert!(matches!(
443 response.authenticate_signature(&signer, key, &provider, &provider, &execution),
444 Err(SignatureError::Digest("digest refusal"))
445 ));
446 let unfunded = Memory::new(alloc::sync::Arc::new(
447 structured_asn1::memory::MemoryAccount::new(0, 0),
448 ));
449 assert!(
450 matches!(response.authenticate_signature(&signer, key, &provider, &provider, &unfunded), Err(SignatureError::Processing(error)) if error.is_resource() && error.offset() == 700)
451 );
452 }
453}