#[repr(u8)]
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum TlvType {
Method = 0,
Identifier = 1,
Salt = 2,
PublicKey = 3,
Proof = 4,
EncryptedData = 5,
State = 6,
Error = 7,
RetryDelay = 8,
Certificate = 9,
Signature = 10,
Permissions = 11,
FragmentData = 13,
FragmentLast = 14,
Flags = 19,
Separator = 0xff,
}
#[derive(Debug, Clone, Default)]
pub struct TlvValues {
entries: Vec<(u8, Vec<u8>)>,
}
impl TlvValues {
pub fn new() -> Self {
Self::default()
}
pub fn add(&mut self, tag: u8, value: &[u8]) {
self.entries.push((tag, value.to_vec()));
}
pub fn add_type(&mut self, tag: TlvType, value: &[u8]) {
self.add(tag as u8, value);
}
pub fn get(&self, tag: u8) -> Option<&[u8]> {
self.entries.iter().find(|(t, _)| *t == tag).map(|(_, v)| v.as_slice())
}
pub fn get_type(&self, tag: TlvType) -> Option<&[u8]> {
self.get(tag as u8)
}
pub fn encode(&self) -> Vec<u8> {
let mut buf = Vec::new();
for (tag, value) in &self.entries {
if value.is_empty() {
buf.push(*tag);
buf.push(0);
} else {
for chunk in value.chunks(255) {
buf.push(*tag);
buf.push(chunk.len() as u8);
buf.extend_from_slice(chunk);
}
}
}
buf
}
pub fn decode(data: &[u8]) -> Result<Self, TlvError> {
let mut values = Self::new();
let mut i = 0;
while i < data.len() {
if i + 1 >= data.len() {
return Err(TlvError::Truncated);
}
let tag = data[i];
let len = data[i + 1] as usize;
i += 2;
if i + len > data.len() {
return Err(TlvError::Truncated);
}
let chunk = &data[i..i + len];
i += len;
if let Some((last_tag, last_val)) = values.entries.last_mut() {
if *last_tag == tag && last_val.len() % 255 == 0 && !last_val.is_empty() {
last_val.extend_from_slice(chunk);
continue;
}
}
values.entries.push((tag, chunk.to_vec()));
}
Ok(values)
}
}
#[derive(Debug, thiserror::Error)]
pub enum TlvError {
#[error("TLV data truncated")]
Truncated,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn roundtrip_simple() {
let mut tlv = TlvValues::new();
tlv.add(6, &[1]); tlv.add(0, &[0]); let encoded = tlv.encode();
assert_eq!(encoded, &[6, 1, 1, 0, 1, 0]);
let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(6), Some(&[1u8][..]));
assert_eq!(decoded.get(0), Some(&[0u8][..]));
}
#[test]
fn roundtrip_chunked() {
let mut tlv = TlvValues::new();
let big = vec![0xAB; 300];
tlv.add(3, &big);
let encoded = tlv.encode();
assert_eq!(encoded.len(), 2 + 255 + 2 + 45);
let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(3).unwrap().len(), 300);
assert_eq!(decoded.get(3).unwrap(), &big[..]);
}
#[test]
fn empty_value() {
let mut tlv = TlvValues::new();
tlv.add(0xff, &[]);
let encoded = tlv.encode();
assert_eq!(encoded, &[0xff, 0]);
let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(0xff), Some(&[][..]));
}
#[test]
fn truncated_input() {
assert!(TlvValues::decode(&[6]).is_err());
assert!(TlvValues::decode(&[6, 5, 1, 2]).is_err());
}
#[test]
fn c_vector_simple_state_method() {
let mut tlv = TlvValues::new();
tlv.add(6, &[1]);
tlv.add(0, &[0]);
let encoded = tlv.encode();
assert_eq!(hex_encode(&encoded), "060101000100");
let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(6), Some(&[1u8][..]));
assert_eq!(decoded.get(0), Some(&[0u8][..]));
}
#[test]
fn c_vector_chunked_300_bytes() {
let mut tlv = TlvValues::new();
let pk: Vec<u8> = (0u16..300).map(|i| (i & 0xff) as u8).collect();
tlv.add(3, &pk);
let encoded = tlv.encode();
assert_eq!(encoded.len(), 304); assert_eq!(encoded[0], 3);
assert_eq!(encoded[1], 255);
assert_eq!(encoded[257], 3);
assert_eq!(encoded[258], 45);
let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(3).unwrap(), &pk[..]);
}
#[test]
fn c_vector_multi_field_m2() {
let mut tlv = TlvValues::new();
tlv.add(6, &[2]);
let salt: Vec<u8> = (0x10u8..0x20).collect();
tlv.add(2, &salt);
let spk: Vec<u8> = (0u16..384).map(|i| ((i * 7) & 0xff) as u8).collect();
tlv.add(3, &spk);
let encoded = tlv.encode();
assert_eq!(encoded.len(), 409); let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(6), Some(&[2u8][..]));
assert_eq!(decoded.get(2).unwrap(), &salt[..]);
assert_eq!(decoded.get(3).unwrap(), &spk[..]);
}
#[test]
fn empty_separator_c_bug_fixed() {
let mut tlv = TlvValues::new();
tlv.add(0xff, &[]);
let encoded = tlv.encode();
assert_eq!(encoded, &[0xff, 0]); let decoded = TlvValues::decode(&encoded).unwrap();
assert_eq!(decoded.get(0xff), Some(&[][..]));
}
fn hex_encode(data: &[u8]) -> String {
data.iter().map(|b| format!("{:02x}", b)).collect()
}
}