1use std::collections::BTreeMap;
4
5use crate::error::HapError;
6
7pub const TLV_METHOD: u8 = 0x00;
8pub const TLV_IDENTIFIER: u8 = 0x01;
9pub const TLV_SALT: u8 = 0x02;
10pub const TLV_PUBLIC_KEY: u8 = 0x03;
11pub const TLV_PROOF: u8 = 0x04;
12pub const TLV_ENCRYPTED_DATA: u8 = 0x05;
13pub const TLV_STATE: u8 = 0x06;
14pub const TLV_ERROR: u8 = 0x07;
15pub const TLV_SIGNATURE: u8 = 0x0a;
16pub const TLV_PERMISSIONS: u8 = 0x0b;
17pub const TLV_FLAGS: u8 = 0x13;
18pub const TLV_SEPARATOR: u8 = 0xff;
19
20pub const TLV_ERROR_UNKNOWN: u8 = 0x01;
21pub const TLV_ERROR_AUTHENTICATION: u8 = 0x02;
22pub const TLV_ERROR_MAX_PEERS: u8 = 0x04;
23pub const TLV_ERROR_MAX_TRIES: u8 = 0x05;
24pub const TLV_ERROR_UNAVAILABLE: u8 = 0x06;
25pub const TLV_ERROR_BUSY: u8 = 0x07;
26
27const MAX_TLV_BYTES: usize = 4096;
28const MAX_TLV_TYPES: usize = 32;
29
30#[derive(Debug, Clone, Default, PartialEq, Eq)]
32pub struct Tlv8 {
33 values: BTreeMap<u8, Vec<u8>>,
34}
35
36impl Tlv8 {
37 pub fn parse(input: &[u8]) -> Result<Self, HapError> {
38 if input.len() > MAX_TLV_BYTES {
39 return Err(HapError::Protocol("TLV8 body exceeds 4096 bytes".into()));
40 }
41 let mut values: BTreeMap<u8, Vec<u8>> = BTreeMap::new();
42 let mut offset = 0usize;
43 while offset < input.len() {
44 if input.len() - offset < 2 {
45 return Err(HapError::Protocol("truncated TLV8 header".into()));
46 }
47 let kind = input[offset];
48 let len = input[offset + 1] as usize;
49 offset += 2;
50 let end = offset
51 .checked_add(len)
52 .filter(|end| *end <= input.len())
53 .ok_or_else(|| HapError::Protocol("truncated TLV8 value".into()))?;
54 if !values.contains_key(&kind) && values.len() == MAX_TLV_TYPES {
55 return Err(HapError::Protocol("too many TLV8 types".into()));
56 }
57 values
58 .entry(kind)
59 .or_default()
60 .extend_from_slice(&input[offset..end]);
61 offset = end;
62 }
63 Ok(Self { values })
64 }
65
66 pub fn get(&self, kind: u8) -> Option<&[u8]> {
67 self.values.get(&kind).map(Vec::as_slice)
68 }
69
70 pub fn byte(&self, kind: u8) -> Option<u8> {
71 let value = self.get(kind)?;
72 (value.len() == 1).then_some(value[0])
73 }
74
75 pub fn insert(&mut self, kind: u8, value: impl Into<Vec<u8>>) {
76 self.values.insert(kind, value.into());
77 }
78
79 pub fn encode(&self) -> Vec<u8> {
80 encode_items(
81 self.values
82 .iter()
83 .map(|(&kind, value)| (kind, value.as_slice())),
84 )
85 }
86}
87
88pub fn encode_items<'a>(items: impl IntoIterator<Item = (u8, &'a [u8])>) -> Vec<u8> {
91 let mut encoded = Vec::new();
92 for (kind, value) in items {
93 if value.is_empty() {
94 encoded.extend_from_slice(&[kind, 0]);
95 continue;
96 }
97 for chunk in value.chunks(u8::MAX as usize) {
98 encoded.push(kind);
99 encoded.push(chunk.len() as u8);
100 encoded.extend_from_slice(chunk);
101 }
102 }
103 encoded
104}
105
106pub fn error_response(state: u8, error: u8) -> Vec<u8> {
107 encode_items([
108 (TLV_STATE, [state].as_slice()),
109 (TLV_ERROR, [error].as_slice()),
110 ])
111}
112
113#[cfg(test)]
114mod tests {
115 use super::*;
116
117 #[test]
118 fn tlv8_roundtrip_supports_fragmented_values() {
119 let mut tlv = Tlv8::default();
120 tlv.insert(TLV_PUBLIC_KEY, vec![3; 300]);
121 tlv.insert(TLV_STATE, vec![1]);
122 let encoded = tlv.encode();
123 let decoded = Tlv8::parse(&encoded).unwrap();
124 assert_eq!(decoded, tlv);
125 }
126
127 #[test]
128 fn malformed_or_oversized_tlv_is_rejected() {
129 assert!(Tlv8::parse(&[TLV_STATE]).is_err());
130 assert!(Tlv8::parse(&[TLV_STATE, 2, 1]).is_err());
131 assert!(Tlv8::parse(&vec![0; MAX_TLV_BYTES + 1]).is_err());
132 }
133
134 #[test]
135 fn error_response_is_not_success_shaped() {
136 let response = error_response(2, TLV_ERROR_UNAVAILABLE);
137 let decoded = Tlv8::parse(&response).unwrap();
138 assert_eq!(decoded.byte(TLV_STATE), Some(2));
139 assert_eq!(decoded.byte(TLV_ERROR), Some(TLV_ERROR_UNAVAILABLE));
140 }
141}