Skip to main content

structured_pkix/ocsp/
authentication.rs

1//! Actual signer binding and response signature authentication.
2
3use super::{BasicResponse, ResponderMatch};
4use crate::algorithm::digest::AsnDigest;
5use crate::error::CallbackError;
6use crate::path::{Verified, Verify, WorkingKey};
7use crate::{Certificate, Error};
8use core::fmt;
9use structured_asn1::memory::{MemoryError, Work};
10
11/// Failure to execute authentication, retaining the originating capability.
12#[derive(Debug)]
13pub enum SignatureError<V, D> {
14    /// Located shared processing/resource failure.
15    Processing(Error),
16    /// Signature provider failure, including its resource or policy error.
17    Verification(V),
18    /// Digest provider failure, including its resource or policy error.
19    Digest(D),
20}
21
22impl<V, D> From<Error> for SignatureError<V, D> {
23    fn from(error: Error) -> Self {
24        Self::Processing(error)
25    }
26}
27
28impl<V: fmt::Display, D: fmt::Display> fmt::Display for SignatureError<V, D> {
29    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
30        match self {
31            Self::Processing(error) => error.fmt(formatter),
32            Self::Verification(error) => write!(formatter, "signature verifier: {error}"),
33            Self::Digest(error) => write!(formatter, "digest provider: {error}"),
34        }
35    }
36}
37
38impl<V: core::error::Error + 'static, D: core::error::Error + 'static> core::error::Error
39    for SignatureError<V, D>
40{
41    fn source(&self) -> Option<&(dyn core::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/// Completed negative binding/signature result, not an execution failure.
51#[derive(Debug, Clone, Copy, PartialEq, Eq)]
52pub enum SignatureFailure {
53    /// Effective key does not belong to the supplied signer's original SPKI.
54    KeyBinding,
55    /// `ResponderID` does not identify the actual supplied signer.
56    ResponderIdentity,
57    /// Provider resolved protocol-imposed SHA-1 with a contradicting width.
58    DigestWidth,
59    /// The completed signature verification rejected the signature.
60    Signature,
61}
62
63/// Original response authenticated by its actual signer. This proves neither
64/// signer authorization for a target issuer nor freshness or request binding.
65#[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    /// Original signed response, retaining every assertion and extension.
74    #[must_use]
75    pub fn response(self) -> &'r BasicResponse<'a> {
76        self.response
77    }
78    /// Actual certificate whose subject/key matched `ResponderID`.
79    #[must_use]
80    pub fn signer(self) -> &'r Certificate<'a> {
81        self.signer
82    }
83    /// Effective key bound to that certificate's original SPKI.
84    #[must_use]
85    pub fn key(self) -> WorkingKey<'a> {
86        self.key
87    }
88}
89
90/// Signature authentication outcome with unsupported capabilities separate.
91#[derive(Debug)]
92pub enum SignatureAuthentication<'r, 'a> {
93    /// Signer identity and signature established; authorization remains separate.
94    Signed(SignedResponse<'r, 'a>),
95    /// Completed semantic rejection.
96    Rejected(SignatureFailure),
97    /// Protocol-imposed SHA-1 cannot be supplied by this provider/policy.
98    UnsupportedDigest,
99    /// Signature algorithm/effective key is unsupported by this verifier.
100    UnsupportedSignature,
101}
102
103impl<'a> BasicResponse<'a> {
104    /// Authenticate exact `ResponseData` with the actual signer's bound key.
105    /// Included certificates are candidates only. RFC6960 §§4.2.2.2–4.2.2.3
106    /// require authorization and status validity beyond this signature fact.
107    /// <https://www.rfc-editor.org/rfc/rfc6960#section-4.2.2.2>
108    pub fn authenticate_signature<'r, 'd, V: Verify<E>, D: AsnDigest<'d> + ?Sized, E: Work>(
109        &'r self,
110        signer: &'r Certificate<'a>,
111        key: WorkingKey<'a>,
112        verifier: &V,
113        digest: &'d 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;