use cms::content_info::CmsVersion;
use cms::content_info::ContentInfo;
use cms::signed_data::{SignedData, SignerInfo};
use core::fmt::{self, Display, Formatter};
use der::Decode;
use der::asn1::{ObjectIdentifier, OctetString};
use der::{Sequence, SliceReader};
use x509_cert::Certificate;
pub const SPC_INDIRECT_DATA_OBJID: ObjectIdentifier =
ObjectIdentifier::new_unwrap("1.3.6.1.4.1.311.2.1.4");
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct SpcIndirectDataContent {
pub data: SpcAttributeTypeAndOptionalValue,
pub message_digest: DigestInfo,
}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct SpcAttributeTypeAndOptionalValue {
pub value_type: ObjectIdentifier,
pub value: der::Any,
}
#[derive(Clone, Debug, Eq, PartialEq, Sequence)]
pub struct DigestInfo {
pub digest_algorithm: spki::AlgorithmIdentifierOwned,
pub digest: OctetString,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum AuthenticodeSignatureParseError {
Empty,
InvalidContentInfo(der::Error),
InvalidContentType(ObjectIdentifier),
InvalidSignedData(der::Error),
InvalidSignedDataVersion(CmsVersion),
InvalidNumDigestAlgorithms(usize),
InvalidEncapsulatedContentType(ObjectIdentifier),
EmptyEncapsulatedContent,
InvalidSpcIndirectDataContent(der::Error),
InvalidNumSignerInfo(usize),
InvalidSignerInfoVersion(CmsVersion),
AlgorithmMismatch,
EmptyAuthenticatedAttributes,
MissingContentTypeAuthenticatedAttribute,
MissingMessageDigestAuthenticatedAttribute,
}
impl Display for AuthenticodeSignatureParseError {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
write!(f, "authenticode signature parse error: {self:?}")
}
}
#[cfg(feature = "std")]
impl std::error::Error for AuthenticodeSignatureParseError {}
#[derive(Clone, Debug, Eq, PartialEq)]
pub struct AuthenticodeSignature {
signed_data: SignedData,
indirect_data: SpcIndirectDataContent,
}
impl AuthenticodeSignature {
pub fn from_bytes(
bytes: &[u8],
) -> Result<Self, AuthenticodeSignatureParseError> {
let mut reader = SliceReader::new(bytes)
.map_err(|_| AuthenticodeSignatureParseError::Empty)?;
let content_info = ContentInfo::decode(&mut reader)
.map_err(AuthenticodeSignatureParseError::InvalidContentInfo)?;
if content_info.content_type != const_oid::db::rfc6268::ID_SIGNED_DATA {
return Err(AuthenticodeSignatureParseError::InvalidContentType(
content_info.content_type,
));
}
let signed_data = content_info
.content
.decode_as::<SignedData>()
.map_err(AuthenticodeSignatureParseError::InvalidSignedData)?;
if signed_data.version != CmsVersion::V1 {
return Err(
AuthenticodeSignatureParseError::InvalidSignedDataVersion(
signed_data.version,
),
);
}
if signed_data.digest_algorithms.len() != 1 {
return Err(
AuthenticodeSignatureParseError::InvalidNumDigestAlgorithms(
signed_data.digest_algorithms.len(),
),
);
}
if signed_data.encap_content_info.econtent_type
!= SPC_INDIRECT_DATA_OBJID
{
return Err(
AuthenticodeSignatureParseError::InvalidEncapsulatedContentType(
signed_data.encap_content_info.econtent_type,
),
);
}
let indirect_data = signed_data
.clone()
.encap_content_info
.econtent
.ok_or(AuthenticodeSignatureParseError::EmptyEncapsulatedContent)?
.decode_as::<SpcIndirectDataContent>()
.map_err(
AuthenticodeSignatureParseError::InvalidSpcIndirectDataContent,
)?;
if signed_data.signer_infos.0.len() != 1 {
return Err(AuthenticodeSignatureParseError::InvalidNumSignerInfo(
signed_data.signer_infos.0.len(),
));
}
let signer_info = &signed_data.signer_infos.0.as_slice()[0];
if signer_info.version != CmsVersion::V1 {
return Err(
AuthenticodeSignatureParseError::InvalidSignerInfoVersion(
signer_info.version,
),
);
}
if signer_info.digest_alg != signed_data.digest_algorithms.as_slice()[0]
{
return Err(AuthenticodeSignatureParseError::AlgorithmMismatch);
}
let signed_attrs = if let Some(signed_attrs) = &signer_info.signed_attrs
{
signed_attrs
} else {
return Err(
AuthenticodeSignatureParseError::EmptyAuthenticatedAttributes,
);
};
if !signed_attrs
.iter()
.any(|a| a.oid == const_oid::db::rfc6268::ID_CONTENT_TYPE)
{
return Err(AuthenticodeSignatureParseError::MissingContentTypeAuthenticatedAttribute);
}
if !signed_attrs
.iter()
.any(|a| a.oid == const_oid::db::rfc6268::ID_MESSAGE_DIGEST)
{
return Err(AuthenticodeSignatureParseError::MissingMessageDigestAuthenticatedAttribute);
}
Ok(Self {
signed_data: signed_data.clone(),
indirect_data,
})
}
pub fn signer_info(&self) -> &SignerInfo {
&self.signed_data.signer_infos.0.as_slice()[0]
}
pub fn digest(&self) -> &[u8] {
self.indirect_data.message_digest.digest.as_bytes()
}
pub fn signature(&self) -> &[u8] {
self.signer_info().signature.as_bytes()
}
pub fn encapsulated_content(&self) -> Option<&[u8]> {
self.signed_data
.encap_content_info
.econtent
.as_ref()
.map(|c| c.value())
}
pub fn certificates(&self) -> impl Iterator<Item = &Certificate> {
self.signed_data
.certificates
.as_ref()
.unwrap()
.0
.iter()
.map(|cert| {
if let cms::cert::CertificateChoices::Certificate(cert) = cert {
cert
} else {
panic!()
}
})
}
}