Skip to main content

structured_pkix/ocsp/request/
authentication.rs

1//! The original request signature, independently of requester authorization.
2
3use super::{FIELD, OcspRequest};
4use crate::error::{CallbackError, Error};
5use crate::general_name::{self, GeneralName};
6use crate::path::{Verified, Verify, WorkingKey};
7use crate::{Certificate, revocation::RevocationTarget};
8use core::fmt;
9use structured_asn1::memory::Work;
10
11/// A processing refusal or the original selected verifier's error.
12pub type RequestSignatureError<E> = CallbackError<E>;
13
14/// Completed negative signature/key result, distinct from an execution error.
15#[derive(Debug, Clone, Copy, PartialEq, Eq)]
16pub enum RequestSignatureFailure {
17    /// The supplied effective key belongs to a different original SPKI.
18    KeyBinding,
19    /// Signature arithmetic completed and rejected the signature.
20    Signature,
21}
22
23/// Relation of the declared requester name to the actual signing certificate.
24#[derive(Debug, Clone, Copy, PartialEq, Eq)]
25pub enum RequestorBinding {
26    /// Directory subject or one subjectAltName matches the original requestor.
27    /// Opaque name forms use the shared `GeneralName` encoded-identity contract.
28    Matches,
29    /// No subject/subjectAltName matches. The signature can still be valid;
30    /// this result must not be presented as an authenticated requester name.
31    Differs,
32}
33
34/// Original key-bound signature and independently reported requester-name fact.
35#[derive(Debug, Clone, Copy)]
36pub struct SignedRequest<'q, 'a, 'c, 'cert> {
37    request: &'q OcspRequest<'a>,
38    signer: &'c Certificate<'cert>,
39    key: WorkingKey<'cert>,
40    name: RequestorBinding,
41}
42impl<'q, 'a, 'c, 'cert> SignedRequest<'q, 'a, 'c, 'cert> {
43    /// The exact original request whose TBS was verified.
44    #[must_use]
45    pub fn request(self) -> &'q OcspRequest<'a> {
46        self.request
47    }
48    /// The actual certificate whose SPKI was bound before verification.
49    #[must_use]
50    pub fn signer(self) -> &'c Certificate<'cert> {
51        self.signer
52    }
53    /// Same effective key passed to the selected verifier.
54    #[must_use]
55    pub fn key(self) -> WorkingKey<'cert> {
56        self.key
57    }
58    /// Separate declared-name binding, not certificate path/server permission.
59    #[must_use]
60    pub fn requestor(self) -> RequestorBinding {
61        self.name
62    }
63}
64
65/// Mathematical authentication does not decide request acceptance or trust.
66#[derive(Debug)]
67pub enum RequestAuthentication<'q, 'a, 'c, 'cert> {
68    /// No signature was present; the verifier was not invoked.
69    Unsigned,
70    /// Original TBS verified with the actual certificate-bound effective key.
71    Signed(SignedRequest<'q, 'a, 'c, 'cert>),
72    /// Completed negative key binding or signature result.
73    Rejected(RequestSignatureFailure),
74    /// Selected verifier cannot process this algorithm/key combination.
75    UnsupportedSignature,
76}
77
78impl fmt::Display for RequestSignatureFailure {
79    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
80        formatter.write_str(match self {
81            Self::KeyBinding => "request key differs from signer SPKI",
82            Self::Signature => "request signature rejected",
83        })
84    }
85}
86
87impl<'a> OcspRequest<'a> {
88    /// Verify the exact original `TBSRequest` and bind the actual signer SPKI.
89    /// RFC6960 ยง4.1.2 makes signing optional and names mandatory when signed.
90    /// Requestor-name binding is reported separately: certificate path, key
91    /// usage, name interpretation and server authorization remain with callers.
92    /// Included certificates are never automatically selected or trusted.
93    /// <https://www.rfc-editor.org/rfc/rfc6960#section-4.1.2>
94    pub fn authenticate_signature<'q, 'c, 'cert, V: Verify<E>, E: Work>(
95        &'q self,
96        signer: &'c Certificate<'cert>,
97        key: WorkingKey<'cert>,
98        verifier: &V,
99        execution: &E,
100    ) -> core::result::Result<
101        RequestAuthentication<'q, 'a, 'c, 'cert>,
102        RequestSignatureError<V::Error>,
103    > {
104        let current = self.execution_in(execution);
105        current
106            .charge_work(1)
107            .map_err(|reason| Error::resource(reason, FIELD, self.span.start))?;
108        let Some(signature) = &self.signature else {
109            return Ok(RequestAuthentication::Unsigned);
110        };
111        let actual_work = signer.execution_in(execution);
112        let key_work = key.key().execution_in(execution);
113        if !super::binding::equal(
114            key.key().as_bytes(),
115            &key_work,
116            signer.tbs().public_key().as_bytes(),
117            &actual_work,
118            "ocspRequest.signerKey",
119            key.key().key_offset(),
120            signer.span().start,
121        )? {
122            return Ok(RequestAuthentication::Rejected(
123                RequestSignatureFailure::KeyBinding,
124            ));
125        }
126        // The parser enforces presence before constructing a signed request.
127        let Some(requestor) = self.requestor.as_ref() else {
128            return Err(Error::new(
129                crate::ErrorCode::MissingField,
130                "ocspRequest.requestorName",
131                self.signed.span().start,
132            )
133            .into());
134        };
135        let mut name = RequestorBinding::Differs;
136        if let GeneralName::Directory(requestor) = requestor
137            && requestor.matches_between(
138                signer.tbs().subject(),
139                (&current, self.parser),
140                (&actual_work, signer.source_parser()),
141            )?
142        {
143            name = RequestorBinding::Matches;
144        }
145        if name == RequestorBinding::Differs
146            && let Some(names) = signer.tbs().extensions().subject_alt_name(execution)?
147        {
148            for candidate in &*names {
149                actual_work.charge_work(1).map_err(|reason| {
150                    Error::resource(reason, "ocspRequest.requestorName", signer.span().start)
151                })?;
152                if general_name::matches_between(
153                    requestor,
154                    candidate,
155                    (&current, self.parser),
156                    (&actual_work, signer.source_parser()),
157                    self.signed.span().start,
158                    "ocspRequest.requestorName",
159                )? {
160                    name = RequestorBinding::Matches;
161                    break;
162                }
163            }
164        }
165        current
166            .charge_work(1)
167            .map_err(|reason| Error::resource(reason, FIELD, self.span.start))?;
168        match verifier
169            .verify(
170                &signature.algorithm,
171                key,
172                self.signed_bytes(),
173                signature.value,
174                execution,
175            )
176            .map_err(CallbackError::Callback)?
177        {
178            Verified::Yes => Ok(RequestAuthentication::Signed(SignedRequest {
179                request: self,
180                signer,
181                key,
182                name,
183            })),
184            Verified::No => Ok(RequestAuthentication::Rejected(
185                RequestSignatureFailure::Signature,
186            )),
187            Verified::Unsupported => Ok(RequestAuthentication::UnsupportedSignature),
188        }
189    }
190}