use super::{FIELD, OcspRequest};
use crate::error::{CallbackError, Error};
use crate::general_name::{self, GeneralName};
use crate::path::{Verified, Verify, WorkingKey};
use crate::{Certificate, revocation::RevocationTarget};
use core::fmt;
use structured_asn1::memory::Work;
pub type RequestSignatureError<E> = CallbackError<E>;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestSignatureFailure {
KeyBinding,
Signature,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum RequestorBinding {
Matches,
Differs,
}
#[derive(Debug, Clone, Copy)]
pub struct SignedRequest<'q, 'a, 'c, 'cert> {
request: &'q OcspRequest<'a>,
signer: &'c Certificate<'cert>,
key: WorkingKey<'cert>,
name: RequestorBinding,
}
impl<'q, 'a, 'c, 'cert> SignedRequest<'q, 'a, 'c, 'cert> {
#[must_use]
pub fn request(self) -> &'q OcspRequest<'a> {
self.request
}
#[must_use]
pub fn signer(self) -> &'c Certificate<'cert> {
self.signer
}
#[must_use]
pub fn key(self) -> WorkingKey<'cert> {
self.key
}
#[must_use]
pub fn requestor(self) -> RequestorBinding {
self.name
}
}
#[derive(Debug)]
pub enum RequestAuthentication<'q, 'a, 'c, 'cert> {
Unsigned,
Signed(SignedRequest<'q, 'a, 'c, 'cert>),
Rejected(RequestSignatureFailure),
UnsupportedSignature,
}
impl fmt::Display for RequestSignatureFailure {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(match self {
Self::KeyBinding => "request key differs from signer SPKI",
Self::Signature => "request signature rejected",
})
}
}
impl<'a> OcspRequest<'a> {
pub fn authenticate_signature<'q, 'c, 'cert, V: Verify<E>, E: Work>(
&'q self,
signer: &'c Certificate<'cert>,
key: WorkingKey<'cert>,
verifier: &V,
execution: &E,
) -> core::result::Result<
RequestAuthentication<'q, 'a, 'c, 'cert>,
RequestSignatureError<V::Error>,
> {
let current = self.execution_in(execution);
current
.charge_work(1)
.map_err(|reason| Error::resource(reason, FIELD, self.span.start))?;
let Some(signature) = &self.signature else {
return Ok(RequestAuthentication::Unsigned);
};
let actual_work = signer.execution_in(execution);
let key_work = key.key().execution_in(execution);
if !super::binding::equal(
key.key().as_bytes(),
&key_work,
signer.tbs().public_key().as_bytes(),
&actual_work,
"ocspRequest.signerKey",
key.key().key_offset(),
signer.span().start,
)? {
return Ok(RequestAuthentication::Rejected(
RequestSignatureFailure::KeyBinding,
));
}
let Some(requestor) = self.requestor.as_ref() else {
return Err(Error::new(
crate::ErrorCode::MissingField,
"ocspRequest.requestorName",
self.signed.span().start,
)
.into());
};
let mut name = RequestorBinding::Differs;
if let GeneralName::Directory(requestor) = requestor
&& requestor.matches_between(
signer.tbs().subject(),
(¤t, self.parser),
(&actual_work, signer.source_parser()),
)?
{
name = RequestorBinding::Matches;
}
if name == RequestorBinding::Differs
&& let Some(names) = signer.tbs().extensions().subject_alt_name(execution)?
{
for candidate in &*names {
actual_work.charge_work(1).map_err(|reason| {
Error::resource(reason, "ocspRequest.requestorName", signer.span().start)
})?;
if general_name::matches_between(
requestor,
candidate,
(¤t, self.parser),
(&actual_work, signer.source_parser()),
self.signed.span().start,
"ocspRequest.requestorName",
)? {
name = RequestorBinding::Matches;
break;
}
}
}
current
.charge_work(1)
.map_err(|reason| Error::resource(reason, FIELD, self.span.start))?;
match verifier
.verify(
&signature.algorithm,
key,
self.signed_bytes(),
signature.value,
execution,
)
.map_err(CallbackError::Callback)?
{
Verified::Yes => Ok(RequestAuthentication::Signed(SignedRequest {
request: self,
signer,
key,
name,
})),
Verified::No => Ok(RequestAuthentication::Rejected(
RequestSignatureFailure::Signature,
)),
Verified::Unsupported => Ok(RequestAuthentication::UnsupportedSignature),
}
}
}