use crate::attribute::{Attributes, Placement};
use crate::{Error, ErrorCode, Octets, Result};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{Element, Encoding, Integer, Reader, Span, Tag, universal};
use structured_pkix::{AlgorithmIdentifier, Name};
#[derive(Debug)]
pub enum SignerIdentifier<'a> {
IssuerAndSerial {
issuer: Name<'a>,
serial: Integer<'a>,
},
SubjectKeyIdentifier(Octets<'a>),
}
impl<'a> SignerIdentifier<'a> {
fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
const FIELD: &str = "signerInfo.sid";
if element.tag() == Tag::context(false, 0) || element.tag() == Tag::context(true, 0) {
return Octets::read_identifier(parent, element, FIELD).map(Self::SubjectKeyIdentifier);
}
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
FIELD,
element.span().start,
));
}
let mut inside = parent
.enter(element)
.map_err(|error| Error::encoding(error, FIELD))?;
let name = inside
.expect(Tag::constructed(universal::SEQUENCE))
.map_err(|error| Error::encoding(error, FIELD))?;
let issuer = structured_pkix::name::read(&inside, &name, FIELD).map_err(Error::pkix)?;
let serial = inside
.read_integer()
.map_err(|error| Error::encoding(error, FIELD))?;
if !inside.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
FIELD,
inside.offset(),
));
}
Ok(Self::IssuerAndSerial { issuer, serial })
}
#[must_use]
pub fn required_version(&self) -> u64 {
match self {
Self::IssuerAndSerial { .. } => 1,
Self::SubjectKeyIdentifier(_) => 3,
}
}
}
#[derive(Debug)]
pub struct SignerInfo<'a> {
version: u64,
sid: SignerIdentifier<'a>,
digest_algorithm: AlgorithmIdentifier<'a>,
signed_attributes: Option<Attributes<'a>>,
signature_algorithm: AlgorithmIdentifier<'a>,
signature: Octets<'a>,
unsigned_attributes: Option<Attributes<'a>>,
encoding: Encoding,
element: Element<'a>,
faults: OwnedVec<Error>,
}
impl<'a> SignerInfo<'a> {
pub(crate) fn require_session(&self, parent: &Reader<'_>, field: &'static str) -> Result<()> {
if !self.digest_algorithm.same_session(parent) {
return Err(Error::pkix(structured_pkix::Error::new(
structured_pkix::ErrorCode::ForeignReader,
field,
self.span().start,
)));
}
Ok(())
}
pub fn read(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
const FIELD: &str = "signerInfo";
if element.tag() != Tag::constructed(universal::SEQUENCE) {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
FIELD,
element.span().start,
));
}
let mut inside = parent
.enter(element)
.map_err(|error| Error::encoding(error, FIELD))?;
let execution = parent.execution();
let mut faults = execution.vector();
let version_element = inside
.expect(Tag::primitive(universal::INTEGER))
.map_err(|error| Error::encoding(error, "signerInfo.version"))?;
let version = inside
.integer(&version_element)
.map_err(|error| Error::encoding(error, "signerInfo.version"))?
.as_u64()
.ok_or_else(|| {
Error::schema(
ErrorCode::ValueTooLarge,
"signerInfo.version",
version_element.span().start,
)
})?;
let named = required(&mut inside, "signerInfo.sid")?;
let sid = SignerIdentifier::read(&inside, &named)?;
if version != sid.required_version() {
faults
.try_push(Error::schema(
ErrorCode::VersionMismatch,
"signerInfo.version",
version_element.span().start,
))
.map_err(|reason| {
Error::resource(reason, "signerInfo.version", version_element.span().start)
})?;
}
let digest = required(&mut inside, "signerInfo.digestAlgorithm")?;
let digest_algorithm =
structured_pkix::algorithm::read(&inside, &digest, "signerInfo.digestAlgorithm")
.map_err(Error::pkix)?;
let mut next = required(&mut inside, "signerInfo.signatureAlgorithm")?;
let signed_attributes = if next.tag() == Tag::context(true, 0) {
let attributes = Attributes::read(&inside, &next, Placement::Signed)?;
next = required(&mut inside, "signerInfo.signatureAlgorithm")?;
Some(attributes)
} else {
None
};
let signature_algorithm =
structured_pkix::algorithm::read(&inside, &next, "signerInfo.signatureAlgorithm")
.map_err(Error::pkix)?;
let signature_element = required(&mut inside, "signerInfo.signature")?;
let signature = Octets::read(&inside, &signature_element, "signerInfo.signature")?;
let unsigned_attributes = match inside
.next_element()
.map_err(|error| Error::encoding(error, "signerInfo.unsignedAttrs"))?
{
Some(attributes) => Some(Attributes::read(&inside, &attributes, Placement::Unsigned)?),
None => None,
};
if !inside.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
FIELD,
inside.offset(),
));
}
Ok(Self {
version,
sid,
digest_algorithm,
signed_attributes,
signature_algorithm,
signature,
unsigned_attributes,
encoding: inside.encoding(),
element: *element,
faults: faults.into_owned(),
})
}
#[must_use]
pub fn version(&self) -> u64 {
self.version
}
#[must_use]
pub fn sid(&self) -> &SignerIdentifier<'a> {
&self.sid
}
#[must_use]
pub fn digest_algorithm(&self) -> AlgorithmIdentifier<'a> {
self.digest_algorithm
}
#[must_use]
pub fn signed_attributes(&self) -> Option<&Attributes<'a>> {
self.signed_attributes.as_ref()
}
#[must_use]
pub fn signature_algorithm(&self) -> AlgorithmIdentifier<'a> {
self.signature_algorithm
}
#[must_use]
pub fn signature(&self) -> &Octets<'a> {
&self.signature
}
#[must_use]
pub fn unsigned_attributes(&self) -> Option<&Attributes<'a>> {
self.unsigned_attributes.as_ref()
}
pub fn countersignatures_in<'s>(
&'s self,
parent: &'s Reader<'a>,
) -> crate::counter::Countersignatures<'s, 'a> {
crate::counter::Countersignatures::new(self, parent)
}
#[must_use]
pub fn encoding(&self) -> Encoding {
self.encoding
}
#[must_use]
pub fn span(&self) -> Span {
self.element.span()
}
#[must_use]
pub fn as_bytes(&self) -> &'a [u8] {
self.element.as_bytes()
}
#[must_use]
pub fn faults(&self) -> &[Error] {
&self.faults
}
pub fn same_value(&self, other: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<bool> {
if !self.same_fields(other, parent)? {
return Ok(false);
}
let comparison_error = |source| duplicate_comparison_error(source, other.span().start);
if !self
.digest_algorithm
.same_written_in(&other.digest_algorithm, parent)
.map_err(comparison_error)?
&& !self
.digest_algorithm
.digest_matches_in(&other.digest_algorithm, parent)
.map_err(comparison_error)?
{
return Ok(false);
}
self.signature_algorithm
.same_written_in(&other.signature_algorithm, parent)
.map_err(comparison_error)
}
pub(crate) fn same_fields(&self, other: &SignerInfo<'_>, parent: &Reader<'_>) -> Result<bool> {
let field = "signedData.signerInfos";
let at = other.span().start;
self.require_session(parent, field)?;
other.require_session(parent, field)?;
parent
.execution()
.charge_work(1)
.map_err(|reason| Error::resource(reason, field, at))?;
if self.version != other.version {
return Ok(false);
}
let same_sid = match (&self.sid, &other.sid) {
(
SignerIdentifier::IssuerAndSerial { issuer, serial },
SignerIdentifier::IssuerAndSerial {
issuer: right,
serial: number,
},
) => {
issuer
.matches_in(right, parent)
.map_err(|source| duplicate_comparison_error(source, at))?
&& same_bytes(parent, serial.as_bytes(), number.as_bytes(), field, at)?
}
(
SignerIdentifier::SubjectKeyIdentifier(left),
SignerIdentifier::SubjectKeyIdentifier(right),
) => left.same_value(right, parent, field, at)?,
_ => false,
};
if !same_sid {
return Ok(false);
}
for (left, right) in [
(&self.signed_attributes, &other.signed_attributes),
(&self.unsigned_attributes, &other.unsigned_attributes),
] {
if !same_optional(
parent,
left.as_ref().map(|attrs| attrs.element().as_bytes()),
right.as_ref().map(|attrs| attrs.element().as_bytes()),
field,
at,
)? {
return Ok(false);
}
}
self.signature
.same_value(&other.signature, parent, field, at)
}
}
fn duplicate_comparison_error(source: structured_pkix::Error, at: u64) -> Error {
if source.is_resource() {
Error::pkix(structured_pkix::Error::new(
source.code(),
"signedData.signerInfos",
at,
))
} else {
Error::pkix(source)
}
}
pub(crate) fn required<'a>(reader: &mut Reader<'a>, field: &'static str) -> Result<Element<'a>> {
reader
.next_element()
.map_err(|error| Error::encoding(error, field))?
.ok_or_else(|| Error::schema(ErrorCode::MissingField, field, reader.offset()))
}
fn same_bytes(
parent: &Reader<'_>,
left: &[u8],
right: &[u8],
field: &'static str,
at: u64,
) -> Result<bool> {
let execution = parent.execution();
execution
.charge_work(1)
.map_err(|reason| Error::resource(reason, field, at))?;
if left.len() != right.len() {
return Ok(false);
}
execution
.charge_work(left.len() as u64)
.map_err(|reason| Error::resource(reason, field, at))?;
Ok(left == right)
}
fn same_optional(
parent: &Reader<'_>,
left: Option<&[u8]>,
right: Option<&[u8]>,
field: &'static str,
at: u64,
) -> Result<bool> {
parent
.execution()
.charge_work(1)
.map_err(|reason| Error::resource(reason, field, at))?;
match (left, right) {
(None, None) => Ok(true),
(Some(left), Some(right)) => same_bytes(parent, left, right, field, at),
_ => Ok(false),
}
}