use alloc::vec::Vec;
use crate::error::{Error, Result};
pub const TAG_INTEGER: u8 = 0x02;
pub const TAG_BIT_STRING: u8 = 0x03;
pub const TAG_OCTET_STRING: u8 = 0x04;
pub const TAG_OID: u8 = 0x06;
pub const TAG_UTC_TIME: u8 = 0x17;
pub const TAG_GENERALIZED_TIME: u8 = 0x18;
pub const TAG_SEQUENCE: u8 = 0x30;
pub const TAG_SET: u8 = 0x31;
pub const TAG_CONTEXT_0: u8 = 0xA0;
pub const TAG_CONTEXT_1: u8 = 0xA1;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct Tlv<'a> {
pub tag: u8,
pub value: &'a [u8],
pub raw: &'a [u8],
}
impl<'a> Tlv<'a> {
pub fn parse(data: &'a [u8]) -> Result<Self> {
read_tlv(data).map(|(tlv, _)| tlv)
}
pub fn parse_tagged(data: &'a [u8], tag: u8) -> Result<Self> {
Self::parse(data)?.expect_tag(tag)
}
pub fn expect_tag(self, tag: u8) -> Result<Self> {
if self.tag == tag {
Ok(self)
} else {
Err(malformed("unexpected tag"))
}
}
pub fn children(&self) -> Result<Vec<Tlv<'a>>> {
let mut children = Vec::new();
let mut rest = self.value;
while !rest.is_empty() {
let (child, remaining) = read_tlv(rest)?;
children.push(child);
rest = remaining;
}
Ok(children)
}
pub fn child(&self, index: usize, tag: u8) -> Result<Tlv<'a>> {
self.children()?
.get(index)
.copied()
.ok_or_else(|| malformed("missing element"))?
.expect_tag(tag)
}
}
pub fn read_tlv(data: &[u8]) -> Result<(Tlv<'_>, &[u8])> {
let (&tag, rest) = data.split_first().ok_or_else(|| malformed("empty input"))?;
if tag & 0x1F == 0x1F {
return Err(malformed("multi byte tags are not supported"));
}
let (&first, rest) = rest
.split_first()
.ok_or_else(|| malformed("missing length"))?;
let (length, rest) = if first & 0x80 == 0 {
(first as usize, rest)
} else {
let count = (first & 0x7F) as usize;
if count == 0 || count > 8 || rest.len() < count {
return Err(malformed("unsupported length encoding"));
}
let length = rest[..count]
.iter()
.fold(0u64, |acc, &b| (acc << 8) | b as u64);
(
usize::try_from(length).map_err(|_| malformed("length overflow"))?,
&rest[count..],
)
};
if rest.len() < length {
return Err(malformed("element exceeds the input"));
}
let header_length = data.len() - rest.len();
let tlv = Tlv {
tag,
value: &rest[..length],
raw: &data[..header_length + length],
};
Ok((tlv, &rest[length..]))
}
fn malformed(reason: &'static str) -> Error {
crate::error::malformed("ASN.1 element", reason)
}