Skip to main content

ferron/setup/
ocsp.rs

1use std::collections::HashMap;
2use std::sync::Arc;
3use std::time::{Duration, SystemTime};
4
5use anyhow::Context as _;
6use ferron_common::logging::LogMessage;
7use hyper::Request;
8use hyper_util::client::legacy::Client;
9use hyper_util::rt::TokioExecutor;
10use num_bigint::BigInt;
11use rasn::prelude::*;
12use rasn_ocsp::BasicOcspResponse;
13use rasn_ocsp::{CertId, OcspRequest, OcspResponse, OcspResponseStatus, Request as OcspInnerRequest, TbsRequest};
14use rustls::client::WebPkiServerVerifier;
15use rustls::server::{ClientHello, ResolvesServerCert};
16use rustls::sign::CertifiedKey;
17use rustls_pki_types::CertificateDer;
18use rustls_platform_verifier::BuilderVerifierExt;
19use sha1::{Digest, Sha1};
20use sha2::Sha256;
21use std::ops::Deref;
22use tokio::sync::RwLock;
23use tokio_util::sync::CancellationToken;
24use x509_parser::prelude::*;
25
26type OcspCache = Arc<RwLock<HashMap<Vec<u8>, Option<Arc<CertifiedKey>>>>>;
27
28#[derive(Debug)]
29pub struct OcspStapler {
30  inner: Arc<dyn ResolvesServerCert>,
31  cache: OcspCache,
32  sender: async_channel::Sender<CertifiedKey>,
33  cancel_token: CancellationToken,
34}
35
36impl OcspStapler {
37  pub fn new(
38    inner: Arc<dyn ResolvesServerCert>,
39    runtime: &tokio::runtime::Runtime,
40    logging_tx: Vec<async_channel::Sender<LogMessage>>,
41  ) -> Self {
42    let (sender, receiver) = async_channel::unbounded();
43    let cache = Arc::new(RwLock::new(HashMap::new()));
44    let cancel_token = CancellationToken::new();
45
46    let stapler = Self {
47      inner,
48      cache,
49      sender,
50      cancel_token: cancel_token.clone(),
51    };
52
53    runtime.spawn(background_ocsp_task(
54      receiver,
55      stapler.cache.clone(),
56      cancel_token,
57      logging_tx,
58    ));
59
60    stapler
61  }
62
63  pub fn preload(&self, key: Arc<CertifiedKey>) {
64    if !key.cert.is_empty() {
65      // Add to cache immediately (even without OCSP) to track it, or just trigger fetch
66      let _ = self.sender.send_blocking((*key).clone());
67    }
68  }
69
70  pub async fn stop(&self) {
71    self.cancel_token.cancel();
72  }
73}
74
75impl ResolvesServerCert for OcspStapler {
76  fn resolve(&self, client_hello: ClientHello<'_>) -> Option<Arc<CertifiedKey>> {
77    let original_key = self.inner.resolve(client_hello)?;
78    if let Some(leaf) = original_key.cert.first() {
79      // Check cache
80      //
81      // If blocking_read() method is used when only Tokio is used, the program would panic on resolving a TLS certificate.
82      #[cfg(any(feature = "runtime-vibeio", feature = "runtime-monoio"))]
83      let cache = self.cache.blocking_read();
84      #[cfg(feature = "runtime-tokio")]
85      let cache = futures_executor::block_on(async { self.cache.read().await });
86
87      if let Some(cached_key_option) = cache.get(&leaf.to_vec()) {
88        if let Some(cached_key) = cached_key_option.as_ref() {
89          // If cached key has OCSP, return it.
90          // Note: We might want to check if it's expired here, but the background task handles cleanup/refresh.
91          // For simplicity, we return what's in cache.
92          if cached_key.ocsp.is_some() {
93            return Some(cached_key.clone());
94          }
95        }
96        // If cached key has no OCSP, don't trigger fetch.
97      } else {
98        // Not in cache or no OCSP yet. Trigger fetch.
99        let _ = self.sender.send_blocking((*original_key).clone());
100      }
101    }
102    Some(original_key)
103  }
104}
105
106async fn background_ocsp_task(
107  receiver: async_channel::Receiver<CertifiedKey>,
108  cache: OcspCache,
109  cancel_token: CancellationToken,
110  logging_tx: Vec<async_channel::Sender<LogMessage>>,
111) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
112  // Track next update times
113  let mut next_updates: HashMap<Vec<u8>, SystemTime> = HashMap::new();
114  // Track known cert chains
115  let mut known_certs: HashMap<Vec<u8>, CertifiedKey> = HashMap::new();
116
117  // Create HTTP client
118  let tls_config_builder =
119    match rustls::ClientConfig::builder_with_provider(rustls::crypto::aws_lc_rs::default_provider().into())
120      .with_safe_default_protocol_versions()
121    {
122      Ok(builder) => builder,
123      Err(e) => {
124        for tx in &logging_tx {
125          let _ = tx
126            .send(LogMessage::new(
127              format!("Failed to create TLS config builder for OCSP stapling: {e}"),
128              true,
129            ))
130            .await;
131        }
132        return Err(e.into());
133      }
134    };
135  let https_connector = hyper_rustls::HttpsConnectorBuilder::new()
136    .with_tls_config(
137      (if let Ok(client_config) = BuilderVerifierExt::with_platform_verifier(tls_config_builder.clone()) {
138        client_config
139      } else {
140        tls_config_builder.with_webpki_verifier(
141          match WebPkiServerVerifier::builder(Arc::new(rustls::RootCertStore {
142            roots: webpki_roots::TLS_SERVER_ROOTS.to_vec(),
143          }))
144          .build()
145          {
146            Ok(verifier) => verifier,
147            Err(e) => {
148              for tx in &logging_tx {
149                let _ = tx
150                  .send(LogMessage::new(
151                    format!("Failed to create TLS verifier for OCSP stapling: {e}"),
152                    true,
153                  ))
154                  .await;
155              }
156              return Err(e.into());
157            }
158          },
159        )
160      })
161      .with_no_client_auth(),
162    )
163    .https_or_http()
164    .enable_http1()
165    .build();
166
167  let client =
168    Client::builder(TokioExecutor::new()).build::<_, http_body_util::Full<hyper::body::Bytes>>(https_connector);
169
170  loop {
171    let mut sleep_duration = Duration::from_secs(60); // Default check interval
172
173    // Calculate time to next update
174    let now = SystemTime::now();
175    for next_update in next_updates.values() {
176      if let Ok(duration) = next_update.duration_since(now) {
177        if duration < sleep_duration {
178          sleep_duration = duration;
179        }
180      } else {
181        // Already expired, refresh immediately (or very soon)
182        sleep_duration = Duration::from_secs(1);
183      }
184    }
185
186    let received_certified_key = tokio::select! {
187      _ = cancel_token.cancelled() => Err(anyhow::anyhow!("Cancelled"))?,
188      _ = tokio::time::sleep(sleep_duration) => None,
189      res = receiver.recv() => match res {
190        Ok(chain) => Some(chain),
191        Err(e) => Err(e)?, // Channel closed
192      }
193    };
194
195    if let Some(certified_key) = received_certified_key {
196      let chain = &certified_key.cert;
197      if let Some(leaf) = chain.first() {
198        let key = leaf.to_vec();
199        if !known_certs.contains_key(&key) {
200          known_certs.insert(key.clone(), certified_key);
201          // Trigger immediate update for new cert
202          next_updates.insert(key, SystemTime::now());
203        }
204      }
205    }
206
207    // Process updates
208    let now = SystemTime::now();
209    let mut updates_to_fetch = Vec::new();
210    for (key, next_update) in &next_updates {
211      if *next_update <= now {
212        updates_to_fetch.push(key.clone());
213      }
214    }
215
216    for key in updates_to_fetch {
217      if let Some(certified_key) = known_certs.get(&key) {
218        match fetch_ocsp_response(&client, &certified_key.cert).await {
219          Ok(Some((response, next_update_time))) => {
220            let mut new_certified_key = certified_key.clone();
221            new_certified_key.ocsp = Some(response.clone());
222            cache
223              .write()
224              .await
225              .insert(certified_key.cert[0].to_vec(), Some(Arc::new(new_certified_key)));
226            next_updates.insert(key, next_update_time);
227          }
228          Ok(None) => {
229            // Don't retry OCSP stapling
230            cache.write().await.insert(certified_key.cert[0].to_vec(), None);
231            next_updates.remove(&key);
232          }
233          Err(e) => {
234            // Log error
235            for tx in &logging_tx {
236              let _ = tx.send(LogMessage::new(format!("OCSP fetch failed: {e}"), true)).await;
237            }
238            // Retry later; with some randomness to avoid refresh storm.
239            next_updates.insert(key, now + Duration::from_secs(rand::random_range(100..=500)));
240            continue;
241          }
242        };
243      }
244    }
245  }
246}
247
248async fn fetch_ocsp_response(
249  client: &Client<
250    hyper_rustls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>,
251    http_body_util::Full<hyper::body::Bytes>,
252  >,
253  chain: &[CertificateDer<'_>],
254) -> anyhow::Result<Option<(Vec<u8>, SystemTime)>> {
255  // Try SHA-256 first (preferred algorithm)
256  let response = fetch_ocsp_response_inner(client, chain, true).await;
257
258  // If SHA-256 succeeded, return immediately (do not downgrade to SHA-1)
259  if response.is_ok() {
260    return response;
261  }
262
263  // Only try SHA-1 fallback for specific error types observed in the wild
264  let should_try_sha1 = match &response {
265    Err(e) => should_try_sha1_for_error(e),
266    _ => false,
267  };
268
269  if should_try_sha1 {
270    if let Ok(sha1_response) = fetch_ocsp_response_inner(client, chain, false).await {
271      return Ok(sha1_response);
272    }
273  }
274
275  // Return the original SHA-256 result (error or success)
276  response
277}
278
279async fn fetch_ocsp_response_inner(
280  client: &Client<
281    hyper_rustls::HttpsConnector<hyper_util::client::legacy::connect::HttpConnector>,
282    http_body_util::Full<hyper::body::Bytes>,
283  >,
284  chain: &[CertificateDer<'_>],
285  use_sha256: bool,
286) -> anyhow::Result<Option<(Vec<u8>, SystemTime)>> {
287  if chain.len() < 2 {
288    return Ok(None);
289  }
290
291  let leaf = &chain[0];
292  let issuer = &chain[1];
293
294  let (_, leaf_cert) =
295    X509Certificate::from_der(leaf).map_err(|e| anyhow::anyhow!("Failed to parse leaf cert: {e}"))?;
296  let (_, issuer_cert) =
297    X509Certificate::from_der(issuer).map_err(|e| anyhow::anyhow!("Failed to parse issuer cert: {e}"))?;
298
299  let Some(ocsp_url) = extract_ocsp_url(&leaf_cert) else {
300    return Ok(None);
301  };
302
303  let req_der = create_ocsp_request(&leaf_cert, &issuer_cert, use_sha256)?;
304
305  let req = Request::builder()
306    .method("POST")
307    .uri(&ocsp_url)
308    .header("Content-Type", "application/ocsp-request")
309    .body(http_body_util::Full::new(hyper::body::Bytes::from(req_der)))
310    .with_context(|| format!("Failed to build OCSP request for {ocsp_url}"))?;
311
312  let res = client.request(req).await?;
313  if !res.status().is_success() {
314    return Err(anyhow::anyhow!(
315      "OCSP request failed with status {} for URL: {ocsp_url}",
316      res.status()
317    ));
318  }
319
320  use http_body_util::BodyExt;
321  let body_bytes = res.collect().await?.to_bytes();
322  let response_der = body_bytes.to_vec();
323
324  // Parse response
325  let response: OcspResponse =
326    rasn::der::decode(&response_der).map_err(|e| anyhow::anyhow!("Failed to decode OCSP response: {e}"))?;
327
328  if response.status != OcspResponseStatus::Successful {
329    return Err(anyhow::anyhow!(
330      "OCSP response status unsuccessful: {}",
331      response.status.identifier()
332    ));
333  }
334
335  let response_bytes = response
336    .bytes
337    .ok_or_else(|| anyhow::anyhow!("No response bytes in OCSP response"))?;
338
339  if response_bytes.r#type
340    != ObjectIdentifier::new(vec![1, 3, 6, 1, 5, 5, 7, 48, 1, 1])
341      .ok_or_else(|| anyhow::anyhow!("Invalid OCSP basic response OID"))?
342  {
343    return Err(anyhow::anyhow!("Unsupported OCSP response type"));
344  }
345
346  let basic_response: BasicOcspResponse = rasn::der::decode(&response_bytes.response)
347    .map_err(|e| anyhow::anyhow!("Failed to decode BasicOcspResponse: {e}"))?;
348
349  // Verify signature (try issuer first, then certs[] in the response)
350  verify_ocsp_signature_with_certs_field(&basic_response, &issuer_cert)?;
351
352  // Compute next_update across all single responses
353  let mut min_next_update: Option<SystemTime> = None;
354  for single_res in basic_response.tbs_response_data.responses {
355    verify_single_res(&single_res, &leaf_cert, &issuer_cert)?;
356
357    let next_update = single_res.next_update.map(SystemTime::from);
358    if let Some(mut nu) = next_update {
359      // Safety margin: 25% of validity period + jitter
360      let this_update = SystemTime::from(single_res.this_update);
361      let validity = nu
362        .duration_since(this_update)
363        .unwrap_or_else(|_| Duration::from_secs(0));
364      let margin = validity / 4 + Duration::from_secs(rand::random_range(0..=300));
365
366      if nu.checked_sub(margin).unwrap_or(nu) > SystemTime::now() {
367        nu = nu.checked_sub(margin).unwrap_or(nu);
368      }
369
370      min_next_update = Some(match min_next_update {
371        Some(min) if nu < min => nu,
372        None => nu,
373        _ => min_next_update.ok_or(anyhow::anyhow!("Failed to compute next update"))?,
374      });
375    }
376  }
377
378  let next_update = min_next_update.unwrap_or_else(|| SystemTime::now() + Duration::from_secs(300));
379  Ok(Some((response_der, next_update)))
380}
381
382fn extract_ocsp_url(cert: &X509Certificate) -> Option<String> {
383  for ext in cert.extensions() {
384    if let x509_parser::extensions::ParsedExtension::AuthorityInfoAccess(aia) = ext.parsed_extension() {
385      for access_desc in &aia.accessdescs {
386        if access_desc.access_method == x509_parser::oid_registry::OID_PKIX_ACCESS_DESCRIPTOR_OCSP {
387          if let x509_parser::extensions::GeneralName::URI(uri) = access_desc.access_location {
388            return Some(uri.to_string());
389          }
390        }
391      }
392    }
393  }
394  None
395}
396
397fn create_ocsp_request(leaf: &X509Certificate, issuer: &X509Certificate, use_sha256: bool) -> anyhow::Result<Vec<u8>> {
398  // 1. Hash Issuer DN
399  let issuer_name_hash = if use_sha256 {
400    let mut sha256 = Sha256::new();
401    sha256.update(issuer.subject().as_raw());
402    sha256.finalize().to_vec()
403  } else {
404    let mut sha1 = Sha1::new();
405    sha1.update(issuer.subject().as_raw());
406    sha1.finalize().to_vec()
407  };
408
409  // 2. Hash Issuer Key
410  // x509-parser gives SubjectPublicKeyInfo.
411  // RFC 6960: hash of the value (excluding tag and length) of the subject public key field.
412  let spki = issuer.public_key();
413  // spki.subject_public_key is BitString. We want the bytes.
414  let pub_key_bytes = &spki.subject_public_key.data;
415  let issuer_key_hash = if use_sha256 {
416    let mut sha256 = Sha256::new();
417    sha256.update(pub_key_bytes);
418    sha256.finalize().to_vec()
419  } else {
420    let mut sha1 = Sha1::new();
421    sha1.update(pub_key_bytes);
422    sha1.finalize().to_vec()
423  };
424
425  // 3. Serial Number
426  let serial_number = &leaf.tbs_certificate.serial;
427  // Need to convert x509_parser serial (BigUint) to rasn Integer.
428  // x509_parser serial is `BigUint`. rasn `Integer` is BigInt.
429  let serial_int = rasn::types::Integer::from(num_bigint::BigInt::from_biguint(
430    num_bigint::Sign::Plus,
431    serial_number.to_owned(),
432  ));
433
434  let cert_id = CertId {
435    hash_algorithm: rasn_pkix::AlgorithmIdentifier {
436      algorithm: if use_sha256 {
437        rasn::types::Oid::JOINT_ISO_ITU_T_COUNTRY_US_ORGANIZATION_GOV_CSOR_NIST_ALGORITHMS_HASH_SHA256.to_owned()
438      } else {
439        rasn::types::Oid::ISO_IDENTIFIED_ORGANISATION_OIW_SECSIG_ALGORITHM_SHA1.to_owned()
440      },
441      parameters: None,
442    },
443    issuer_name_hash: rasn::types::OctetString::from(issuer_name_hash),
444    issuer_key_hash: rasn::types::OctetString::from(issuer_key_hash),
445    serial_number: serial_int,
446  };
447
448  let req = OcspRequest {
449    tbs_request: TbsRequest {
450      version: rasn::types::Integer::from(0), // v1(0)
451      requestor_name: None,
452      request_list: vec![OcspInnerRequest {
453        req_cert: cert_id,
454        single_request_extensions: None,
455      }],
456      request_extensions: None,
457    },
458    optional_signature: None,
459  };
460
461  rasn::der::encode(&req).map_err(|e| anyhow::anyhow!(e))
462}
463
464// ---------------------------------------------------------------------------
465// OCSP verification helpers (backported from ferron3 ocsp-stapler)
466// ---------------------------------------------------------------------------
467
468fn verify_ocsp_signature(basic_response: &BasicOcspResponse, issuer_cert: &X509Certificate) -> anyhow::Result<()> {
469  let spki = issuer_cert.public_key();
470  let alg: &dyn aws_lc_rs::signature::VerificationAlgorithm =
471    match *basic_response.signature_algorithm.algorithm.deref().deref() {
472      // RSA + PKCS#1
473      [1, 2, 840, 113549, 1, 1, 11] => &aws_lc_rs::signature::RSA_PKCS1_2048_8192_SHA256,
474      [1, 2, 840, 113549, 1, 1, 12] => &aws_lc_rs::signature::RSA_PKCS1_2048_8192_SHA384,
475      [1, 2, 840, 113549, 1, 1, 13] => &aws_lc_rs::signature::RSA_PKCS1_2048_8192_SHA512,
476      [1, 2, 840, 113549, 1, 1, 5] => &aws_lc_rs::signature::RSA_PKCS1_1024_8192_SHA1_FOR_LEGACY_USE_ONLY,
477
478      // Ed25519
479      [1, 3, 101, 112] => &aws_lc_rs::signature::ED25519,
480
481      // ECDSA
482      [1, 2, 840, 10045, 4, 3, algo] => {
483        // Get curve OID
484        let curve_oid: Option<ObjectIdentifier> = issuer_cert
485          .public_key()
486          .algorithm
487          .parameters
488          .as_ref()
489          .and_then(|v| rasn::der::decode::<ObjectIdentifier>(v.as_bytes()).ok());
490        let curve_oid_u32: Option<&[u32]> = curve_oid.as_deref().map(|oid| oid.as_ref());
491        match (curve_oid_u32, algo) {
492          // P-256
493          (Some([1, 2, 840, 10045, 3, 1, 7]), 2) => &aws_lc_rs::signature::ECDSA_P256_SHA256_ASN1,
494          (Some([1, 2, 840, 10045, 3, 1, 7]), 3) => &aws_lc_rs::signature::ECDSA_P256_SHA384_ASN1,
495          (Some([1, 2, 840, 10045, 3, 1, 7]), 4) => &aws_lc_rs::signature::ECDSA_P256_SHA512_ASN1,
496
497          // P-384
498          (Some([1, 3, 132, 0, 34]), 2) => &aws_lc_rs::signature::ECDSA_P384_SHA256_ASN1,
499          (Some([1, 3, 132, 0, 34]), 3) => &aws_lc_rs::signature::ECDSA_P384_SHA384_ASN1,
500          (Some([1, 3, 132, 0, 34]), 4) => &aws_lc_rs::signature::ECDSA_P384_SHA512_ASN1,
501
502          // P-521
503          (Some([1, 3, 132, 0, 35]), 2) => &aws_lc_rs::signature::ECDSA_P521_SHA256_ASN1,
504          (Some([1, 3, 132, 0, 35]), 3) => &aws_lc_rs::signature::ECDSA_P521_SHA384_ASN1,
505          (Some([1, 3, 132, 0, 35]), 4) => &aws_lc_rs::signature::ECDSA_P521_SHA512_ASN1,
506
507          // secp256k1 (not common in OCSP but handle just in case)
508          (Some([1, 3, 132, 0, 10]), 2) => &aws_lc_rs::signature::ECDSA_P256K1_SHA256_ASN1,
509
510          _ => {
511            return Err(anyhow::anyhow!(
512              "Unsupported OCSP signature algorithm OID: {}",
513              basic_response.signature_algorithm.algorithm
514            ))
515          }
516        }
517      }
518
519      _ => {
520        return Err(anyhow::anyhow!(
521          "Unsupported OCSP signature algorithm OID: {}",
522          basic_response.signature_algorithm.algorithm
523        ))
524      }
525    };
526
527  let signature = basic_response.signature.as_raw_slice();
528
529  alg
530    .verify_sig(
531      spki.subject_public_key.data.as_ref(),
532      &rasn::der::encode(&basic_response.tbs_response_data)
533        .map_err(|e| anyhow::anyhow!("OCSP response signature verification failed: {e}"))?,
534      signature,
535    )
536    .map_err(|_| anyhow::anyhow!("OCSP response signature verification failed"))?;
537
538  Ok(())
539}
540
541fn verify_ocsp_signature_with_certs_field(
542  basic_response: &BasicOcspResponse,
543  issuer_cert: &X509Certificate,
544) -> anyhow::Result<()> {
545  let Err(mut last_error) = verify_ocsp_signature(basic_response, issuer_cert) else {
546    return Ok(());
547  };
548
549  if let Some(ref certs) = basic_response.certs {
550    for cert in certs {
551      // Re-encode the cert to DER and parse with x509-parser to get
552      // an X509Certificate struct for signature verification
553      let Ok(cert_der) = rasn::der::encode(cert) else {
554        continue;
555      };
556      let Ok((_, parsed_cert)) = X509Certificate::from_der(&cert_der) else {
557        continue;
558      };
559
560      // Ensure the candidate cert appears to be issued by the expected issuer (name match)
561      if parsed_cert.tbs_certificate.issuer != *issuer_cert.subject() {
562        // Certificate not issued by the expected issuer, skip
563        continue;
564      }
565
566      if !parsed_cert.extensions().iter().any(|e| {
567        let parsed = e.parsed_extension();
568        match parsed {
569          ParsedExtension::ExtendedKeyUsage(eku) => eku.ocsp_signing,
570          _ => false,
571        }
572      }) {
573        // The certificate does not have OCSP Extended Key Usage, skip verification
574        continue;
575      }
576
577      let Err(new_last_error) = verify_ocsp_signature(basic_response, &parsed_cert) else {
578        return Ok(());
579      };
580      last_error = new_last_error;
581    }
582  }
583
584  Err(last_error)
585}
586
587fn hash_oid(data: impl AsRef<[u8]>, oid: ObjectIdentifier) -> anyhow::Result<Vec<u8>> {
588  let mut ctx =
589    if oid == *rasn::types::Oid::JOINT_ISO_ITU_T_COUNTRY_US_ORGANIZATION_GOV_CSOR_NIST_ALGORITHMS_HASH_SHA256 {
590      aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA256)
591    } else if oid == *rasn::types::Oid::JOINT_ISO_ITU_T_COUNTRY_US_ORGANIZATION_GOV_CSOR_NIST_ALGORITHMS_HASH_SHA384 {
592      aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA384)
593    } else if oid == *rasn::types::Oid::JOINT_ISO_ITU_T_COUNTRY_US_ORGANIZATION_GOV_CSOR_NIST_ALGORITHMS_HASH_SHA512 {
594      aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA512)
595    } else if oid == *rasn::types::Oid::ISO_IDENTIFIED_ORGANISATION_OIW_SECSIG_ALGORITHM_SHA1 {
596      aws_lc_rs::digest::Context::new(&aws_lc_rs::digest::SHA1_FOR_LEGACY_USE_ONLY)
597    } else {
598      return Err(anyhow::anyhow!(
599        "Unsupported hash algorithm OID in OCSP response: {}",
600        oid
601      ));
602    };
603  ctx.update(data.as_ref());
604  Ok(ctx.finish().as_ref().to_vec())
605}
606
607fn should_try_sha1_for_error(err: &anyhow::Error) -> bool {
608  let e_message = err.to_string();
609  e_message.contains("OCSP request failed with status")
610    || e_message.contains("Failed to decode OCSP response")
611    || e_message.contains("OCSP response status unsuccessful")
612}
613
614fn verify_single_res(
615  single_res: &rasn_ocsp::SingleResponse,
616  leaf_cert: &X509Certificate,
617  issuer_cert: &X509Certificate,
618) -> anyhow::Result<()> {
619  // Check for issuer name hash
620  if single_res.cert_id.issuer_name_hash.as_ref()
621    != hash_oid(
622      issuer_cert.subject().as_raw(),
623      single_res.cert_id.hash_algorithm.algorithm.clone(),
624    )?
625  {
626    return Err(anyhow::anyhow!("Issuer name hash mismatch in OCSP response"));
627  }
628
629  // Check for issuer key hash
630  if single_res.cert_id.issuer_key_hash.as_ref()
631    != hash_oid(
632      issuer_cert.public_key().subject_public_key.data.as_ref(),
633      single_res.cert_id.hash_algorithm.algorithm.clone(),
634    )?
635  {
636    return Err(anyhow::anyhow!("Issuer key hash mismatch in OCSP response"));
637  }
638
639  // Check for serial number
640  let serial_number = &leaf_cert.tbs_certificate.serial;
641  let serial_int = BigInt::from_biguint(num_bigint::Sign::Plus, serial_number.to_owned());
642  if single_res.cert_id.serial_number != rasn::types::Integer::from(serial_int) {
643    return Err(anyhow::anyhow!("Serial number mismatch in OCSP response"));
644  }
645
646  // Check if the response falls between the issuer's valid time range,
647  // allowing for a 60-second clock skew to account for network latency and time differences.
648  let now_with_skew = SystemTime::now() + Duration::from_secs(60);
649  let now = SystemTime::now();
650  let this_update_st = SystemTime::from(single_res.this_update);
651  if this_update_st > now_with_skew || single_res.next_update.map(SystemTime::from).is_some_and(|nu| nu < now) {
652    return Err(anyhow::anyhow!("OCSP response is not current"));
653  }
654
655  Ok(())
656}
657
658#[cfg(test)]
659mod tests {
660  use super::*;
661  use anyhow::anyhow;
662  use sha1::Sha1 as TestSha1;
663  use sha2::{Digest as Sha2Digest, Sha256 as TestSha256};
664
665  #[test]
666  fn test_should_try_sha1_for_error() {
667    let e1 = anyhow!("OCSP request failed with status 500 for URL");
668    assert!(super::should_try_sha1_for_error(&e1));
669
670    let e2 = anyhow!("Failed to decode OCSP response: malformed");
671    assert!(super::should_try_sha1_for_error(&e2));
672
673    let e3 = anyhow!("OCSP response status unsuccessful: 3");
674    assert!(super::should_try_sha1_for_error(&e3));
675
676    let e4 = anyhow!("network down");
677    assert!(!super::should_try_sha1_for_error(&e4));
678  }
679
680  #[test]
681  fn test_hash_oid_sha256_and_sha1() {
682    let data = b"abc";
683
684    // SHA-256
685    let oid_sha256 = rasn::types::Oid::JOINT_ISO_ITU_T_COUNTRY_US_ORGANIZATION_GOV_CSOR_NIST_ALGORITHMS_HASH_SHA256;
686    let got = hash_oid(data.as_ref(), oid_sha256.into()).expect("hash_oid sha256 failed");
687    let mut hasher = TestSha256::new();
688    hasher.update(data);
689    let expected = hasher.finalize().to_vec();
690    assert_eq!(got, expected);
691
692    // SHA-1
693    let oid_sha1 = rasn::types::Oid::ISO_IDENTIFIED_ORGANISATION_OIW_SECSIG_ALGORITHM_SHA1;
694    let got1 = hash_oid(data.as_ref(), oid_sha1.into()).expect("hash_oid sha1 failed");
695    let mut hasher1 = TestSha1::new();
696    hasher1.update(data);
697    let expected1 = hasher1.finalize().to_vec();
698    assert_eq!(got1, expected1);
699  }
700}