use crate::signed::{CertificateChoice, catalogue, read_certificates, read_revocations};
use crate::{
ContentInfo, Error, ErrorCode, Octets, Result, attribute::Attribute, signer::required,
};
use structured_asn1::memory::{OwnedVec, Work};
use structured_asn1::{Element, Encoding, Oid, Parser, Reader, Tag, universal};
use structured_pkix::{AlgorithmIdentifier, Certificate};
mod recipient;
pub use recipient::{AgreementIdentifier, KeyIdentifier, Originator, Recipient, RecipientKey};
pub const IDENTIFIER: Oid<'static> = Oid::from_bytes(&[42, 134, 72, 134, 247, 13, 1, 7, 3]);
const FIELD: &str = "envelopedData";
#[derive(Debug, Clone, Copy)]
pub struct EncodedValue<'a> {
element: Element<'a>,
parser: &'a Parser,
encoding: Encoding,
depth: u32,
}
impl<'a> EncodedValue<'a> {
pub(crate) fn new(parent: &Reader<'a>, element: Element<'a>) -> Self {
Self {
element,
parser: parent.parser(),
encoding: parent.encoding(),
depth: parent.depth(),
}
}
#[must_use]
pub fn element(&self) -> &Element<'a> {
&self.element
}
pub fn reader_in<'op>(&self, query: &Reader<'op>) -> Result<Reader<'op>>
where
'a: 'op,
{
self.parser
.read_query_in(
query,
self.element.as_bytes(),
self.element.span().start,
self.encoding,
self.depth,
)
.map_err(|e| Error::encoding(e, FIELD))
}
}
#[derive(Debug)]
pub struct OriginatorInfo<'a> {
parser: &'a Parser,
encoding: Encoding,
outer: u32,
at: u64,
choices: OwnedVec<CertificateChoice<'a>>,
certificates: OwnedVec<Certificate<'a>>,
revocations: OwnedVec<Element<'a>>,
}
impl<'a> OriginatorInfo<'a> {
#[must_use]
pub fn choices(&self) -> &[CertificateChoice<'a>] {
&self.choices
}
#[must_use]
pub fn certificates(&self) -> &[Certificate<'a>] {
&self.certificates
}
#[must_use]
pub fn revocations(&self) -> &[Element<'a>] {
&self.revocations
}
pub fn certificate_reader_in<'op>(
&self,
query: &Reader<'op>,
ordinal: usize,
) -> Result<Option<Reader<'op>>>
where
'a: 'op,
{
self.admit(query)?;
let Some(
CertificateChoice::Extended(entry)
| CertificateChoice::AttributeV1(entry)
| CertificateChoice::AttributeV2(entry)
| CertificateChoice::Other(entry),
) = self.choices.get(ordinal)
else {
return Ok(None);
};
self.reopen(query, entry).map(Some)
}
pub fn revocation_reader_in<'op>(
&self,
query: &Reader<'op>,
ordinal: usize,
) -> Result<Option<Reader<'op>>>
where
'a: 'op,
{
self.admit(query)?;
self.revocations
.get(ordinal)
.map(|entry| self.reopen(query, entry))
.transpose()
}
fn admit(&self, query: &Reader<'_>) -> Result<()> {
query
.execution()
.execution_for(self.parser)
.charge_work(1)
.map_err(|e| Error::resource(e, "envelopedData.originatorInfo", self.at))
}
fn reopen<'op>(&self, query: &Reader<'op>, entry: &Element<'a>) -> Result<Reader<'op>>
where
'a: 'op,
{
self.parser
.read_nested_query_in(
query,
entry.as_bytes(),
entry.span().start,
self.encoding,
self.outer,
)
.map_err(|e| Error::encoding(e, "envelopedData.originatorInfo"))
}
}
#[derive(Debug)]
pub struct EncryptedContent<'a> {
pub kind: Oid<'a>,
pub algorithm: AlgorithmIdentifier<'a>,
pub ciphertext: Option<Octets<'a>>,
}
#[derive(Debug)]
pub struct EnvelopedData<'a> {
parser: &'a Parser,
element: Element<'a>,
depth: u32,
encoding: Encoding,
pub version: u64,
pub originator: Option<OriginatorInfo<'a>>,
pub recipients: OwnedVec<Recipient<'a>>,
pub content: EncryptedContent<'a>,
pub unprotected: Option<OwnedVec<Attribute<'a>>>,
}
impl<'a> EnvelopedData<'a> {
pub fn read(parent: &mut Reader<'a>) -> Result<Self> {
let (wrapper, reader) = ContentInfo::read(parent)?;
Self::from_content_info(&wrapper, &reader)
}
pub fn read_content_info(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
let (wrapper, reader) = ContentInfo::read_element(parent, element)?;
Self::from_content_info(&wrapper, &reader)
}
fn from_content_info(wrapper: &ContentInfo<'a>, parent: &Reader<'a>) -> Result<Self> {
oid_is(
wrapper.kind(),
IDENTIFIER,
parent,
"contentInfo.contentType",
wrapper.span().start,
)?;
Self::read_element(parent, wrapper.content())
}
pub fn read_element(parent: &Reader<'a>, element: &Element<'a>) -> Result<Self> {
tag(element, Tag::constructed(universal::SEQUENCE), FIELD)?;
let mut inside = enter(parent, element, FIELD)?;
let (version, at) = number(&mut inside, "envelopedData.version")?;
let mut next = required(&mut inside, FIELD)?;
let mut material_version = 0;
let originator = if next.tag() == Tag::context(true, 0) {
let original = originator_info(&inside, &next, &mut material_version)?;
next = required(&mut inside, FIELD)?;
Some(original)
} else {
None
};
let mut all_zero = true;
let mut advanced = false;
let recipients = catalogue(
&inside,
&next,
Tag::constructed(universal::SET),
"envelopedData.recipientInfos",
|reader, entry| {
let recipient = Recipient::read(reader, entry)?;
all_zero &= recipient.is_transport_zero();
advanced |= matches!(
recipient,
Recipient::Password { .. } | Recipient::Other { .. }
);
Ok(recipient)
},
)?;
if recipients.is_empty() {
return Err(Error::schema(
ErrorCode::MissingField,
"envelopedData.recipientInfos",
next.span().start,
));
}
let value = required(&mut inside, "envelopedData.encryptedContentInfo")?;
let content = encrypted_content(&inside, &value)?;
let unprotected = inside
.next_element()
.map_err(|e| Error::encoding(e, FIELD))?
.map(|set| {
let values = catalogue(
&inside,
&set,
Tag::context(true, 1),
"envelopedData.unprotectedAttrs",
|r, e| Attribute::read(r, e, "envelopedData.unprotectedAttrs"),
)?;
if values.is_empty() {
return Err(Error::schema(
ErrorCode::EmptyAttributes,
"envelopedData.unprotectedAttrs",
set.span().start,
));
}
Ok(values)
})
.transpose()?;
end(&inside, FIELD)?;
let expected = if material_version == 5 {
4
} else if material_version >= 4 || advanced {
3
} else if originator.is_none() && unprotected.is_none() && all_zero {
0
} else {
2
};
if version != expected {
return Err(Error::schema(
ErrorCode::VersionMismatch,
"envelopedData.version",
at,
));
}
Ok(Self {
parser: parent.parser(),
element: *element,
depth: parent.depth(),
encoding: parent.encoding(),
version,
originator,
recipients,
content,
unprotected,
})
}
#[must_use]
pub fn element(&self) -> &Element<'a> {
&self.element
}
pub fn execution_in<'op, W: Work>(
&self,
current: &'op W,
) -> structured_asn1::memory::Execution<'op>
where
'a: 'op,
{
current.execution_for(self.parser)
}
pub fn reader_in<'op>(&self, current: &Reader<'op>) -> Result<Reader<'op>>
where
'a: 'op,
{
let outer = self.element.span();
self.parser
.read_query_in(
current,
self.element.as_bytes(),
outer.start,
self.encoding,
self.depth,
)
.map_err(|e| Error::encoding(e, FIELD))
}
}
fn originator_info<'a>(
parent: &Reader<'a>,
element: &Element<'a>,
version: &mut u64,
) -> Result<OriginatorInfo<'a>> {
let field = "envelopedData.originatorInfo";
let mut inside = enter(parent, element, field)?;
let work = parent.execution();
let mut choices = work.vector().into_owned();
let mut certificates = work.vector().into_owned();
let mut revocations = work.vector().into_owned();
let mut next = inside
.next_element()
.map_err(|e| Error::encoding(e, field))?;
if let Some(set) = next.as_ref().filter(|e| e.tag() == Tag::context(true, 0)) {
(choices, certificates) =
read_certificates(&inside, set, version, "envelopedData.originatorInfo.certs")?;
next = inside
.next_element()
.map_err(|e| Error::encoding(e, field))?;
}
if let Some(set) = next.as_ref().filter(|e| e.tag() == Tag::context(true, 1)) {
revocations = read_revocations(&inside, set, version, "envelopedData.originatorInfo.crls")?;
next = inside
.next_element()
.map_err(|e| Error::encoding(e, field))?;
}
if let Some(extra) = next {
return Err(Error::schema(
ErrorCode::UnknownField,
field,
extra.span().start,
));
}
Ok(OriginatorInfo {
parser: parent.parser(),
encoding: parent.encoding(),
outer: inside.depth(),
at: element.span().start,
choices,
certificates,
revocations,
})
}
pub(crate) fn encrypted_content<'a>(
parent: &Reader<'a>,
element: &Element<'a>,
) -> Result<EncryptedContent<'a>> {
let field = "envelopedData.encryptedContentInfo";
tag(element, Tag::constructed(universal::SEQUENCE), field)?;
let mut inside = enter(parent, element, field)?;
let kind = inside.read_oid().map_err(|e| Error::encoding(e, field))?;
let value = required(&mut inside, field)?;
let algorithm = algorithm(&inside, &value, field)?;
let ciphertext = inside
.next_element()
.map_err(|e| Error::encoding(e, field))?
.map(|value| Octets::read_identifier(&inside, &value, field))
.transpose()?;
end(&inside, field)?;
Ok(EncryptedContent {
kind,
algorithm,
ciphertext,
})
}
fn enter<'a>(
parent: &Reader<'a>,
element: &Element<'a>,
field: &'static str,
) -> Result<Reader<'a>> {
parent.enter(element).map_err(|e| Error::encoding(e, field))
}
fn end(parent: &Reader<'_>, field: &'static str) -> Result<()> {
if !parent.is_empty() {
return Err(Error::schema(
ErrorCode::TrailingData,
field,
parent.offset(),
));
}
Ok(())
}
fn tag(element: &Element<'_>, expected: Tag, field: &'static str) -> Result<()> {
if element.tag() != expected {
return Err(Error::schema(
ErrorCode::UnexpectedTag,
field,
element.span().start,
));
}
Ok(())
}
fn number(parent: &mut Reader<'_>, field: &'static str) -> Result<(u64, u64)> {
let element = parent
.expect(Tag::primitive(universal::INTEGER))
.map_err(|e| Error::encoding(e, field))?;
let value = parent
.integer(&element)
.map_err(|e| Error::encoding(e, field))?
.as_u64()
.ok_or_else(|| Error::schema(ErrorCode::ValueTooLarge, field, element.span().start))?;
Ok((value, element.span().start))
}
fn version(parent: &mut Reader<'_>, expected: u64, field: &'static str) -> Result<()> {
let (value, at) = number(parent, field)?;
if value != expected {
return Err(Error::schema(ErrorCode::UnsupportedVersion, field, at));
}
Ok(())
}
fn algorithm<'a>(
parent: &Reader<'a>,
element: &Element<'a>,
field: &'static str,
) -> Result<AlgorithmIdentifier<'a>> {
structured_pkix::algorithm::read(parent, element, field).map_err(Error::pkix)
}
pub(crate) fn oid_is(
value: Oid<'_>,
expected: Oid<'_>,
parent: &Reader<'_>,
field: &'static str,
at: u64,
) -> Result<()> {
parent.charge(1).map_err(|e| Error::encoding(e, field))?;
if value.as_bytes().len() == expected.as_bytes().len() {
parent
.charge(value.as_bytes().len() as u64)
.map_err(|e| Error::encoding(e, field))?;
if value == expected {
return Ok(());
}
}
Err(Error::schema(ErrorCode::UnexpectedContentType, field, at))
}