use super::{ComprInfo, PayloadPropsRef};
use crate::{util, Tag};
use core::ops::Add;
use der::{
asn1::{Ia5StringRef, OctetStringRef},
Decode, DecodeValue, Encode, EncodeValue, ErrorKind, FixedTag, Header, Length, Reader,
TagNumber, Writer,
};
#[cfg(any(feature = "alloc", test))]
use {
super::Payload,
der::referenced::{OwnedToRef, RefToOwned},
};
const MAGIC_DER: &[u8] = &[0x16, 0x04, 0x49, 0x4D, 0x34, 0x50];
const MAGIC_AND_TAG_DER_LEN: u16 = 12;
fn decode_4cc_tag<'a, R: Reader<'a>>(decoder: &mut R) -> der::Result<Tag> {
let position = decoder.position();
let tag_4cc: Ia5StringRef<'_> = decoder.decode()?;
Tag::try_from(tag_4cc.as_bytes()).map_err(|_| {
ErrorKind::Value {
tag: der::Tag::Ia5String,
}
.at(position)
})
}
#[derive(Clone, Debug)]
#[cfg_attr(test, derive(Eq, PartialEq))]
pub struct PayloadRef<'a> {
pub(super) tag_4cc: Tag,
pub(super) builder_string: Ia5StringRef<'a>,
pub(super) data: OctetStringRef<'a>,
pub(super) keybags: Option<OctetStringRef<'a>>,
pub(super) compr_info: Option<ComprInfo>,
pub(super) props: Option<PayloadPropsRef<'a>>,
}
impl<'a> PayloadRef<'a> {
pub fn new(tag: Tag, builder_string: &'a str, data: &'a [u8]) -> der::Result<Self> {
Ok(Self {
tag_4cc: tag,
builder_string: Ia5StringRef::new(builder_string)?,
data: OctetStringRef::new(data)?,
keybags: None,
compr_info: None,
props: None,
})
}
pub fn parse(bytes: &'a [u8]) -> der::Result<Self> {
Self::from_der(bytes)
}
pub fn tag(&self) -> Tag {
self.tag_4cc
}
pub fn set_tag(&mut self, tag: Tag) {
self.tag_4cc = tag;
}
pub fn builder_string(&self) -> &str {
self.builder_string.as_str()
}
pub fn set_builder_string(&mut self, s: &'a str) -> der::Result<()> {
self.builder_string = Ia5StringRef::new(s)?;
Ok(())
}
pub fn data(&self) -> &[u8] {
self.data.as_bytes()
}
pub fn set_data(&mut self, data: &'a [u8]) -> der::Result<()> {
self.data = OctetStringRef::new(data)?;
Ok(())
}
pub fn keybags(&self) -> Option<&'a [u8]> {
self.keybags.as_ref().map(OctetStringRef::as_bytes)
}
pub fn set_keybags(&mut self, bytes: Option<&'a [u8]>) -> der::Result<()> {
self.keybags = bytes.map(OctetStringRef::new).transpose()?;
Ok(())
}
pub fn compr_info(&self) -> Option<&ComprInfo> {
self.compr_info.as_ref()
}
pub fn set_compr_info(&mut self, compr_info: Option<ComprInfo>) {
self.compr_info = compr_info;
}
pub fn props(&self) -> Option<&PayloadPropsRef<'a>> {
self.props.as_ref()
}
pub fn set_props(&mut self, props: Option<PayloadPropsRef<'a>>) {
self.props = props;
}
pub fn decode_after_magic<R: Reader<'a>>(decoder: &mut R) -> der::Result<Self> {
Ok(PayloadRef {
tag_4cc: decode_4cc_tag(decoder)?,
builder_string: decoder.decode()?,
data: decoder.decode()?,
keybags: decoder.decode()?,
compr_info: decoder.decode()?,
props: util::decode_opt_context_specific_field(decoder, TagNumber::N0, true)?,
})
}
}
#[cfg(any(feature = "alloc", test))]
impl<'a> From<&'a Payload> for PayloadRef<'a> {
fn from(value: &'a Payload) -> Self {
Self {
tag_4cc: value.tag_4cc,
builder_string: value.builder_string.owned_to_ref(),
data: value.data.owned_to_ref(),
keybags: value.keybags.owned_to_ref(),
compr_info: value.compr_info.clone(),
props: value.props.owned_to_ref(),
}
}
}
#[cfg(any(feature = "alloc", test))]
impl<'a> RefToOwned<'a> for PayloadRef<'a> {
type Owned = Payload;
fn ref_to_owned(&self) -> Self::Owned {
self.into()
}
}
impl<'a> DecodeValue<'a> for PayloadRef<'a> {
fn decode_value<R: Reader<'a>>(decoder: &mut R, header: Header) -> der::Result<Self> {
decoder.read_nested(header.length, |decoder| {
util::decode_and_check_magic(decoder, b"IM4P")?;
PayloadRef::decode_after_magic(decoder)
})
}
}
impl EncodeValue for PayloadRef<'_> {
fn value_len(&self) -> der::Result<Length> {
Length::new(MAGIC_AND_TAG_DER_LEN)
.add(self.builder_string.encoded_len()?)?
.add(self.data.encoded_len()?)?
.add(self.keybags.encoded_len()?)?
.add(self.compr_info.encoded_len()?)?
.add(util::encoded_opt_context_specific_field_len(
self.props.as_ref(),
TagNumber::N0,
true,
)?)
}
fn encode_value(&self, encoder: &mut impl Writer) -> der::Result<()> {
encoder.write(MAGIC_DER)?;
let tag_str_bytes = self.tag_4cc.to_bytes();
let tag_str = Ia5StringRef::new(&tag_str_bytes)?;
tag_str.encode(encoder)?;
self.builder_string.encode(encoder)?;
self.data.encode(encoder)?;
self.keybags.encode(encoder)?;
self.compr_info.encode(encoder)?;
util::encode_opt_context_specific_field(encoder, self.props.as_ref(), TagNumber::N0, true)?;
Ok(())
}
}
impl FixedTag for PayloadRef<'_> {
const TAG: der::Tag = der::Tag::Sequence;
}