use super::*;
pub use crate::provider::KeyAssociation;
pub struct SelectedKeyReceipt<'a> {
seal: &'a [u8; 1],
signature: NodeIdentity,
provider: &'a dyn crate::provider::CryptoProvider,
public: Option<&'a [u8]>,
binding: Option<&'a crate::provider::ProviderBinding>,
}
impl SelectedKeyReceipt<'_> {
pub fn signature(&self) -> NodeIdentity {
self.signature
}
pub fn provider_name(&self) -> &'static str {
self.provider.name()
}
pub fn has_public_evidence(&self) -> bool {
self.public.is_some()
}
}
impl<W: Work> VerifiedSession<'_, '_, '_, W> {
pub fn with_key_association_in<Q: Work, R, E>(
&self,
signature: NodeIdentity,
expected: structured_pkix::path::WorkingKey<'_>,
work: &Q,
visitor: impl FnOnce(KeyAssociation) -> Result<R, E>,
) -> Result<R, CatalogueError<E>> {
let index = self
.authenticated_signature_in(signature, work)
.map_err(CatalogueError::Processing)?;
flatten_catalogue(self.with_key_receipt_at_in(index, work, |receipt| {
let outcome = self
.associate_receipt_in(index, &receipt, expected, work)
.map_err(CatalogueError::Processing)?;
visitor(outcome).map_err(CatalogueError::Visitor)
}))
}
fn with_key_receipt_at_in<Q: Work, R, E>(
&self,
index: usize,
work: &Q,
visitor: impl for<'a> FnOnce(SelectedKeyReceipt<'a>) -> Result<R, E>,
) -> Result<R, CatalogueError<E>> {
self.signature_method_in(index, work)
.map_err(CatalogueError::Processing)?;
work.charge_work(1)
.map_err(|error| CatalogueError::Processing(error.into()))?;
let capture = &self.operations[index].capture;
let key = capture.key.borrow();
visitor(SelectedKeyReceipt {
seal: &capture.seal,
signature: self.evidence.signatures()[index].identity,
provider: self.context.provider,
public: key.public.as_deref(),
binding: key.binding.as_ref(),
})
.map_err(CatalogueError::Visitor)
}
fn associate_receipt_in<Q: Work>(
&self,
index: usize,
receipt: &SelectedKeyReceipt<'_>,
expected: structured_pkix::path::WorkingKey<'_>,
work: &Q,
) -> Result<KeyAssociation, DsigError> {
work.charge_work(1)?;
let capture = &self.operations[index].capture;
if !std::ptr::eq(receipt.seal, &capture.seal)
|| receipt.signature != self.evidence.signatures()[index].identity
|| !std::ptr::eq(receipt.provider, self.context.provider)
{
return Err(DsigError::InvalidRequest {
reason: "key receipt belongs to another selected operation",
});
}
let domain_matches = match (receipt.binding, receipt.provider.binding()) {
(None, None) => true,
(Some(key), Some(provider)) => key.matches(provider),
_ => false,
};
if receipt.public.is_some() && !domain_matches {
return Err(crate::provider::ProviderError::External(
crate::provider::ExternalProviderError::Binding,
)
.into());
}
let outcome = match receipt.public {
Some(public) => {
receipt
.provider
.associate_verification_key(public, expected, &|cost| work.charge_work(cost))?
}
None => KeyAssociation::Unproven,
};
work.charge_work(1)?;
Ok(outcome)
}
}
impl<W: Work, Q: Work> SelectedSignature<'_, '_, '_, '_, W, Q> {
pub fn with_key_receipt<R, E>(
&self,
visitor: impl for<'a> FnOnce(SelectedKeyReceipt<'a>) -> Result<R, E>,
) -> Result<R, CatalogueError<E>> {
self.session
.with_key_receipt_at_in(self.index, self.work, visitor)
}
pub fn associate_key<R, E>(
&self,
receipt: &SelectedKeyReceipt<'_>,
expected: structured_pkix::path::WorkingKey<'_>,
visitor: impl FnOnce(KeyAssociation) -> Result<R, E>,
) -> Result<R, CatalogueError<E>> {
self.signature_method()
.map_err(CatalogueError::Processing)?;
let outcome = self
.session
.associate_receipt_in(self.index, receipt, expected, self.work)
.map_err(CatalogueError::Processing)?;
visitor(outcome).map_err(CatalogueError::Visitor)
}
}