use crate::binding::{self, Binding, Protection, Scope};
use crate::provider::{DigestAlgorithm, Resolution, SignatureMode, SignatureProvider};
use crate::signer::SignerInfo;
use crate::{Error, ErrorCode};
use core::cmp::Ordering;
use structured_asn1::Reader;
use structured_asn1::memory::Work;
use structured_pkix::path::WorkingKey;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Part {
Digest,
Signature,
ProtectedDigest,
ProtectedSignature,
DigestComparison,
SignatureComparison,
}
#[derive(Debug)]
pub enum PrepareError<E> {
Processing(Error),
Callback {
part: Part,
source: E,
},
}
pub enum Preparation<'a, P: SignatureProvider<'a>> {
Ready(ResolvedSigner<'a, P>),
Unsupported(Part),
Malformed(Error),
}
#[derive(Debug)]
pub enum ResolvedBinding<'a> {
Direct,
Attributes {
digest: &'a [u8],
digest_span: structured_asn1::Span,
},
}
pub struct ResolvedSigner<'a, P: SignatureProvider<'a>> {
provider: &'a P,
signer: &'a SignerInfo<'a>,
key: WorkingKey<'a>,
binding: ResolvedBinding<'a>,
digest: P::Identity,
signature: SignatureMode<P::SignatureIdentity>,
}
impl<'a, P: SignatureProvider<'a>> ResolvedSigner<'a, P> {
pub(crate) fn into_parts(self) -> ResolvedParts<'a, P> {
ResolvedParts {
signer: self.signer,
key: self.key,
binding: self.binding,
digest: self.digest,
signature: self.signature,
}
}
#[must_use]
pub fn provider(&self) -> &'a P {
self.provider
}
#[must_use]
pub fn signer(&self) -> &'a SignerInfo<'a> {
self.signer
}
#[must_use]
pub fn key(&self) -> WorkingKey<'a> {
self.key
}
#[must_use]
pub fn binding(&self) -> &ResolvedBinding<'a> {
&self.binding
}
#[must_use]
pub fn digest(&self) -> &P::Identity {
&self.digest
}
#[must_use]
pub fn signature(&self) -> &SignatureMode<P::SignatureIdentity> {
&self.signature
}
pub fn same_signer_value(
&self,
other: &Self,
parent: &Reader<'_>,
) -> Result<bool, PrepareError<P::Error>> {
if !core::ptr::eq(self.provider, other.provider) {
return Err(PrepareError::Processing(Error::schema(
ErrorCode::ProviderMismatch,
"signedData.signerInfos",
other.signer.span().start,
)));
}
if !self
.signer
.same_fields(other.signer, parent)
.map_err(PrepareError::Processing)?
{
return Ok(false);
}
let execution = parent.execution();
let digest = self
.provider
.compare_digest(&self.digest, &other.digest, &execution)
.map_err(|source| PrepareError::Callback {
part: Part::DigestComparison,
source,
})?;
if digest != Ordering::Equal {
return Ok(false);
}
let ((SignatureMode::Raw(left), SignatureMode::Raw(right))
| (SignatureMode::Prehashed(left), SignatureMode::Prehashed(right))) =
(&self.signature, &other.signature)
else {
return Ok(false);
};
self.provider
.compare_signature(left, right, &execution)
.map(|order| order == Ordering::Equal)
.map_err(|source| PrepareError::Callback {
part: Part::SignatureComparison,
source,
})
}
}
pub(crate) struct ResolvedParts<'a, P: SignatureProvider<'a>> {
pub signer: &'a SignerInfo<'a>,
pub key: WorkingKey<'a>,
pub binding: ResolvedBinding<'a>,
pub digest: P::Identity,
pub signature: SignatureMode<P::SignatureIdentity>,
}
pub fn resolve<'a, P: SignatureProvider<'a>>(
provider: &'a P,
signer: &'a SignerInfo<'a>,
key: WorkingKey<'a>,
parent: &Reader<'a>,
scope: Scope<'a>,
) -> Result<Preparation<'a, P>, PrepareError<P::Error>> {
crate::provider::require_instance(provider, "signerInfo", signer.span().start)
.map_err(PrepareError::Processing)?;
signer
.require_session(parent, "signerInfo")
.map_err(PrepareError::Processing)?;
let identifiers = match algorithms(signer) {
Ok(identifiers) => identifiers,
Err(error) => return Ok(Preparation::Malformed(error)),
};
let binding = match binding::inspect(signer, parent, scope) {
Ok(binding) => binding,
Err(error) if error.is_terminal() => return Err(PrepareError::Processing(error)),
Err(error) => return Ok(Preparation::Malformed(error)),
};
let identifier = identifiers.0;
let digest = match provider
.resolve_digest(
DigestAlgorithm::Written(identifier),
identifier.encoding(),
parent,
)
.map_err(|source| PrepareError::Callback {
part: Part::Digest,
source,
})? {
Resolution::Supported(digest) => digest,
Resolution::Unsupported => return Ok(Preparation::Unsupported(Part::Digest)),
Resolution::Malformed => {
return Ok(Preparation::Malformed(Error::schema(
ErrorCode::AlgorithmSyntax,
"signerInfo.digestAlgorithm",
identifier.offset(),
)));
}
};
if let Binding::Attributes {
digest: expected,
digest_span,
..
} = &binding
&& let Some(width) = provider.digest_width(&digest)
&& expected.len() != width
{
return Ok(Preparation::Malformed(Error::schema(
ErrorCode::DigestWidth,
"signerInfo.signedAttrs.messageDigest",
digest_span.start,
)));
}
let identifier = identifiers.1;
let signature = match provider
.resolve_signature(identifier, &digest, key, identifier.encoding(), parent)
.map_err(|source| PrepareError::Callback {
part: Part::Signature,
source,
})? {
Resolution::Supported(signature) => signature,
Resolution::Unsupported => return Ok(Preparation::Unsupported(Part::Signature)),
Resolution::Malformed => {
return Ok(Preparation::Malformed(Error::schema(
ErrorCode::AlgorithmSyntax,
"signerInfo.signatureAlgorithm",
identifier.offset(),
)));
}
};
if let Binding::Attributes {
protection: Some(protection),
..
} = &binding
{
match protected(provider, *protection, &digest, &signature, key, parent)? {
ProtectionStatus::Matching => {}
ProtectionStatus::Unsupported(part) => return Ok(Preparation::Unsupported(part)),
ProtectionStatus::Malformed(error) => return Ok(Preparation::Malformed(error)),
}
}
let binding = match binding {
Binding::Direct => ResolvedBinding::Direct,
Binding::Attributes {
digest,
digest_span,
..
} => ResolvedBinding::Attributes {
digest,
digest_span,
},
};
Ok(Preparation::Ready(ResolvedSigner {
provider,
signer,
key,
binding,
digest,
signature,
}))
}
enum ProtectionStatus {
Matching,
Unsupported(Part),
Malformed(Error),
}
fn algorithms<'a>(
signer: &SignerInfo<'a>,
) -> Result<
(
structured_pkix::AlgorithmIdentifier<'a>,
structured_pkix::AlgorithmIdentifier<'a>,
),
Error,
> {
let digest = signer
.digest_algorithm()
.require_identifier()
.map_err(Error::pkix)?;
let signature = signer
.signature_algorithm()
.require_identifier()
.map_err(Error::pkix)?;
Ok((digest, signature))
}
fn protected<'a, P: SignatureProvider<'a>>(
provider: &'a P,
protection: Protection<'a>,
digest: &P::Identity,
signature: &SignatureMode<P::SignatureIdentity>,
key: WorkingKey<'a>,
parent: &Reader<'a>,
) -> Result<ProtectionStatus, PrepareError<P::Error>> {
const FIELD: &str = "signerInfo.signedAttrs.cmsAlgorithmProtection";
let identifier = protection.digest();
let protected_digest = match provider
.resolve_digest(
DigestAlgorithm::Written(identifier),
identifier.encoding(),
parent,
)
.map_err(|source| PrepareError::Callback {
part: Part::ProtectedDigest,
source,
})? {
Resolution::Supported(digest) => digest,
Resolution::Unsupported => return Ok(ProtectionStatus::Unsupported(Part::ProtectedDigest)),
Resolution::Malformed => {
return Ok(ProtectionStatus::Malformed(Error::schema(
ErrorCode::AlgorithmSyntax,
FIELD,
identifier.offset(),
)));
}
};
if provider
.compare_digest(digest, &protected_digest, &parent.execution())
.map_err(|source| PrepareError::Callback {
part: Part::DigestComparison,
source,
})?
!= Ordering::Equal
{
return Ok(ProtectionStatus::Malformed(Error::schema(
ErrorCode::AlgorithmProtection,
FIELD,
identifier.offset(),
)));
}
let identifier = protection.signature();
let protected_signature = match provider
.resolve_signature(
identifier,
&protected_digest,
key,
identifier.encoding(),
parent,
)
.map_err(|source| PrepareError::Callback {
part: Part::ProtectedSignature,
source,
})? {
Resolution::Supported(signature) => signature,
Resolution::Unsupported => {
return Ok(ProtectionStatus::Unsupported(Part::ProtectedSignature));
}
Resolution::Malformed => {
return Ok(ProtectionStatus::Malformed(Error::schema(
ErrorCode::AlgorithmSyntax,
FIELD,
identifier.offset(),
)));
}
};
parent.execution().charge_work(1).map_err(|reason| {
PrepareError::Processing(Error::resource(reason, FIELD, identifier.offset()))
})?;
let same = match (signature, &protected_signature) {
(SignatureMode::Raw(left), SignatureMode::Raw(right))
| (SignatureMode::Prehashed(left), SignatureMode::Prehashed(right)) => {
provider
.compare_signature(left, right, &parent.execution())
.map_err(|source| PrepareError::Callback {
part: Part::SignatureComparison,
source,
})?
== Ordering::Equal
}
_ => false,
};
if !same {
return Ok(ProtectionStatus::Malformed(Error::schema(
ErrorCode::AlgorithmProtection,
FIELD,
identifier.offset(),
)));
}
Ok(ProtectionStatus::Matching)
}