1use crate::bitstream::{BitReader, BitWriter, re_emit_bits};
5use crate::error::Error;
6use crate::origin_path::OriginPath;
7use crate::use_site_path::UseSitePath;
8use crate::varint::{read_varint, write_varint};
9
10pub const TLV_USE_SITE_PATH_OVERRIDES: u8 = 0x00;
12pub const TLV_FINGERPRINTS: u8 = 0x01;
14pub const TLV_PUBKEYS: u8 = 0x02;
17pub const TLV_ORIGIN_PATH_OVERRIDES: u8 = 0x03;
20
21#[derive(Debug, Clone, PartialEq, Eq)]
24pub struct TlvSection {
25 pub use_site_path_overrides: Option<Vec<(u8, UseSitePath)>>,
27 pub fingerprints: Option<Vec<(u8, [u8; 4])>>,
29 pub pubkeys: Option<Vec<(u8, [u8; 65])>>,
33 pub origin_path_overrides: Option<Vec<(u8, OriginPath)>>,
36 pub unknown: Vec<(u8, Vec<u8>, usize)>,
39}
40
41impl TlvSection {
42 pub fn new_empty() -> Self {
44 Self {
48 use_site_path_overrides: None,
49 fingerprints: None,
50 pubkeys: None,
51 origin_path_overrides: None,
52 unknown: Vec::new(),
53 }
54 }
55
56 pub fn is_empty(&self) -> bool {
58 let Self {
61 use_site_path_overrides,
62 fingerprints,
63 pubkeys,
64 origin_path_overrides,
65 unknown,
66 } = self;
67 use_site_path_overrides.is_none()
68 && fingerprints.is_none()
69 && pubkeys.is_none()
70 && origin_path_overrides.is_none()
71 && unknown.is_empty()
72 }
73
74 pub fn write(&self, w: &mut BitWriter, key_index_width: u8) -> Result<(), Error> {
87 let Self {
89 use_site_path_overrides,
90 fingerprints,
91 pubkeys,
92 origin_path_overrides,
93 unknown,
94 } = self;
95
96 let mut entries: Vec<(u8, Vec<u8>, usize)> = Vec::new();
98
99 if let Some(overrides) = use_site_path_overrides {
100 if overrides.is_empty() {
101 return Err(Error::EmptyTlvEntry {
102 tag: TLV_USE_SITE_PATH_OVERRIDES,
103 });
104 }
105 let mut sub = BitWriter::new();
106 let mut last_idx: Option<u8> = None;
107 for (idx, path) in overrides {
108 if let Some(prev) = last_idx {
109 if *idx <= prev {
110 return Err(Error::OverrideOrderViolation {
111 prev,
112 current: *idx,
113 });
114 }
115 }
116 last_idx = Some(*idx);
117 sub.write_bits(u64::from(*idx), key_index_width as usize);
118 path.write(&mut sub)?;
119 }
120 let bit_len = sub.bit_len();
121 entries.push((TLV_USE_SITE_PATH_OVERRIDES, sub.into_bytes(), bit_len));
122 }
123 if let Some(fps) = fingerprints {
124 if fps.is_empty() {
125 return Err(Error::EmptyTlvEntry {
126 tag: TLV_FINGERPRINTS,
127 });
128 }
129 let mut sub = BitWriter::new();
130 let mut last_idx: Option<u8> = None;
131 for (idx, fp) in fps {
132 if let Some(prev) = last_idx {
133 if *idx <= prev {
134 return Err(Error::OverrideOrderViolation {
135 prev,
136 current: *idx,
137 });
138 }
139 }
140 last_idx = Some(*idx);
141 sub.write_bits(u64::from(*idx), key_index_width as usize);
142 for b in fp {
143 sub.write_bits(u64::from(*b), 8);
144 }
145 }
146 let bit_len = sub.bit_len();
147 entries.push((TLV_FINGERPRINTS, sub.into_bytes(), bit_len));
148 }
149 if let Some(pks) = pubkeys {
150 if pks.is_empty() {
151 return Err(Error::EmptyTlvEntry { tag: TLV_PUBKEYS });
152 }
153 let mut sub = BitWriter::new();
154 let mut last_idx: Option<u8> = None;
155 for (idx, xpub) in pks {
156 if let Some(prev) = last_idx {
157 if *idx <= prev {
158 return Err(Error::OverrideOrderViolation {
159 prev,
160 current: *idx,
161 });
162 }
163 }
164 last_idx = Some(*idx);
165 sub.write_bits(u64::from(*idx), key_index_width as usize);
166 for b in xpub {
167 sub.write_bits(u64::from(*b), 8);
168 }
169 }
170 let bit_len = sub.bit_len();
171 entries.push((TLV_PUBKEYS, sub.into_bytes(), bit_len));
172 }
173 if let Some(paths) = origin_path_overrides {
174 if paths.is_empty() {
175 return Err(Error::EmptyTlvEntry {
176 tag: TLV_ORIGIN_PATH_OVERRIDES,
177 });
178 }
179 let mut sub = BitWriter::new();
180 let mut last_idx: Option<u8> = None;
181 for (idx, path) in paths {
182 if let Some(prev) = last_idx {
183 if *idx <= prev {
184 return Err(Error::OverrideOrderViolation {
185 prev,
186 current: *idx,
187 });
188 }
189 }
190 last_idx = Some(*idx);
191 sub.write_bits(u64::from(*idx), key_index_width as usize);
192 path.write(&mut sub)?;
193 }
194 let bit_len = sub.bit_len();
195 entries.push((TLV_ORIGIN_PATH_OVERRIDES, sub.into_bytes(), bit_len));
196 }
197 for (tag, payload, bit_len) in unknown {
198 entries.push((*tag, payload.clone(), *bit_len));
199 }
200 entries.sort_by_key(|(t, _, _)| *t);
201
202 for (tag, payload, bit_len) in entries {
203 w.write_bits(u64::from(tag), 5);
204 write_varint(w, bit_len as u32)?;
205 re_emit_bits(w, &payload, bit_len)?;
206 }
207 Ok(())
208 }
209
210 pub fn read(r: &mut BitReader, key_index_width: u8, n: u8) -> Result<Self, Error> {
213 let mut section = Self::new_empty();
214 let mut last_tag: Option<u8> = None;
215 loop {
216 let entry_start = r.save_position();
219 if r.remaining_bits() < 5 {
220 break; }
222 let parse_result: Result<(), Error> = (|| {
229 let tag = r.read_bits(5)? as u8;
230 if let Some(prev) = last_tag {
234 if tag <= prev {
235 return Err(Error::TlvOrderingViolation { prev, current: tag });
236 }
237 }
238 let bit_len = read_varint(r)? as usize;
239 if bit_len > r.remaining_bits() {
240 return Err(Error::TlvLengthExceedsRemaining {
241 length: bit_len,
242 remaining: r.remaining_bits(),
243 });
244 }
245 if bit_len == 0 {
249 return Err(Error::EmptyTlvEntry { tag });
250 }
251 match tag {
252 TLV_USE_SITE_PATH_OVERRIDES => {
253 let entry = read_use_site_overrides(r, bit_len, key_index_width, n)?;
254 section.use_site_path_overrides = Some(entry);
255 }
256 TLV_FINGERPRINTS => {
257 let entry = read_fingerprints(r, bit_len, key_index_width, n)?;
258 section.fingerprints = Some(entry);
259 }
260 TLV_PUBKEYS => {
261 let entry = read_pubkeys(r, bit_len, key_index_width, n)?;
262 section.pubkeys = Some(entry);
263 }
264 TLV_ORIGIN_PATH_OVERRIDES => {
265 let entry = read_origin_path_overrides(r, bit_len, key_index_width, n)?;
266 section.origin_path_overrides = Some(entry);
267 }
268 _ => {
269 let mut sub = BitWriter::new();
271 let mut remaining = bit_len;
272 while remaining > 0 {
273 let chunk = remaining.min(8);
274 let bits = r.read_bits(chunk)?;
275 sub.write_bits(bits, chunk);
276 remaining -= chunk;
277 }
278 let payload = sub.into_bytes();
279 section.unknown.push((tag, payload, bit_len));
280 }
281 }
282 last_tag = Some(tag);
283 Ok(())
284 })();
285
286 match parse_result {
287 Ok(()) => continue,
288 Err(e) => {
289 r.restore_position(entry_start);
293 let remaining_at_entry_start = r.remaining_bits();
294 if remaining_at_entry_start <= 7 {
297 break;
298 }
299 return Err(e);
302 }
303 }
304 }
305 Ok(section)
306 }
307}
308
309fn read_sparse_tlv_idx(
318 r: &mut BitReader,
319 key_index_width: u8,
320 n: u8,
321 last_idx: Option<u8>,
322) -> Result<u8, Error> {
323 let idx = r.read_bits(key_index_width as usize)? as u8;
324 if idx >= n {
325 return Err(Error::PlaceholderIndexOutOfRange { idx, n });
326 }
327 if let Some(prev) = last_idx {
328 if idx <= prev {
329 return Err(Error::OverrideOrderViolation { prev, current: idx });
330 }
331 }
332 Ok(idx)
333}
334
335fn read_sparse_tlv_body<T, F>(
349 r: &mut BitReader,
350 bit_len: usize,
351 tag: u8,
352 key_index_width: u8,
353 n: u8,
354 mut read_value: F,
355) -> Result<Vec<(u8, T)>, Error>
356where
357 F: FnMut(&mut BitReader) -> Result<T, Error>,
358{
359 let start = r.bit_position();
360 let saved_limit = r.save_bit_limit();
361 r.set_bit_limit_for_scope(start + bit_len);
362
363 let mut entries: Vec<(u8, T)> = Vec::new();
364 let mut last_idx: Option<u8> = None;
365
366 let result = (|| -> Result<(), Error> {
367 while r.bit_position() - start < bit_len {
368 let idx = read_sparse_tlv_idx(r, key_index_width, n, last_idx)?;
369 let value = read_value(r)?;
370 last_idx = Some(idx);
371 entries.push((idx, value));
372 }
373 Ok(())
374 })();
375
376 r.restore_bit_limit(saved_limit);
377 result?;
378
379 if entries.is_empty() {
380 return Err(Error::EmptyTlvEntry { tag });
381 }
382 Ok(entries)
383}
384
385fn read_use_site_overrides(
386 r: &mut BitReader,
387 bit_len: usize,
388 key_index_width: u8,
389 n: u8,
390) -> Result<Vec<(u8, UseSitePath)>, Error> {
391 read_sparse_tlv_body(
392 r,
393 bit_len,
394 TLV_USE_SITE_PATH_OVERRIDES,
395 key_index_width,
396 n,
397 UseSitePath::read,
398 )
399}
400
401fn read_fingerprints(
402 r: &mut BitReader,
403 bit_len: usize,
404 key_index_width: u8,
405 n: u8,
406) -> Result<Vec<(u8, [u8; 4])>, Error> {
407 read_sparse_tlv_body(r, bit_len, TLV_FINGERPRINTS, key_index_width, n, |r| {
408 let mut fp = [0u8; 4];
409 for byte in &mut fp {
410 *byte = r.read_bits(8)? as u8;
411 }
412 Ok(fp)
413 })
414}
415
416fn read_pubkeys(
417 r: &mut BitReader,
418 bit_len: usize,
419 key_index_width: u8,
420 n: u8,
421) -> Result<Vec<(u8, [u8; 65])>, Error> {
422 read_sparse_tlv_body(r, bit_len, TLV_PUBKEYS, key_index_width, n, |r| {
423 let mut xpub = [0u8; 65];
424 for byte in &mut xpub {
425 *byte = r.read_bits(8)? as u8;
426 }
427 Ok(xpub)
428 })
429}
430
431fn read_origin_path_overrides(
432 r: &mut BitReader,
433 bit_len: usize,
434 key_index_width: u8,
435 n: u8,
436) -> Result<Vec<(u8, OriginPath)>, Error> {
437 read_sparse_tlv_body(
440 r,
441 bit_len,
442 TLV_ORIGIN_PATH_OVERRIDES,
443 key_index_width,
444 n,
445 OriginPath::read,
446 )
447}
448
449#[cfg(test)]
450mod tests {
451 use super::*;
452 use crate::origin_path::PathComponent;
453
454 #[test]
455 fn empty_tlv_section_round_trip() {
456 let s = TlvSection::new_empty();
457 assert!(s.is_empty());
458 let mut w = BitWriter::new();
459 s.write(&mut w, 2).unwrap();
460 assert_eq!(w.bit_len(), 0);
461 }
462
463 #[test]
464 fn use_site_path_override_round_trip() {
465 let mut s = TlvSection::new_empty();
466 s.use_site_path_overrides = Some(vec![(
467 1u8,
468 UseSitePath {
469 multipath: None,
470 wildcard_hardened: true,
471 },
472 )]);
473 let mut w = BitWriter::new();
474 s.write(&mut w, 2).unwrap();
475 let bit_len = w.bit_len();
476 let bytes = w.into_bytes();
477 let mut r = BitReader::new(&bytes);
478 let s2 = TlvSection::read(&mut r, 2, 3).unwrap();
479 assert_eq!(s2, s);
480 assert_eq!(r.bit_position(), bit_len);
481 }
482
483 #[test]
484 fn fingerprint_round_trip() {
485 let mut s = TlvSection::new_empty();
486 s.fingerprints = Some(vec![
487 (0u8, [0xaa, 0xbb, 0xcc, 0xdd]),
488 (2u8, [0x11, 0x22, 0x33, 0x44]),
489 ]);
490 let mut w = BitWriter::new();
491 s.write(&mut w, 2).unwrap();
492 let bytes = w.into_bytes();
493 let mut r = BitReader::new(&bytes);
494 let s2 = TlvSection::read(&mut r, 2, 3).unwrap();
495 assert_eq!(s2, s);
496 }
497
498 #[test]
499 fn pubkeys_round_trip() {
500 let mut xpub_a = [0u8; 65];
502 for (i, b) in xpub_a.iter_mut().enumerate() {
503 *b = i as u8;
504 }
505 let mut xpub_b = [0u8; 65];
506 for (i, b) in xpub_b.iter_mut().enumerate() {
507 *b = (0xff - i as u8) ^ 0x5a;
508 }
509 let mut s = TlvSection::new_empty();
510 s.pubkeys = Some(vec![(0u8, xpub_a), (2u8, xpub_b)]);
511
512 let mut w = BitWriter::new();
513 s.write(&mut w, 2).unwrap();
514 let bit_len = w.bit_len();
515 let bytes = w.into_bytes();
516 let mut r = BitReader::new(&bytes);
517 let s2 = TlvSection::read(&mut r, 2, 3).unwrap();
518 assert_eq!(s2, s);
519 assert_eq!(r.bit_position(), bit_len);
520 }
521
522 #[test]
523 fn origin_path_overrides_round_trip() {
524 let bip84 = OriginPath {
526 components: vec![
527 PathComponent {
528 hardened: true,
529 value: 84,
530 },
531 PathComponent {
532 hardened: true,
533 value: 0,
534 },
535 PathComponent {
536 hardened: true,
537 value: 5,
538 },
539 ],
540 };
541 let bip48 = OriginPath {
542 components: vec![
543 PathComponent {
544 hardened: true,
545 value: 48,
546 },
547 PathComponent {
548 hardened: true,
549 value: 0,
550 },
551 PathComponent {
552 hardened: true,
553 value: 0,
554 },
555 PathComponent {
556 hardened: true,
557 value: 2,
558 },
559 ],
560 };
561 let mut s = TlvSection::new_empty();
562 s.origin_path_overrides = Some(vec![(0u8, bip84), (1u8, bip48)]);
563
564 let mut w = BitWriter::new();
565 s.write(&mut w, 2).unwrap();
566 let bit_len = w.bit_len();
567 let bytes = w.into_bytes();
568 let mut r = BitReader::new(&bytes);
569 let s2 = TlvSection::read(&mut r, 2, 3).unwrap();
570 assert_eq!(s2, s);
571 assert_eq!(r.bit_position(), bit_len);
572 }
573
574 #[test]
575 fn ascending_tag_order_enforced_in_encoder() {
576 let mut s = TlvSection::new_empty();
578 s.use_site_path_overrides = Some(vec![(
579 0,
580 UseSitePath {
581 multipath: None,
582 wildcard_hardened: false,
583 },
584 )]);
585 s.fingerprints = Some(vec![(0, [0u8; 4])]);
586 s.pubkeys = Some(vec![(0, [0u8; 65])]);
587 s.origin_path_overrides = Some(vec![(
588 0,
589 OriginPath {
590 components: vec![PathComponent {
591 hardened: true,
592 value: 84,
593 }],
594 },
595 )]);
596 let mut w = BitWriter::new();
597 s.write(&mut w, 2).unwrap();
598 let bytes = w.into_bytes();
599 let first_tag = (bytes[0] >> 3) & 0x1F;
600 assert_eq!(first_tag, TLV_USE_SITE_PATH_OVERRIDES);
601 }
602
603 #[test]
604 fn pubkeys_ordering_violation_rejected_at_encoder() {
605 let mut s = TlvSection::new_empty();
607 s.pubkeys = Some(vec![(1u8, [0u8; 65]), (0u8, [0u8; 65])]);
608 let mut w = BitWriter::new();
609 let result = s.write(&mut w, 2);
610 assert!(matches!(
611 result,
612 Err(Error::OverrideOrderViolation {
613 prev: 1,
614 current: 0
615 })
616 ));
617 }
618
619 #[test]
620 fn pubkeys_ordering_violation_rejected_at_decoder() {
621 let mut sub = BitWriter::new();
625 sub.write_bits(1, 2);
627 for _ in 0..65 {
628 sub.write_bits(0, 8);
629 }
630 sub.write_bits(1, 2);
632 for _ in 0..65 {
633 sub.write_bits(0, 8);
634 }
635 let bit_len = sub.bit_len();
636 let payload_bytes = sub.into_bytes();
637
638 let mut w = BitWriter::new();
639 w.write_bits(u64::from(TLV_PUBKEYS), 5);
640 write_varint(&mut w, bit_len as u32).unwrap();
641 re_emit_bits(&mut w, &payload_bytes, bit_len).unwrap();
642 let total_bit_len = w.bit_len();
643 let bytes = w.into_bytes();
644
645 let mut r = BitReader::with_bit_limit(&bytes, total_bit_len);
646 let result = TlvSection::read(&mut r, 2, 3);
647 assert!(matches!(
648 result,
649 Err(Error::OverrideOrderViolation {
650 prev: 1,
651 current: 1
652 })
653 ));
654 }
655
656 #[test]
657 fn read_sparse_tlv_idx_out_of_range() {
658 let mut sub = BitWriter::new();
660 sub.write_bits(3, 2);
661 let bit_len = sub.bit_len();
662 let bytes = sub.into_bytes();
663 let mut r = BitReader::with_bit_limit(&bytes, bit_len);
664
665 let result = read_sparse_tlv_idx(&mut r, 2, 2, None);
666 assert!(matches!(
667 result,
668 Err(Error::PlaceholderIndexOutOfRange { idx: 3, n: 2 })
669 ));
670 }
671
672 #[test]
673 fn read_sparse_tlv_idx_non_ascending() {
674 let mut sub = BitWriter::new();
675 sub.write_bits(0, 2);
676 let bit_len = sub.bit_len();
677 let bytes = sub.into_bytes();
678 let mut r = BitReader::with_bit_limit(&bytes, bit_len);
679
680 let result = read_sparse_tlv_idx(&mut r, 2, 3, Some(1));
681 assert!(matches!(
682 result,
683 Err(Error::OverrideOrderViolation {
684 prev: 1,
685 current: 0
686 })
687 ));
688 }
689
690 #[test]
691 fn empty_pubkeys_vec_rejected_at_encoder() {
692 let mut s = TlvSection::new_empty();
693 s.pubkeys = Some(Vec::new());
694 let mut w = BitWriter::new();
695 let result = s.write(&mut w, 2);
696 assert!(matches!(
697 result,
698 Err(Error::EmptyTlvEntry { tag }) if tag == TLV_PUBKEYS
699 ));
700 }
701
702 #[test]
703 fn empty_origin_path_overrides_vec_rejected_at_encoder() {
704 let mut s = TlvSection::new_empty();
705 s.origin_path_overrides = Some(Vec::new());
706 let mut w = BitWriter::new();
707 let result = s.write(&mut w, 2);
708 assert!(matches!(
709 result,
710 Err(Error::EmptyTlvEntry { tag }) if tag == TLV_ORIGIN_PATH_OVERRIDES
711 ));
712 }
713
714 fn craft_inflated_tlv_wire(
719 tag: u8,
720 idx: u8,
721 idx_width: u8,
722 record_payload_bits: &[(u64, usize)],
723 slack_bits: usize,
724 ) -> (Vec<u8>, usize) {
725 let mut w = BitWriter::new();
726 w.write_bits(u64::from(tag), 5);
728 let actual_record_bits: usize =
730 (idx_width as usize) + record_payload_bits.iter().map(|(_, n)| n).sum::<usize>();
731 let declared_bit_len = actual_record_bits + slack_bits;
732 write_varint(&mut w, declared_bit_len as u32).unwrap();
733 w.write_bits(u64::from(idx), idx_width as usize);
735 for (val, bits) in record_payload_bits {
736 w.write_bits(*val, *bits);
737 }
738 for _ in 0..slack_bits {
740 w.write_bits(0, 1);
741 }
742 let bit_len = w.bit_len();
743 (w.into_bytes(), bit_len)
744 }
745
746 #[test]
758 fn fingerprints_with_trailing_slack_rejected() {
759 let (bytes, total_bits) =
760 craft_inflated_tlv_wire(TLV_FINGERPRINTS, 0, 1, &[(0xDEAD_BEEF, 32)], 4);
761 let mut r = BitReader::with_bit_limit(&bytes, total_bits);
762 let result = TlvSection::read(&mut r, 1, 1);
763 assert!(
764 result.is_err(),
765 "trailing slack must be rejected, got {:?}",
766 result
767 );
768 }
769
770 #[test]
771 fn pubkeys_with_trailing_slack_rejected() {
772 let payload: Vec<(u64, usize)> = (0..65).map(|_i| (0x42u64, 8)).collect();
773 let (bytes, total_bits) = craft_inflated_tlv_wire(TLV_PUBKEYS, 0, 1, &payload, 3);
774 let mut r = BitReader::with_bit_limit(&bytes, total_bits);
775 let result = TlvSection::read(&mut r, 1, 1);
776 assert!(
777 result.is_err(),
778 "trailing slack must be rejected, got {:?}",
779 result
780 );
781 }
782
783 #[test]
784 fn use_site_path_overrides_with_trailing_slack_rejected() {
785 let mut path_w = BitWriter::new();
786 UseSitePath::standard_multipath()
787 .write(&mut path_w)
788 .unwrap();
789 let path_bit_len = path_w.bit_len();
790 let path_bytes = path_w.into_bytes();
791 let mut path_record: Vec<(u64, usize)> = Vec::new();
792 let mut br = BitReader::new(&path_bytes);
793 let mut consumed = 0;
794 while consumed < path_bit_len {
795 let chunk = (path_bit_len - consumed).min(8);
796 path_record.push((br.read_bits(chunk).unwrap(), chunk));
797 consumed += chunk;
798 }
799 let (bytes, total_bits) =
800 craft_inflated_tlv_wire(TLV_USE_SITE_PATH_OVERRIDES, 0, 1, &path_record, 2);
801 let mut r = BitReader::with_bit_limit(&bytes, total_bits);
802 let result = TlvSection::read(&mut r, 1, 1);
803 assert!(
804 result.is_err(),
805 "trailing slack must be rejected, got {:?}",
806 result
807 );
808 }
809
810 #[test]
811 fn origin_path_overrides_with_trailing_slack_rejected() {
812 let (bytes, total_bits) =
813 craft_inflated_tlv_wire(TLV_ORIGIN_PATH_OVERRIDES, 0, 1, &[(0, 4)], 5);
814 let mut r = BitReader::with_bit_limit(&bytes, total_bits);
815 let result = TlvSection::read(&mut r, 1, 1);
816 assert!(
817 result.is_err(),
818 "trailing slack must be rejected, got {:?}",
819 result
820 );
821 }
822}