1use std::time::{Duration, SystemTime};
19
20use rama_core::error::{BoxError, ErrorContext};
21use yasna::{
22 Tag,
23 models::{GeneralizedTime, ObjectIdentifier},
24};
25
26fn oid_sha1() -> ObjectIdentifier {
27 ObjectIdentifier::from_slice(&[1, 3, 14, 3, 2, 26])
28}
29fn oid_ecdsa_sha256() -> ObjectIdentifier {
30 ObjectIdentifier::from_slice(&[1, 2, 840, 10045, 4, 3, 2])
31}
32fn oid_rsa_sha256() -> ObjectIdentifier {
33 ObjectIdentifier::from_slice(&[1, 2, 840, 113549, 1, 1, 11])
34}
35fn oid_ocsp_basic() -> ObjectIdentifier {
36 ObjectIdentifier::from_slice(&[1, 3, 6, 1, 5, 5, 7, 48, 1, 1])
37}
38fn oid_ocsp_nonce() -> ObjectIdentifier {
39 ObjectIdentifier::from_slice(&[1, 3, 6, 1, 5, 5, 7, 48, 1, 2])
40}
41fn oid_ad_ocsp() -> ObjectIdentifier {
42 ObjectIdentifier::from_slice(&[1, 3, 6, 1, 5, 5, 7, 48, 1])
43}
44
45#[must_use]
49pub fn authority_info_access_ocsp_der(uri: &str) -> Vec<u8> {
50 yasna::construct_der(|w| {
51 w.write_sequence(|w| {
52 w.next().write_sequence(|w| {
53 w.next().write_oid(&oid_ad_ocsp());
54 w.next().write_tagged_implicit(Tag::context(6), |w| {
56 w.write_bytes(uri.as_bytes());
57 });
58 });
59 });
60 })
61}
62
63#[must_use]
67pub fn sha1_hash_algorithm_der() -> Vec<u8> {
68 yasna::construct_der(|w| {
69 w.write_sequence(|w| {
70 w.next().write_oid(&oid_sha1());
71 w.next().write_null();
72 });
73 })
74}
75
76#[derive(Debug, Clone, Copy)]
78pub enum OcspCertStatus {
79 Good,
81 Revoked {
83 revocation_time: SystemTime,
85 },
86}
87
88#[derive(Debug, Clone, Copy)]
90pub enum OcspSignatureAlgorithm {
91 EcdsaSha256,
93 RsaSha256,
95}
96
97#[derive(Debug, Clone, Copy)]
105pub struct OcspCertId<'a> {
106 pub issuer_name_der: &'a [u8],
109 pub hash_algorithm_der: &'a [u8],
111 pub issuer_name_hash: &'a [u8],
113 pub issuer_key_hash: &'a [u8],
116 pub serial: &'a [u8],
118}
119
120pub fn build_ocsp_response(
132 cert: &OcspCertId<'_>,
133 status: OcspCertStatus,
134 produced_at: SystemTime,
135 validity: Duration,
136 nonce: Option<&[u8]>,
137 sign_tbs: impl FnOnce(&[u8]) -> Result<(OcspSignatureAlgorithm, Vec<u8>), BoxError>,
138) -> Result<Vec<u8>, BoxError> {
139 let revoked_at = match status {
142 OcspCertStatus::Good => None,
143 OcspCertStatus::Revoked { revocation_time } => Some(generalized_time(revocation_time)?),
144 };
145
146 let produced = generalized_time(produced_at)?;
148 let next_at = produced_at
149 .checked_add(validity)
150 .ok_or_else(|| BoxError::from("ocsp: nextUpdate overflow"))?;
151 let next_update = generalized_time(next_at)?;
152
153 let tbs_der = yasna::construct_der(|w| {
156 w.write_sequence(|w| {
157 w.next()
160 .write_tagged(Tag::context(1), |w| w.write_der(cert.issuer_name_der));
161 w.next().write_generalized_time(&produced);
163 w.next().write_sequence(|w| {
165 w.next().write_sequence(|w| {
166 w.next().write_sequence(|w| {
168 w.next().write_der(cert.hash_algorithm_der);
169 w.next().write_bytes(cert.issuer_name_hash);
170 w.next().write_bytes(cert.issuer_key_hash);
171 w.next().write_bigint_bytes(cert.serial, true);
172 });
173 match &revoked_at {
176 None => {
177 w.next()
178 .write_tagged_implicit(Tag::context(0), |w| w.write_null());
179 }
180 Some(revoked_at) => {
181 w.next().write_tagged_implicit(Tag::context(1), |w| {
182 w.write_sequence(|w| {
183 w.next().write_generalized_time(revoked_at);
184 });
185 });
186 }
187 }
188 w.next().write_generalized_time(&produced);
190 w.next()
192 .write_tagged(Tag::context(0), |w| w.write_generalized_time(&next_update));
193 });
194 });
195 if let Some(nonce) = nonce {
197 w.next().write_tagged(Tag::context(1), |w| {
198 w.write_sequence(|w| {
199 w.next().write_sequence(|w| {
200 w.next().write_oid(&oid_ocsp_nonce());
201 w.next().write_bytes(nonce);
202 });
203 });
204 });
205 }
206 });
207 });
208
209 let (alg, signature) = sign_tbs(&tbs_der)?;
210
211 let basic_der = yasna::construct_der(|w| {
213 w.write_sequence(|w| {
214 w.next().write_der(&tbs_der);
215 w.next().write_sequence(|w| match alg {
217 OcspSignatureAlgorithm::EcdsaSha256 => {
218 w.next().write_oid(&oid_ecdsa_sha256());
219 }
220 OcspSignatureAlgorithm::RsaSha256 => {
221 w.next().write_oid(&oid_rsa_sha256());
222 w.next().write_null();
223 }
224 });
225 w.next().write_bitvec_bytes(&signature, signature.len() * 8);
227 });
228 });
229
230 let resp_der = yasna::construct_der(|w| {
232 w.write_sequence(|w| {
233 w.next().write_enum(0);
235 w.next().write_tagged(Tag::context(0), |w| {
237 w.write_sequence(|w| {
238 w.next().write_oid(&oid_ocsp_basic());
239 w.next().write_bytes(&basic_der);
240 });
241 });
242 });
243 });
244
245 Ok(resp_der)
246}
247
248#[derive(Debug, Clone)]
250pub struct OcspRequestCertId {
251 pub hash_algorithm_der: Vec<u8>,
254 pub issuer_name_hash: Vec<u8>,
256 pub issuer_key_hash: Vec<u8>,
258 pub serial: Vec<u8>,
260}
261
262#[derive(Debug, Clone, Default)]
264pub struct OcspRequestInfo {
265 pub certs: Vec<OcspRequestCertId>,
267 pub nonce: Option<Vec<u8>>,
270}
271
272pub fn parse_ocsp_request(der: &[u8]) -> Result<OcspRequestInfo, BoxError> {
278 yasna::parse_der(der, |r| {
279 r.read_sequence(|r| {
280 let info = r.next().read_sequence(|r| {
281 r.read_optional(|r| r.read_tagged(Tag::context(0), |r| r.read_i64()))?;
283 r.read_optional(|r| r.read_tagged(Tag::context(1), |r| r.read_der()))?;
285 let mut certs = Vec::new();
287 r.next().read_sequence_of(|r| {
288 r.read_sequence(|r| {
289 let cert = r.next().read_sequence(|r| {
290 let hash_algorithm_der = r.next().read_der()?;
291 let issuer_name_hash = r.next().read_bytes()?;
292 let issuer_key_hash = r.next().read_bytes()?;
293 let (serial, _positive) = r.next().read_bigint_bytes()?;
294 let serial = match serial.split_first() {
298 Some((0x00, rest)) if !rest.is_empty() => rest.to_vec(),
299 _ => serial,
300 };
301 Ok(OcspRequestCertId {
302 hash_algorithm_der,
303 issuer_name_hash,
304 issuer_key_hash,
305 serial,
306 })
307 })?;
308 r.read_optional(|r| r.read_tagged(Tag::context(0), |r| r.read_der()))?;
310 certs.push(cert);
311 Ok(())
312 })
313 })?;
314 let nonce = r
316 .read_optional(|r| {
317 r.read_tagged(Tag::context(2), |r| {
318 let mut nonce = None;
319 r.read_sequence_of(|r| {
320 r.read_sequence(|r| {
321 let oid = r.next().read_oid()?;
322 r.read_optional(|r| r.read_bool())?;
323 let value = r.next().read_bytes()?;
324 if oid == oid_ocsp_nonce() {
325 nonce = Some(value);
326 }
327 Ok(())
328 })
329 })?;
330 Ok(nonce)
331 })
332 })?
333 .flatten();
334 Ok(OcspRequestInfo { certs, nonce })
335 })?;
336 r.read_optional(|r| r.read_tagged(Tag::context(0), |r| r.read_der()))?;
338 Ok(info)
339 })
340 })
341 .map_err(|e| BoxError::from(format!("ocsp: parse request: {e}")))
342}
343
344fn generalized_time(t: SystemTime) -> Result<GeneralizedTime, BoxError> {
347 let secs = t
348 .duration_since(SystemTime::UNIX_EPOCH)
349 .context("ocsp: timestamp before unix epoch")?
350 .as_secs();
351 let odt = time::OffsetDateTime::from_unix_timestamp(secs as i64)
352 .map_err(|e| BoxError::from(format!("ocsp: invalid timestamp: {e}")))?;
353 Ok(GeneralizedTime::from_datetime(odt))
354}
355
356#[cfg(test)]
357mod tests {
358 use super::*;
359
360 #[test]
365 fn builds_wellformed_ocsp_response() {
366 let cert = OcspCertId {
367 issuer_name_der: &yasna::construct_der(|w| {
368 w.write_sequence(|_| {});
370 }),
371 hash_algorithm_der: &sha1_hash_algorithm_der(),
372 issuer_name_hash: &[0xAA; 20],
373 issuer_key_hash: &[0xBB; 20],
374 serial: &[0x12, 0x34, 0x56],
375 };
376
377 let mut signed_tbs: Vec<u8> = Vec::new();
378 let der = build_ocsp_response(
379 &cert,
380 OcspCertStatus::Good,
381 SystemTime::UNIX_EPOCH + Duration::from_secs(1_800_000_000),
382 Duration::from_hours(24 * 7),
383 None,
384 |tbs| {
385 signed_tbs = tbs.to_vec();
386 Ok((
387 OcspSignatureAlgorithm::EcdsaSha256,
388 vec![0xDE, 0xAD, 0xBE, 0xEF],
389 ))
390 },
391 )
392 .expect("build ocsp response");
393
394 assert!(
395 !signed_tbs.is_empty(),
396 "tbsResponseData was handed to the signer"
397 );
398
399 let basic_der = yasna::parse_der(&der, |r| {
401 r.read_sequence(|r| {
402 let status = r.next().read_enum()?;
403 assert_eq!(status, 0, "responseStatus successful");
404 r.next().read_tagged(Tag::context(0), |r| {
405 r.read_sequence(|r| {
406 let oid = r.next().read_oid()?;
407 assert_eq!(oid, oid_ocsp_basic(), "responseType id-pkix-ocsp-basic");
408 r.next().read_bytes()
409 })
410 })
411 })
412 })
413 .expect("parse OCSPResponse");
414
415 yasna::parse_der(&basic_der, |r| {
417 r.read_sequence(|r| {
418 let tbs = r.next().read_der()?;
420 assert_eq!(tbs, signed_tbs, "embedded tbs == signed tbs");
421 r.next().read_sequence(|r| {
423 let oid = r.next().read_oid()?;
424 assert_eq!(oid, oid_ecdsa_sha256());
425 Ok(())
426 })?;
427 let (sig, _bits) = r.next().read_bitvec_bytes()?;
429 assert_eq!(sig, vec![0xDE, 0xAD, 0xBE, 0xEF]);
430 Ok(())
431 })
432 })
433 .expect("parse BasicOCSPResponse");
434 }
435
436 fn sha256_hash_algorithm_der() -> Vec<u8> {
438 yasna::construct_der(|w| {
439 w.write_sequence(|w| {
440 w.next().write_oid(&ObjectIdentifier::from_slice(&[
441 2, 16, 840, 1, 101, 3, 4, 2, 1,
442 ]));
443 });
444 })
445 }
446
447 fn build_request(version: bool, nonce: Option<&[u8]>, algid: &[u8]) -> Vec<u8> {
450 yasna::construct_der(|w| {
451 w.write_sequence(|w| {
452 w.next().write_sequence(|w| {
453 if version {
454 w.next().write_tagged(Tag::context(0), |w| w.write_i64(0));
455 }
456 w.next().write_sequence(|w| {
457 w.next().write_sequence(|w| {
458 w.next().write_sequence(|w| {
459 w.next().write_der(algid);
460 w.next().write_bytes(&[0xAA; 20]);
461 w.next().write_bytes(&[0xBB; 20]);
462 w.next().write_bigint_bytes(&[0x12, 0x34, 0x56], true);
463 });
464 });
465 });
466 if let Some(nonce) = nonce {
467 w.next().write_tagged(Tag::context(2), |w| {
468 w.write_sequence(|w| {
469 w.next().write_sequence(|w| {
470 w.next().write_oid(&oid_ocsp_nonce());
471 w.next().write_bytes(nonce);
472 });
473 });
474 });
475 }
476 });
477 });
478 })
479 }
480
481 fn contains(haystack: &[u8], needle: &[u8]) -> bool {
482 haystack.windows(needle.len()).any(|w| w == needle)
483 }
484
485 #[test]
486 fn parses_minimal_request() {
487 let info = parse_ocsp_request(&build_request(false, None, &sha1_hash_algorithm_der()))
488 .expect("parse");
489 assert_eq!(info.certs.len(), 1);
490 let c = &info.certs[0];
491 assert_eq!(c.hash_algorithm_der, sha1_hash_algorithm_der());
492 assert_eq!(c.issuer_name_hash, vec![0xAA; 20]);
493 assert_eq!(c.issuer_key_hash, vec![0xBB; 20]);
494 assert_eq!(c.serial, vec![0x12, 0x34, 0x56]);
495 assert!(info.nonce.is_none());
496 }
497
498 #[test]
499 fn parses_request_with_version_and_nonce() {
500 let nonce_value = yasna::construct_der(|w| w.write_bytes(&[1, 2, 3, 4, 5, 6, 7, 8]));
501 let info = parse_ocsp_request(&build_request(
502 true,
503 Some(&nonce_value),
504 &sha1_hash_algorithm_der(),
505 ))
506 .expect("parse");
507 assert_eq!(info.certs.len(), 1);
508 assert_eq!(info.nonce.as_deref(), Some(nonce_value.as_slice()));
509 }
510
511 #[test]
514 fn parses_msb_set_serial_as_unsigned_magnitude() {
515 let algid = sha1_hash_algorithm_der();
516 let req = yasna::construct_der(|w| {
517 w.write_sequence(|w| {
518 w.next().write_sequence(|w| {
519 w.next().write_sequence(|w| {
520 w.next().write_sequence(|w| {
521 w.next().write_sequence(|w| {
522 w.next().write_der(&algid);
523 w.next().write_bytes(&[0xAA; 20]);
524 w.next().write_bytes(&[0xBB; 20]);
525 w.next().write_bigint_bytes(&[0xDE, 0xAD, 0xBE, 0xEF], true);
526 });
527 });
528 });
529 });
530 });
531 });
532 let info = parse_ocsp_request(&req).expect("parse");
533 assert_eq!(
534 info.certs[0].serial,
535 vec![0xDE, 0xAD, 0xBE, 0xEF],
536 "leading 0x00 sign byte stripped"
537 );
538 }
539
540 #[test]
543 fn response_echoes_request_certid_and_nonce() {
544 let nonce_value = yasna::construct_der(|w| w.write_bytes(&[9, 9, 9, 9]));
545 let info = parse_ocsp_request(&build_request(
546 true,
547 Some(&nonce_value),
548 &sha1_hash_algorithm_der(),
549 ))
550 .expect("parse");
551 let c = &info.certs[0];
552 let issuer = yasna::construct_der(|w| w.write_sequence(|_| {}));
553 let cert = OcspCertId {
554 issuer_name_der: &issuer,
555 hash_algorithm_der: &c.hash_algorithm_der,
556 issuer_name_hash: &c.issuer_name_hash,
557 issuer_key_hash: &c.issuer_key_hash,
558 serial: &c.serial,
559 };
560 let der = build_ocsp_response(
561 &cert,
562 OcspCertStatus::Good,
563 SystemTime::UNIX_EPOCH + Duration::from_secs(1_800_000_000),
564 Duration::from_hours(24),
565 info.nonce.as_deref(),
566 |_| Ok((OcspSignatureAlgorithm::EcdsaSha256, vec![0x00])),
567 )
568 .expect("build ocsp response");
569
570 assert!(contains(&der, &[0xAA; 20]), "issuerNameHash echoed");
571 assert!(contains(&der, &[0xBB; 20]), "issuerKeyHash echoed");
572 assert!(contains(&der, &[0x12, 0x34, 0x56]), "serial echoed");
573 assert!(contains(&der, &nonce_value), "nonce echoed");
574 }
575
576 fn cert_status_first_byte(response_der: &[u8]) -> u8 {
579 let basic = yasna::parse_der(response_der, |r| {
580 r.read_sequence(|r| {
581 let _status = r.next().read_enum()?;
582 r.next().read_tagged(Tag::context(0), |r| {
583 r.read_sequence(|r| {
584 let _oid = r.next().read_oid()?;
585 r.next().read_bytes()
586 })
587 })
588 })
589 })
590 .expect("parse OCSPResponse");
591 yasna::parse_der(&basic, |r| {
592 r.read_sequence(|r| {
593 let tbs = r.next().read_der()?;
594 let _alg = r.next().read_der()?;
595 let _sig = r.next().read_bitvec_bytes()?;
596 yasna::parse_der(&tbs, |r| {
597 r.read_sequence(|r| {
598 let _responder = r.next().read_der()?;
599 let _produced = r.next().read_der()?;
600 r.next().read_sequence(|r| {
601 r.next().read_sequence(|r| {
602 let _cert_id = r.next().read_der()?;
603 let cert_status = r.next().read_der()?;
604 let _this = r.next().read_der()?;
605 let _next = r.next().read_der()?;
606 Ok(cert_status[0])
607 })
608 })
609 })
610 })
611 })
612 })
613 .expect("parse BasicOCSPResponse")
614 }
615
616 fn good_or_revoked(status: OcspCertStatus) -> u8 {
617 let cert = OcspCertId {
618 issuer_name_der: &yasna::construct_der(|w| w.write_sequence(|_| {})),
619 hash_algorithm_der: &sha1_hash_algorithm_der(),
620 issuer_name_hash: &[0xAA; 20],
621 issuer_key_hash: &[0xBB; 20],
622 serial: &[0x12, 0x34, 0x56],
623 };
624 let der = build_ocsp_response(
625 &cert,
626 status,
627 SystemTime::UNIX_EPOCH + Duration::from_secs(1_800_000_000),
628 Duration::from_hours(24),
629 None,
630 |_| Ok((OcspSignatureAlgorithm::EcdsaSha256, vec![0x00])),
631 )
632 .expect("build ocsp response");
633 cert_status_first_byte(&der)
634 }
635
636 #[test]
638 fn revoked_status_encodes_as_context_1() {
639 assert_eq!(good_or_revoked(OcspCertStatus::Good), 0x80, "good is [0]");
640 assert_eq!(
641 good_or_revoked(OcspCertStatus::Revoked {
642 revocation_time: SystemTime::UNIX_EPOCH + Duration::from_secs(1_700_000_000),
643 }),
644 0xA1,
645 "revoked is [1] IMPLICIT RevokedInfo"
646 );
647 }
648
649 #[test]
651 fn echoes_sha256_cert_id_hash_algorithm() {
652 let sha256 = sha256_hash_algorithm_der();
653 let info = parse_ocsp_request(&build_request(false, None, &sha256)).expect("parse");
654 let c = &info.certs[0];
655 assert_eq!(c.hash_algorithm_der, sha256, "parsed verbatim");
656 let issuer = yasna::construct_der(|w| w.write_sequence(|_| {}));
657 let cert = OcspCertId {
658 issuer_name_der: &issuer,
659 hash_algorithm_der: &c.hash_algorithm_der,
660 issuer_name_hash: &c.issuer_name_hash,
661 issuer_key_hash: &c.issuer_key_hash,
662 serial: &c.serial,
663 };
664 let der = build_ocsp_response(
665 &cert,
666 OcspCertStatus::Good,
667 SystemTime::UNIX_EPOCH + Duration::from_secs(1_800_000_000),
668 Duration::from_hours(24),
669 None,
670 |_| Ok((OcspSignatureAlgorithm::EcdsaSha256, vec![0x00])),
671 )
672 .expect("build ocsp response");
673 assert!(contains(&der, &sha256), "sha256 hashAlgorithm echoed");
674 }
675
676 #[test]
677 fn aia_ocsp_carries_the_responder_uri() {
678 let uri = "http://127.0.0.1:9999/ocsp/abc";
679 let der = authority_info_access_ocsp_der(uri);
680 let oid = yasna::parse_der(&der, |r| {
681 r.read_sequence(|r| {
682 r.next().read_sequence(|r| {
683 let oid = r.next().read_oid()?;
684 let _loc = r.next().read_der()?;
685 Ok(oid)
686 })
687 })
688 })
689 .expect("AIA structure");
690 assert_eq!(oid, oid_ad_ocsp());
691 assert!(contains(&der, uri.as_bytes()), "responder URI present");
692 }
693}