use super::{DelegateFacts, Digest, Lookup, ResponderRevocation, SignedResponse};
use crate::Certificate;
use crate::error::{CallbackResult, Error, Result};
use crate::path::WorkingKey;
use structured_asn1::memory::{Memory, MemoryError};
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthoritySource {
Issuer,
Delegate {
revocation: ResponderRevocation,
},
Configured,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AuthorityFailure {
IssuerKeyBinding,
Signer,
EffectiveKey,
KeyUsage,
}
#[derive(Debug, Clone, Copy)]
pub struct ScopedResponse<'r, 'a> {
signed: SignedResponse<'r, 'a>,
issuer: &'r Certificate<'a>,
source: AuthoritySource,
}
impl<'r, 'a> ScopedResponse<'r, 'a> {
#[must_use]
pub fn signed(self) -> SignedResponse<'r, 'a> {
self.signed
}
#[must_use]
pub fn issuer(self) -> &'r Certificate<'a> {
self.issuer
}
#[must_use]
pub fn source(self) -> AuthoritySource {
self.source
}
pub fn find<D: Digest>(
self,
target: &Certificate<'_>,
digest: &D,
execution: &Memory,
) -> CallbackResult<Lookup<'r, 'a>, D::Error> {
self.signed.find(target, self.issuer, digest, execution)
}
}
#[derive(Debug, Clone, Copy)]
pub enum AuthorityBinding<'r, 'a> {
Bound(ScopedResponse<'r, 'a>),
Rejected(AuthorityFailure),
}
fn equal_bytes(left: &[u8], right: &[u8], execution: &Memory, at: u64) -> Result<bool> {
let work = u64::try_from(left.len().max(right.len()))
.ok()
.and_then(|n| n.checked_mul(2))
.and_then(|n| n.checked_add(1))
.ok_or_else(|| Error::resource(MemoryError::WorkExceeded, "ocspResponse.authority", at))?;
execution
.charge_work(work)
.map_err(|reason| Error::resource(reason, "ocspResponse.authority", at))?;
Ok(left == right)
}
fn equal_key(
left: WorkingKey<'_>,
right: WorkingKey<'_>,
execution: &Memory,
at: u64,
) -> Result<bool> {
if !equal_bytes(left.key().as_bytes(), right.key().as_bytes(), execution, at)? {
return Ok(false);
}
match (left.parameters(), right.parameters()) {
(None, None) => Ok(true),
(Some(left), Some(right)) => equal_bytes(left, right, execution, at),
_ => Ok(false),
}
}
pub(super) fn signing_usage(certificate: &Certificate<'_>, execution: &Memory) -> Result<bool> {
Ok(certificate
.tbs()
.extensions()
.key_usage(execution)?
.is_none_or(|usage| usage.digital_signature() || usage.content_commitment()))
}
impl<'r, 'a> SignedResponse<'r, 'a> {
pub fn bind_issuer(
self,
issuer: &'r Certificate<'a>,
issuing_key: WorkingKey<'a>,
execution: &Memory,
) -> Result<AuthorityBinding<'r, 'a>> {
let at = self.response().span().start;
if !equal_bytes(
issuer.tbs().public_key().as_bytes(),
issuing_key.key().as_bytes(),
execution,
at,
)? {
return Ok(AuthorityBinding::Rejected(
AuthorityFailure::IssuerKeyBinding,
));
}
if !equal_bytes(
self.signer().tbs().public_key().as_bytes(),
issuer.tbs().public_key().as_bytes(),
execution,
at,
)? || !self
.signer()
.tbs()
.subject()
.matches(issuer.tbs().subject(), execution)?
{
return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
}
if !equal_key(self.key(), issuing_key, execution, at)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
}
if !signing_usage(issuer, execution)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::KeyUsage));
}
Ok(AuthorityBinding::Bound(ScopedResponse {
signed: self,
issuer,
source: AuthoritySource::Issuer,
}))
}
pub fn bind_delegate(
self,
facts: DelegateFacts<'a>,
execution: &Memory,
) -> Result<AuthorityBinding<'r, 'a>>
where
'a: 'r,
{
let at = self.response().span().start;
if !equal_bytes(
self.signer().as_bytes(),
facts.responder().as_bytes(),
execution,
at,
)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::Signer));
}
if !equal_key(self.key(), facts.key(), execution, at)? {
return Ok(AuthorityBinding::Rejected(AuthorityFailure::EffectiveKey));
}
Ok(AuthorityBinding::Bound(ScopedResponse {
signed: self,
issuer: facts.issuer(),
source: AuthoritySource::Delegate {
revocation: facts.revocation(),
},
}))
}
#[must_use]
pub fn trust_for_issuer(self, issuer: &'r Certificate<'a>) -> ScopedResponse<'r, 'a> {
ScopedResponse {
signed: self,
issuer,
source: AuthoritySource::Configured,
}
}
}