structured_pkix/ocsp/
delegation.rs1use crate::error::{Located, Result};
4use crate::extension::{Extension, Recognizer};
5use crate::path::{
6 self, Anchor, BuildResult, EffectiveAnchor, Validation, ValidationContext, Verify, Watch,
7 WorkingKey,
8};
9use crate::{Certificate, oid};
10use structured_asn1::memory::Memory;
11use structured_asn1::{Tag, universal};
12
13#[derive(Debug, Clone, Copy, PartialEq, Eq)]
15pub enum ResponderRevocation {
16 NoCheck,
18 Unresolved,
20}
21
22pub(crate) fn read_no_check(extension: &Extension<'_>, execution: &Memory) -> Result<()> {
23 const FIELD: &str = "extensions.ocspNoCheck";
24 let mut reader = extension.reader(execution).field(FIELD)?;
25 let element = reader
26 .expect(Tag::primitive(universal::NULL))
27 .field(FIELD)?;
28 reader.null(&element).field(FIELD)?;
29 super::end(&reader, FIELD)
32}
33
34#[derive(Debug, Clone, Copy, PartialEq, Eq)]
36pub enum DelegationFailure {
37 IssuerKeyBinding,
39 SigningPurpose,
41 KeyUsage,
43}
44
45#[derive(Debug, Clone, Copy)]
48pub struct DelegateFacts<'a> {
49 responder: &'a Certificate<'a>,
50 issuer: &'a Certificate<'a>,
51 key: WorkingKey<'a>,
52 revocation: ResponderRevocation,
53}
54
55impl<'a> DelegateFacts<'a> {
56 #[must_use]
58 pub fn responder(self) -> &'a Certificate<'a> {
59 self.responder
60 }
61 #[must_use]
63 pub fn issuer(self) -> &'a Certificate<'a> {
64 self.issuer
65 }
66 #[must_use]
68 pub fn key(self) -> WorkingKey<'a> {
69 self.key
70 }
71 #[must_use]
73 pub fn revocation(self) -> ResponderRevocation {
74 self.revocation
75 }
76}
77
78#[derive(Debug)]
80pub enum Delegation<'a> {
81 Accepted(DelegateFacts<'a>),
83 Rejected(DelegationFailure),
85 PathRejected(path::Rejection<'a>),
87 Incomplete(path::Truncation),
89}
90
91pub fn check_delegate<'a, V: Verify, W: Watch, R: Recognizer>(
102 responder: &'a Certificate<'a>,
103 issuer: &'a Certificate<'a>,
104 issuing_key: WorkingKey<'a>,
105 context: &ValidationContext<'a, '_, V, W, R>,
106) -> BuildResult<Delegation<'a>, V::Error, R::Error> {
107 let execution = context.execution;
108 let Some(anchor) =
109 EffectiveAnchor::bind(Anchor::from_certificate(issuer), issuing_key, execution)?
110 else {
111 return Ok(Delegation::Rejected(DelegationFailure::IssuerKeyBinding));
112 };
113 let extensions = responder.tbs().extensions();
114 let Some(purposes) = extensions.extended_key_usage(execution)? else {
115 return Ok(Delegation::Rejected(DelegationFailure::SigningPurpose));
116 };
117 if !purposes.has(oid::OCSP_SIGNING, execution)? {
118 return Ok(Delegation::Rejected(DelegationFailure::SigningPurpose));
119 }
120 if !super::authority::signing_usage(responder, execution)? {
121 return Ok(Delegation::Rejected(DelegationFailure::KeyUsage));
122 }
123 let revocation = match extensions.get(oid::OCSP_NO_CHECK, execution)? {
124 Some(extension) => {
125 read_no_check(extension, execution)?;
126 ResponderRevocation::NoCheck
127 }
128 None => ResponderRevocation::Unresolved,
129 };
130 Ok(
131 match path::validate_effective(&[responder], anchor, context)? {
132 Validation::Accepted(facts) => Delegation::Accepted(DelegateFacts {
133 responder,
134 issuer,
135 key: facts.key,
136 revocation,
137 }),
138 Validation::Rejected(reason) => Delegation::PathRejected(reason),
139 Validation::Incomplete(reason) => Delegation::Incomplete(reason),
140 },
141 )
142}
143
144#[cfg(test)]
145mod tests;