use crate::error::{Error, Result};
use broadcast_common::{Parse, Serialize};
pub const IV_DESCRIPTOR_TAG: u8 = 0xD0;
pub const KEY_IDENTIFIER_DESCRIPTOR_TAG: u8 = 0xD1;
const DESC_HEADER: usize = 2;
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CiplusInitializationVectorDescriptor<'a> {
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub iv_data: &'a [u8],
}
impl<'a> Parse<'a> for CiplusInitializationVectorDescriptor<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = parse_tlv(
bytes,
IV_DESCRIPTOR_TAG,
"ciplus_initialization_vector_descriptor",
)?;
Ok(Self { iv_data: body })
}
}
impl Serialize for CiplusInitializationVectorDescriptor<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
DESC_HEADER + self.iv_data.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
write_tlv(IV_DESCRIPTOR_TAG, self.iv_data, buf)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize))]
pub struct CiplusKeyIdentifierDescriptor<'a> {
#[cfg_attr(feature = "serde", serde(borrow, with = "crate::objects::bytes_serde"))]
pub key_id_data: &'a [u8],
}
impl<'a> Parse<'a> for CiplusKeyIdentifierDescriptor<'a> {
type Error = Error;
fn parse(bytes: &'a [u8]) -> Result<Self> {
let body = parse_tlv(
bytes,
KEY_IDENTIFIER_DESCRIPTOR_TAG,
"ciplus_key_identifier_descriptor",
)?;
Ok(Self { key_id_data: body })
}
}
impl Serialize for CiplusKeyIdentifierDescriptor<'_> {
type Error = Error;
fn serialized_len(&self) -> usize {
DESC_HEADER + self.key_id_data.len()
}
fn serialize_into(&self, buf: &mut [u8]) -> Result<usize> {
write_tlv(KEY_IDENTIFIER_DESCRIPTOR_TAG, self.key_id_data, buf)
}
}
fn parse_tlv<'a>(bytes: &'a [u8], expected_tag: u8, what: &'static str) -> Result<&'a [u8]> {
if bytes.len() < DESC_HEADER {
return Err(Error::BufferTooShort {
need: DESC_HEADER,
have: bytes.len(),
what,
});
}
if bytes[0] != expected_tag {
return Err(Error::InvalidObject {
what,
reason: "descriptor_tag mismatch",
});
}
let len = bytes[1] as usize;
let end = DESC_HEADER + len;
if bytes.len() < end {
return Err(Error::LengthMismatch {
what,
declared: len,
actual: bytes.len().saturating_sub(DESC_HEADER),
});
}
Ok(&bytes[DESC_HEADER..end])
}
fn write_tlv(tag: u8, body: &[u8], buf: &mut [u8]) -> Result<usize> {
if body.len() > u8::MAX as usize {
return Err(Error::LengthTooLarge(body.len()));
}
let total = DESC_HEADER + body.len();
if buf.len() < total {
return Err(Error::OutputBufferTooSmall {
need: total,
have: buf.len(),
});
}
buf[0] = tag;
buf[1] = body.len() as u8;
buf[DESC_HEADER..total].copy_from_slice(body);
Ok(total)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn iv_descriptor_round_trips_and_bites() {
let d = CiplusInitializationVectorDescriptor {
iv_data: &[0x00, 0x11, 0x22, 0x33],
};
let bytes = d.to_bytes();
assert_eq!(bytes, [0xD0, 0x04, 0x00, 0x11, 0x22, 0x33]);
assert_eq!(
CiplusInitializationVectorDescriptor::parse(&bytes).unwrap(),
d
);
let other = CiplusInitializationVectorDescriptor {
iv_data: &[0x00, 0x11, 0x22, 0x34],
};
assert_ne!(bytes, other.to_bytes());
}
#[test]
fn iv_descriptor_zero_byte_body() {
let d = CiplusInitializationVectorDescriptor { iv_data: &[] };
let bytes = d.to_bytes();
assert_eq!(bytes, [0xD0, 0x00]);
let parsed = CiplusInitializationVectorDescriptor::parse(&bytes).unwrap();
assert_eq!(parsed, d);
assert!(parsed.iv_data.is_empty());
}
#[test]
fn key_id_descriptor_round_trips() {
let d = CiplusKeyIdentifierDescriptor {
key_id_data: &[0xDE, 0xAD],
};
let bytes = d.to_bytes();
assert_eq!(bytes, [0xD1, 0x02, 0xDE, 0xAD]);
assert_eq!(CiplusKeyIdentifierDescriptor::parse(&bytes).unwrap(), d);
}
#[test]
fn key_id_descriptor_zero_byte_body() {
let d = CiplusKeyIdentifierDescriptor { key_id_data: &[] };
let bytes = d.to_bytes();
assert_eq!(bytes, [0xD1, 0x00]);
assert_eq!(CiplusKeyIdentifierDescriptor::parse(&bytes).unwrap(), d);
}
#[test]
fn wrong_tag_rejected() {
let bytes = [0xD1, 0x00];
assert!(matches!(
CiplusInitializationVectorDescriptor::parse(&bytes),
Err(Error::InvalidObject { .. })
));
}
}