1use heapless::Vec;
23
24use crate::limits::MAX_TLVS;
25
26#[derive(Debug, Clone, Copy, PartialEq, Eq)]
29pub struct Tlv<'a> {
30 pub tag: u32,
31 pub value: &'a [u8],
32}
33
34const TAG_MULTIBYTE: u8 = 0x1F;
37const TAG_MORE: u8 = 0x80;
39const TAG_MAX_OCTETS: usize = 4;
41const LEN_LONG: u8 = 0x80;
43
44pub fn parse(input: &[u8]) -> Result<Vec<Tlv<'_>, MAX_TLVS>, TlvError> {
53 let mut out = Vec::new();
54 let mut pos = 0usize;
55 while pos < input.len() {
56 let tag = parse_tag(input, &mut pos)?;
57 let len = parse_len(input, &mut pos)?;
58 let end = pos.checked_add(len).ok_or(TlvError::Truncated)?;
59 if end > input.len() {
60 return Err(TlvError::Truncated);
61 }
62 out.push(Tlv {
63 tag,
64 value: &input[pos..end],
65 })
66 .map_err(|_| TlvError::TooMany)?;
67 pos = end;
68 }
69 Ok(out)
70}
71
72pub fn encode(items: &[Tlv<'_>], out: &mut [u8]) -> Result<usize, TlvError> {
80 let mut pos = 0usize;
81 for item in items {
82 pos = write_tag(item.tag, out, pos)?;
83 pos = write_len(item.value.len(), out, pos)?;
84 pos = write_bytes(out, pos, item.value)?;
85 }
86 Ok(pos)
87}
88
89fn parse_tag(input: &[u8], pos: &mut usize) -> Result<u32, TlvError> {
91 let b0 = *input.get(*pos).ok_or(TlvError::Truncated)?;
92 *pos += 1;
93 let mut tag = u32::from(b0);
94 if b0 & TAG_MULTIBYTE == TAG_MULTIBYTE {
95 let mut octets = 1usize;
96 loop {
97 let b = *input.get(*pos).ok_or(TlvError::Truncated)?;
98 *pos += 1;
99 octets += 1;
100 if octets > TAG_MAX_OCTETS {
101 return Err(TlvError::BadLength);
102 }
103 tag = (tag << 8) | u32::from(b);
104 if b & TAG_MORE == 0 {
105 break;
106 }
107 }
108 }
109 Ok(tag)
110}
111
112fn parse_len(input: &[u8], pos: &mut usize) -> Result<usize, TlvError> {
114 let b0 = *input.get(*pos).ok_or(TlvError::Truncated)?;
115 *pos += 1;
116 if b0 & LEN_LONG == 0 {
117 return Ok(usize::from(b0));
118 }
119 let count = usize::from(b0 & !LEN_LONG);
120 if count == 0 || count > core::mem::size_of::<usize>() {
123 return Err(TlvError::BadLength);
124 }
125 let mut len = 0usize;
126 for _ in 0..count {
127 let b = *input.get(*pos).ok_or(TlvError::Truncated)?;
128 *pos += 1;
129 len = (len << 8) | usize::from(b);
130 }
131 Ok(len)
132}
133
134fn write_tag(tag: u32, out: &mut [u8], pos: usize) -> Result<usize, TlvError> {
136 let bytes = tag.to_be_bytes();
137 let start = bytes
139 .iter()
140 .position(|&b| b != 0)
141 .unwrap_or(bytes.len() - 1);
142 write_bytes(out, pos, &bytes[start..])
143}
144
145fn write_len(len: usize, out: &mut [u8], pos: usize) -> Result<usize, TlvError> {
147 if len < usize::from(LEN_LONG) {
148 let b = u8::try_from(len).map_err(|_| TlvError::Overflow)?;
150 return write_bytes(out, pos, &[b]);
151 }
152 let bytes = len.to_be_bytes();
153 let start = bytes
154 .iter()
155 .position(|&b| b != 0)
156 .unwrap_or(bytes.len() - 1);
157 let body = &bytes[start..];
158 let count = u8::try_from(body.len()).map_err(|_| TlvError::Overflow)?;
160 let pos = write_bytes(out, pos, &[LEN_LONG | count])?;
161 write_bytes(out, pos, body)
162}
163
164fn write_bytes(out: &mut [u8], pos: usize, src: &[u8]) -> Result<usize, TlvError> {
166 let end = pos.checked_add(src.len()).ok_or(TlvError::Overflow)?;
167 if end > out.len() {
168 return Err(TlvError::Overflow);
169 }
170 out[pos..end].copy_from_slice(src);
171 Ok(end)
172}
173
174#[derive(thiserror::Error, Debug, Clone, PartialEq, Eq)]
176#[non_exhaustive]
177pub enum TlvError {
178 #[error("TLV value runs past end of buffer")]
180 Truncated,
181 #[error("malformed TLV length encoding")]
183 BadLength,
184 #[error("more than MAX_TLVS top-level TLV objects")]
186 TooMany,
187 #[error("encode output buffer too small")]
189 Overflow,
190}
191
192#[cfg(test)]
193mod tests {
194 use super::*;
195 use proptest::prelude::*;
196 use scll_test_util::HexSlice;
197
198 #[test]
201 fn parses_two_single_byte_tag_objects() {
202 let input = [0x4F, 0x02, 0xA0, 0x00, 0x9F, 0x70, 0x01, 0x07];
204 let tlvs = parse(&input).unwrap();
205 assert_eq!(tlvs.len(), 2);
206 assert_eq!(tlvs[0].tag, 0x4F);
207 assert_eq!(HexSlice(tlvs[0].value), HexSlice(&[0xA0, 0x00]));
208 assert_eq!(tlvs[1].tag, 0x9F70);
209 assert_eq!(HexSlice(tlvs[1].value), HexSlice(&[0x07]));
210 }
211
212 #[test]
213 fn parses_long_form_length() {
214 let mut input = heapless::Vec::<u8, 300>::new();
216 input.extend_from_slice(&[0x66, 0x81, 0x82]).unwrap();
217 input.extend_from_slice(&[0xAB; 0x82]).unwrap();
218 let tlvs = parse(&input).unwrap();
219 assert_eq!(tlvs.len(), 1);
220 assert_eq!(tlvs[0].tag, 0x66);
221 assert_eq!(tlvs[0].value.len(), 0x82);
222 }
223
224 #[test]
225 fn empty_input_yields_no_objects() {
226 assert_eq!(parse(&[]).unwrap().len(), 0);
227 }
228
229 #[test]
232 fn truncated_value_is_rejected() {
233 assert_eq!(parse(&[0x4F, 0x05, 0x01, 0x02]), Err(TlvError::Truncated));
234 }
235
236 #[test]
237 fn truncated_tag_is_rejected() {
238 assert_eq!(parse(&[0x9F]), Err(TlvError::Truncated));
240 }
241
242 #[test]
243 fn missing_length_octet_is_rejected() {
244 assert_eq!(parse(&[0x4F]), Err(TlvError::Truncated));
245 }
246
247 #[test]
248 fn indefinite_length_is_rejected() {
249 assert_eq!(parse(&[0x4F, 0x80, 0x01]), Err(TlvError::BadLength));
250 }
251
252 #[test]
253 fn oversized_tag_is_rejected() {
254 assert_eq!(
256 parse(&[0x1F, 0x81, 0x81, 0x81, 0x81, 0x01]),
257 Err(TlvError::BadLength)
258 );
259 }
260
261 #[test]
262 fn long_form_length_octets_exceeding_usize_rejected() {
263 assert_eq!(parse(&[0x4F, 0x89]), Err(TlvError::BadLength));
265 }
266
267 #[test]
268 fn too_many_objects_is_rejected() {
269 let mut input = heapless::Vec::<u8, { (MAX_TLVS + 1) * 3 }>::new();
271 for _ in 0..=MAX_TLVS {
272 input.extend_from_slice(&[0x80, 0x01, 0x00]).unwrap();
273 }
274 assert_eq!(parse(&input), Err(TlvError::TooMany));
275 }
276
277 #[test]
280 fn encode_emits_canonical_long_form() {
281 let value = [0xAB; 0x82];
282 let items = [Tlv {
283 tag: 0x66,
284 value: &value,
285 }];
286 let mut out = [0u8; 300];
287 let n = encode(&items, &mut out).unwrap();
288 assert_eq!(HexSlice(&out[..3]), HexSlice(&[0x66, 0x81, 0x82]));
289 assert_eq!(n, 3 + 0x82);
290 }
291
292 #[test]
293 fn encode_overflow_is_reported_not_panicked() {
294 let items = [Tlv {
295 tag: 0x4F,
296 value: &[1, 2, 3, 4],
297 }];
298 let mut out = [0u8; 3]; assert_eq!(encode(&items, &mut out), Err(TlvError::Overflow));
300 }
301
302 fn tag_strategy() -> impl Strategy<Value = u32> {
307 prop_oneof![
308 (0u32..=0xFF).prop_filter("not a continuation tag", |t| t & 0x1F != 0x1F),
309 (0u32..=0x7F).prop_map(|lo| 0x9F00 | lo),
310 ]
311 }
312
313 fn tlv_items() -> impl Strategy<Value = std::vec::Vec<(u32, std::vec::Vec<u8>)>> {
314 proptest::collection::vec(
315 (
316 tag_strategy(),
317 proptest::collection::vec(any::<u8>(), 0..=64),
318 ),
319 0..=MAX_TLVS,
320 )
321 }
322
323 proptest! {
324 #[test]
326 fn parse_after_encode_is_identity(items in tlv_items()) {
327 let tlvs: std::vec::Vec<Tlv> =
328 items.iter().map(|(t, v)| Tlv { tag: *t, value: v }).collect();
329 let mut out = [0u8; 64 * 70];
330 let n = encode(&tlvs, &mut out).unwrap();
331 let parsed = parse(&out[..n]).unwrap();
332 prop_assert_eq!(parsed.len(), tlvs.len());
333 for (got, want) in parsed.iter().zip(tlvs.iter()) {
334 prop_assert_eq!(got.tag, want.tag);
335 prop_assert_eq!(got.value, want.value);
336 }
337 }
338
339 #[test]
341 fn encoder_output_always_parses(items in tlv_items()) {
342 let tlvs: std::vec::Vec<Tlv> =
343 items.iter().map(|(t, v)| Tlv { tag: *t, value: v }).collect();
344 let mut out = [0u8; 64 * 70];
345 let n = encode(&tlvs, &mut out).unwrap();
346 prop_assert!(parse(&out[..n]).is_ok());
347 }
348
349 #[test]
351 fn parse_arbitrary_never_panics(bytes in proptest::collection::vec(any::<u8>(), 0..512)) {
352 let _ = parse(&bytes);
353 }
354 }
355}