use super::{BasicResponse, ResponderMatch};
use crate::algorithm::digest::AsnDigest;
use crate::error::CallbackError;
use crate::path::{Verified, Verify, WorkingKey};
use crate::{Certificate, Error};
use core::fmt;
use structured_asn1::memory::{MemoryError, Work};
#[derive(Debug)]
pub enum SignatureError<V, D> {
Processing(Error),
Verification(V),
Digest(D),
}
impl<V, D> From<Error> for SignatureError<V, D> {
fn from(error: Error) -> Self {
Self::Processing(error)
}
}
impl<V: fmt::Display, D: fmt::Display> fmt::Display for SignatureError<V, D> {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Processing(error) => error.fmt(formatter),
Self::Verification(error) => write!(formatter, "signature verifier: {error}"),
Self::Digest(error) => write!(formatter, "digest provider: {error}"),
}
}
}
#[cfg(feature = "std")]
impl<V: std::error::Error + 'static, D: std::error::Error + 'static> std::error::Error
for SignatureError<V, D>
{
fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
match self {
Self::Processing(error) => Some(error),
Self::Verification(error) => Some(error),
Self::Digest(error) => Some(error),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum SignatureFailure {
KeyBinding,
ResponderIdentity,
DigestWidth,
Signature,
}
#[derive(Debug, Clone, Copy)]
pub struct SignedResponse<'r, 'a> {
response: &'r BasicResponse<'a>,
signer: &'r Certificate<'a>,
key: WorkingKey<'a>,
}
impl<'r, 'a> SignedResponse<'r, 'a> {
#[must_use]
pub fn response(self) -> &'r BasicResponse<'a> {
self.response
}
#[must_use]
pub fn signer(self) -> &'r Certificate<'a> {
self.signer
}
#[must_use]
pub fn key(self) -> WorkingKey<'a> {
self.key
}
}
#[derive(Debug)]
pub enum SignatureAuthentication<'r, 'a> {
Signed(SignedResponse<'r, 'a>),
Rejected(SignatureFailure),
UnsupportedDigest,
UnsupportedSignature,
}
impl<'a> BasicResponse<'a> {
pub fn authenticate_signature<'r, 'd, V: Verify<E>, D: AsnDigest<'d> + ?Sized, E: Work>(
&'r self,
signer: &'r Certificate<'a>,
key: WorkingKey<'a>,
verifier: &V,
digest: &'d D,
execution: &E,
) -> core::result::Result<SignatureAuthentication<'r, 'a>, SignatureError<V::Error, D::Error>>
{
const FIELD: &str = "ocspResponse.signature";
let charge = |work| {
execution
.charge_work(work)
.map_err(|reason| Error::resource(reason, FIELD, self.span.start))
};
let encoded = key.key().as_bytes();
let actual = signer.tbs().public_key().as_bytes();
let work = u64::try_from(encoded.len().max(actual.len()))
.ok()
.and_then(|n| n.checked_mul(2))
.and_then(|n| n.checked_add(1))
.ok_or_else(|| Error::resource(MemoryError::WorkExceeded, FIELD, self.span.start))?;
charge(work)?;
if encoded != actual {
return Ok(SignatureAuthentication::Rejected(
SignatureFailure::KeyBinding,
));
}
let identity = self
.responder
.matches_certificate(signer, digest, execution, FIELD, self.span.start)
.map_err(|cause| match cause {
CallbackError::Processing(cause) => SignatureError::Processing(cause),
CallbackError::Callback(cause) => SignatureError::Digest(cause),
})?;
match identity {
ResponderMatch::Matches => {}
ResponderMatch::Differs => {
return Ok(SignatureAuthentication::Rejected(
SignatureFailure::ResponderIdentity,
));
}
ResponderMatch::MalformedHashWidth => {
return Ok(SignatureAuthentication::Rejected(
SignatureFailure::DigestWidth,
));
}
ResponderMatch::UnsupportedHash => {
return Ok(SignatureAuthentication::UnsupportedDigest);
}
}
charge(1)?;
match verifier
.verify(&self.algorithm, key, self.signed, self.signature, execution)
.map_err(SignatureError::Verification)?
{
Verified::Yes => Ok(SignatureAuthentication::Signed(SignedResponse {
response: self,
signer,
key,
})),
Verified::No => Ok(SignatureAuthentication::Rejected(
SignatureFailure::Signature,
)),
Verified::Unsupported => Ok(SignatureAuthentication::UnsupportedSignature),
}
}
}
#[cfg(test)]
mod tests;