1use std::time::Duration;
2
3use bytes::{BufMut, Bytes, BytesMut};
4use ndn_tlv::TlvWriter;
5
6use super::{write_name, write_nni};
7use crate::{Name, SignatureType, tlv_type};
8
9const SIGINFO_DIGEST_SHA256: [u8; 5] = [0x16, 0x03, 0x1B, 0x01, 0x00];
20
21const SIGINFO_DIGEST_BLAKE3: [u8; 5] = [0x16, 0x03, 0x1B, 0x01, 0x06];
26
27#[inline(always)]
29fn put_vu(buf: &mut BytesMut, v: u64) {
30 let mut tmp = [0u8; 9];
31 let n = ndn_tlv::write_varu64(&mut tmp, v);
32 buf.put_slice(&tmp[..n]);
33}
34
35struct FastPathSizes {
40 comps_inner: usize,
42 name_tlv: usize,
44 mi_inner: usize,
46 metainfo_tlv: usize,
48 content_tlv: usize,
50}
51
52impl FastPathSizes {
53 fn compute(
54 name: &Name,
55 freshness: Option<Duration>,
56 final_block_id: Option<&Bytes>,
57 content: &[u8],
58 ) -> Self {
59 use ndn_tlv::varu64_size;
60
61 let comps_inner: usize = name
62 .components()
63 .iter()
64 .map(|c| varu64_size(c.typ) + varu64_size(c.value.len() as u64) + c.value.len())
65 .sum();
66 let name_tlv = varu64_size(tlv_type::NAME) + varu64_size(comps_inner as u64) + comps_inner;
67
68 let mi_inner = {
69 let mut s = 0usize;
70 if let Some(f) = freshness {
71 let (_, nni_len) = super::nni(f.as_millis() as u64);
72 s +=
73 varu64_size(tlv_type::FRESHNESS_PERIOD) + varu64_size(nni_len as u64) + nni_len;
74 }
75 if let Some(fb) = final_block_id {
76 s +=
77 varu64_size(tlv_type::FINAL_BLOCK_ID) + varu64_size(fb.len() as u64) + fb.len();
78 }
79 s
80 };
81 let metainfo_tlv = if mi_inner > 0 {
82 varu64_size(tlv_type::META_INFO) + varu64_size(mi_inner as u64) + mi_inner
83 } else {
84 0
85 };
86
87 let content_tlv =
88 varu64_size(tlv_type::CONTENT) + varu64_size(content.len() as u64) + content.len();
89
90 Self {
91 comps_inner,
92 name_tlv,
93 mi_inner,
94 metainfo_tlv,
95 content_tlv,
96 }
97 }
98}
99
100fn write_fields(
106 buf: &mut BytesMut,
107 name: &Name,
108 freshness: Option<Duration>,
109 final_block_id: Option<&Bytes>,
110 content: &[u8],
111 sz: &FastPathSizes,
112) {
113 put_vu(buf, tlv_type::NAME);
114 put_vu(buf, sz.comps_inner as u64);
115 for comp in name.components() {
116 put_vu(buf, comp.typ);
117 put_vu(buf, comp.value.len() as u64);
118 buf.put_slice(&comp.value);
119 }
120 if sz.mi_inner > 0 {
121 put_vu(buf, tlv_type::META_INFO);
122 put_vu(buf, sz.mi_inner as u64);
123 if let Some(f) = freshness {
124 let (nni_buf, nni_len) = super::nni(f.as_millis() as u64);
125 put_vu(buf, tlv_type::FRESHNESS_PERIOD);
126 put_vu(buf, nni_len as u64);
127 buf.put_slice(&nni_buf[..nni_len]);
128 }
129 if let Some(fb) = final_block_id {
130 put_vu(buf, tlv_type::FINAL_BLOCK_ID);
131 put_vu(buf, fb.len() as u64);
132 buf.put_slice(fb);
133 }
134 }
135 put_vu(buf, tlv_type::CONTENT);
136 put_vu(buf, content.len() as u64);
137 buf.put_slice(content);
138}
139
140pub struct DataBuilder {
152 name: Name,
153 content: Vec<u8>,
154 freshness: Option<Duration>,
155 final_block_id: Option<Bytes>,
159}
160
161impl DataBuilder {
162 pub fn new(name: impl Into<Name>, content: &[u8]) -> Self {
163 Self {
164 name: name.into(),
165 content: content.to_vec(),
166 freshness: None,
167 final_block_id: None,
168 }
169 }
170
171 pub fn freshness(mut self, d: Duration) -> Self {
172 self.freshness = Some(d);
173 self
174 }
175
176 pub fn final_block_id(mut self, component_bytes: Bytes) -> Self {
178 self.final_block_id = Some(component_bytes);
179 self
180 }
181
182 pub fn final_block_id_seg(self, last_seg: usize) -> Self {
193 let s = last_seg.to_string();
194 let bytes = s.as_bytes();
195 let mut buf = Vec::with_capacity(2 + bytes.len());
197 buf.push(0x08u8); buf.push(bytes.len() as u8);
200 buf.extend_from_slice(bytes);
201 self.final_block_id(Bytes::from(buf))
202 }
203
204 pub fn final_block_id_typed_seg(self, last_seg: u64) -> Self {
210 let encoded = encode_nni_be(last_seg);
211 let mut buf = Vec::with_capacity(2 + encoded.len());
212 buf.push(0x32u8); buf.push(encoded.len() as u8);
214 buf.extend_from_slice(&encoded);
215 self.final_block_id(Bytes::from(buf))
216 }
217
218 #[cfg(feature = "std")]
236 pub fn sign_digest_sha256(self) -> Bytes {
237 use ndn_tlv::varu64_size;
238
239 const SIGVALUE: usize = 34;
241
242 let sz = FastPathSizes::compute(
243 &self.name,
244 self.freshness,
245 self.final_block_id.as_ref(),
246 &self.content,
247 );
248 let signed_size =
249 sz.name_tlv + sz.metainfo_tlv + sz.content_tlv + SIGINFO_DIGEST_SHA256.len();
250 let inner_size = signed_size + SIGVALUE;
251 let header_size = varu64_size(tlv_type::DATA) + varu64_size(inner_size as u64);
252
253 let mut buf = BytesMut::with_capacity(header_size + inner_size);
254 put_vu(&mut buf, tlv_type::DATA);
255 put_vu(&mut buf, inner_size as u64);
256
257 let signed_start = buf.len();
258 write_fields(
259 &mut buf,
260 &self.name,
261 self.freshness,
262 self.final_block_id.as_ref(),
263 &self.content,
264 &sz,
265 );
266 buf.put_slice(&SIGINFO_DIGEST_SHA256);
267 debug_assert_eq!(
268 buf.len() - signed_start,
269 signed_size,
270 "signed region size mismatch"
271 );
272
273 let hash = ring::digest::digest(&ring::digest::SHA256, &buf[signed_start..]);
274 buf.put_slice(&[0x17u8, 0x20]);
275 buf.put_slice(hash.as_ref());
276 debug_assert_eq!(buf.len(), header_size + inner_size, "total size mismatch");
277
278 buf.freeze()
279 }
280
281 #[cfg(feature = "std")]
301 pub fn sign_digest_blake3(self) -> Bytes {
302 use ndn_tlv::varu64_size;
303
304 const SIGVALUE: usize = 34;
306
307 let sz = FastPathSizes::compute(
308 &self.name,
309 self.freshness,
310 self.final_block_id.as_ref(),
311 &self.content,
312 );
313 let signed_size =
314 sz.name_tlv + sz.metainfo_tlv + sz.content_tlv + SIGINFO_DIGEST_BLAKE3.len();
315 let inner_size = signed_size + SIGVALUE;
316 let header_size = varu64_size(tlv_type::DATA) + varu64_size(inner_size as u64);
317
318 let mut buf = BytesMut::with_capacity(header_size + inner_size);
319 put_vu(&mut buf, tlv_type::DATA);
320 put_vu(&mut buf, inner_size as u64);
321
322 let signed_start = buf.len();
323 write_fields(
324 &mut buf,
325 &self.name,
326 self.freshness,
327 self.final_block_id.as_ref(),
328 &self.content,
329 &sz,
330 );
331 buf.put_slice(&SIGINFO_DIGEST_BLAKE3);
332 debug_assert_eq!(
333 buf.len() - signed_start,
334 signed_size,
335 "signed region size mismatch"
336 );
337
338 let hash = blake3::hash(&buf[signed_start..]);
339 buf.put_slice(&[0x17u8, 0x20]);
340 buf.put_slice(hash.as_bytes());
341 debug_assert_eq!(buf.len(), header_size + inner_size, "total size mismatch");
342
343 buf.freeze()
344 }
345
346 pub fn sign_none(self) -> Bytes {
356 use ndn_tlv::varu64_size;
357
358 let sz = FastPathSizes::compute(
359 &self.name,
360 self.freshness,
361 self.final_block_id.as_ref(),
362 &self.content,
363 );
364 let inner_size = sz.name_tlv + sz.metainfo_tlv + sz.content_tlv;
365 let header_size = varu64_size(tlv_type::DATA) + varu64_size(inner_size as u64);
366
367 let mut buf = BytesMut::with_capacity(header_size + inner_size);
368 put_vu(&mut buf, tlv_type::DATA);
369 put_vu(&mut buf, inner_size as u64);
370 write_fields(
371 &mut buf,
372 &self.name,
373 self.freshness,
374 self.final_block_id.as_ref(),
375 &self.content,
376 &sz,
377 );
378 buf.freeze()
379 }
380
381 pub fn build(self) -> Bytes {
383 let mut w = TlvWriter::new();
384 w.write_nested(tlv_type::DATA, |w| {
385 write_name(w, &self.name);
386 if self.freshness.is_some() || self.final_block_id.is_some() {
387 let freshness = self.freshness;
388 let fbi = self.final_block_id.as_deref();
389 w.write_nested(tlv_type::META_INFO, |w| {
390 if let Some(f) = freshness {
391 write_nni(w, tlv_type::FRESHNESS_PERIOD, f.as_millis() as u64);
392 }
393 if let Some(fb) = fbi {
394 w.write_tlv(tlv_type::FINAL_BLOCK_ID, fb);
395 }
396 });
397 }
398 w.write_tlv(tlv_type::CONTENT, &self.content);
399 w.write_nested(tlv_type::SIGNATURE_INFO, |w| {
400 w.write_tlv(tlv_type::SIGNATURE_TYPE, &[0u8]);
401 });
402 w.write_tlv(tlv_type::SIGNATURE_VALUE, &[0u8; 32]);
403 });
404 w.finish()
405 }
406
407 pub async fn sign<F, Fut>(
414 self,
415 sig_type: SignatureType,
416 key_locator: Option<&Name>,
417 sign_fn: F,
418 ) -> Bytes
419 where
420 F: FnOnce(&[u8]) -> Fut,
421 Fut: std::future::Future<Output = Bytes>,
422 {
423 let mut inner = TlvWriter::new();
425 write_name(&mut inner, &self.name);
426 if self.freshness.is_some() || self.final_block_id.is_some() {
427 let freshness = self.freshness;
428 let fbi = self.final_block_id.as_deref();
429 inner.write_nested(tlv_type::META_INFO, |w| {
430 if let Some(f) = freshness {
431 write_nni(w, tlv_type::FRESHNESS_PERIOD, f.as_millis() as u64);
432 }
433 if let Some(fb) = fbi {
434 w.write_tlv(tlv_type::FINAL_BLOCK_ID, fb);
435 }
436 });
437 }
438 inner.write_tlv(tlv_type::CONTENT, &self.content);
439 let inner_bytes = inner.finish();
440
441 let mut sig_info_writer = TlvWriter::new();
443 sig_info_writer.write_nested(tlv_type::SIGNATURE_INFO, |w| {
444 write_nni(w, tlv_type::SIGNATURE_TYPE, sig_type.code());
445 if let Some(kl_name) = key_locator {
446 w.write_nested(tlv_type::KEY_LOCATOR, |w| {
447 write_name(w, kl_name);
448 });
449 }
450 });
451 let sig_info_bytes = sig_info_writer.finish();
452
453 let mut signed_region = Vec::with_capacity(inner_bytes.len() + sig_info_bytes.len());
455 signed_region.extend_from_slice(&inner_bytes);
456 signed_region.extend_from_slice(&sig_info_bytes);
457
458 let sig_value = sign_fn(&signed_region).await;
460
461 let mut w = TlvWriter::new();
463 w.write_nested(tlv_type::DATA, |w| {
464 w.write_raw(&signed_region);
465 w.write_tlv(tlv_type::SIGNATURE_VALUE, &sig_value);
466 });
467 w.finish()
468 }
469
470 pub fn sign_sync<F>(
476 self,
477 sig_type: SignatureType,
478 key_locator: Option<&Name>,
479 sign_fn: F,
480 ) -> Bytes
481 where
482 F: FnOnce(&[u8]) -> Bytes,
483 {
484 let est = self.content.len() + 256;
487 let mut w = TlvWriter::with_capacity(est);
488
489 let signed_start = w.len();
492 write_name(&mut w, &self.name);
493 if self.freshness.is_some() || self.final_block_id.is_some() {
494 let freshness = self.freshness;
495 let fbi = self.final_block_id.as_deref();
496 w.write_nested(tlv_type::META_INFO, |w| {
497 if let Some(f) = freshness {
498 write_nni(w, tlv_type::FRESHNESS_PERIOD, f.as_millis() as u64);
499 }
500 if let Some(fb) = fbi {
501 w.write_tlv(tlv_type::FINAL_BLOCK_ID, fb);
502 }
503 });
504 }
505 w.write_tlv(tlv_type::CONTENT, &self.content);
506 w.write_nested(tlv_type::SIGNATURE_INFO, |w| {
507 write_nni(w, tlv_type::SIGNATURE_TYPE, sig_type.code());
508 if let Some(kl_name) = key_locator {
509 w.write_nested(tlv_type::KEY_LOCATOR, |w| {
510 write_name(w, kl_name);
511 });
512 }
513 });
514 let sig_value = sign_fn(w.slice_from(signed_start));
516
517 let signed_region = w.slice_from(signed_start);
519 let inner_len = signed_region.len()
520 + ndn_tlv::varu64_size(tlv_type::SIGNATURE_VALUE)
521 + ndn_tlv::varu64_size(sig_value.len() as u64)
522 + sig_value.len();
523 let mut outer = TlvWriter::with_capacity(inner_len + 10);
524 outer.write_varu64(tlv_type::DATA);
525 outer.write_varu64(inner_len as u64);
526 outer.write_raw(signed_region);
527 outer.write_tlv(tlv_type::SIGNATURE_VALUE, &sig_value);
528 outer.finish()
529 }
530}
531
532fn encode_nni_be(v: u64) -> Vec<u8> {
537 if v == 0 {
538 return vec![0x00];
539 }
540 let bytes = v.to_be_bytes();
541 let first_nonzero = bytes.iter().position(|&b| b != 0).unwrap_or(7);
542 bytes[first_nonzero..].to_vec()
543}
544
545#[cfg(test)]
548mod tests {
549 use super::super::tests::{assert_bytes_eq, hex};
550 use super::*;
551 use crate::Data;
552 use bytes::Bytes;
553 use std::time::Duration;
554
555 #[test]
556 fn data_builder_basic() {
557 let wire = DataBuilder::new("/test", b"hello").build();
558 let data = Data::decode(wire).unwrap();
559 assert_eq!(data.name.to_string(), "/test");
560 assert_eq!(data.content().map(|b| b.as_ref()), Some(b"hello".as_ref()));
561 }
562
563 #[test]
564 fn data_builder_freshness() {
565 let wire = DataBuilder::new("/test", b"x")
566 .freshness(Duration::from_secs(60))
567 .build();
568 let data = Data::decode(wire).unwrap();
569 let mi = data.meta_info().expect("meta_info present");
570 assert_eq!(mi.freshness_period, Some(Duration::from_secs(60)));
571 }
572
573 #[test]
574 fn data_builder_sign() {
575 use std::pin::pin;
576 use std::task::{Context, Wake, Waker};
577
578 struct NoopWaker;
579 impl Wake for NoopWaker {
580 fn wake(self: std::sync::Arc<Self>) {}
581 }
582 let waker = Waker::from(std::sync::Arc::new(NoopWaker));
583 let mut cx = Context::from_waker(&waker);
584
585 let key_name: Name = "/key/test".parse().unwrap();
586 let fut = DataBuilder::new("/signed/data", b"payload")
587 .freshness(Duration::from_secs(10))
588 .sign(
589 SignatureType::SignatureEd25519,
590 Some(&key_name),
591 |region: &[u8]| {
592 let digest = ring::digest::digest(&ring::digest::SHA256, region);
593 std::future::ready(Bytes::copy_from_slice(digest.as_ref()))
594 },
595 );
596 let mut fut = pin!(fut);
597 let wire = match fut.as_mut().poll(&mut cx) {
598 std::task::Poll::Ready(b) => b,
599 std::task::Poll::Pending => panic!("sign future should complete immediately"),
600 };
601
602 let data = Data::decode(wire).unwrap();
603 assert_eq!(data.name.to_string(), "/signed/data");
604 assert_eq!(
605 data.content().map(|b| b.as_ref()),
606 Some(b"payload".as_ref())
607 );
608
609 let si = data.sig_info().expect("sig info");
610 assert_eq!(si.sig_type, SignatureType::SignatureEd25519);
611 let kl = si.key_locator.clone().expect("key locator");
612 assert_eq!(kl.to_string(), "/key/test");
613 }
614
615 #[test]
616 fn data_builder_sign_sync_matches_async() {
617 use std::pin::pin;
618 use std::task::{Context, Wake, Waker};
619
620 let key_name: Name = "/key/test".parse().unwrap();
621 let sign_fn = |region: &[u8]| -> Bytes {
622 let digest = ring::digest::digest(&ring::digest::SHA256, region);
623 Bytes::copy_from_slice(digest.as_ref())
624 };
625
626 struct NoopWaker;
628 impl Wake for NoopWaker {
629 fn wake(self: std::sync::Arc<Self>) {}
630 }
631 let waker = Waker::from(std::sync::Arc::new(NoopWaker));
632 let mut cx = Context::from_waker(&waker);
633
634 let fut = DataBuilder::new("/signed/data", b"payload")
635 .freshness(Duration::from_secs(10))
636 .sign(
637 SignatureType::SignatureEd25519,
638 Some(&key_name),
639 |region: &[u8]| {
640 let digest = ring::digest::digest(&ring::digest::SHA256, region);
641 std::future::ready(Bytes::copy_from_slice(digest.as_ref()))
642 },
643 );
644 let mut fut = pin!(fut);
645 let async_wire = match fut.as_mut().poll(&mut cx) {
646 std::task::Poll::Ready(b) => b,
647 std::task::Poll::Pending => panic!("should complete immediately"),
648 };
649
650 let sync_wire = DataBuilder::new("/signed/data", b"payload")
652 .freshness(Duration::from_secs(10))
653 .sign_sync(SignatureType::SignatureEd25519, Some(&key_name), sign_fn);
654
655 assert_eq!(
656 async_wire, sync_wire,
657 "sign_sync must produce identical wire format"
658 );
659 }
660
661 #[test]
662 fn data_builder_sign_sync_no_freshness() {
663 let wire = DataBuilder::new("/test", b"content").sign_sync(
664 SignatureType::SignatureEd25519,
665 None,
666 |region| {
667 let digest = ring::digest::digest(&ring::digest::SHA256, region);
668 Bytes::copy_from_slice(digest.as_ref())
669 },
670 );
671 let data = Data::decode(wire).unwrap();
672 assert_eq!(data.name.to_string(), "/test");
673 assert_eq!(
674 data.content().map(|b| b.as_ref()),
675 Some(b"content".as_ref())
676 );
677 assert!(data.meta_info().is_none());
678 let si = data.sig_info().expect("sig info");
679 assert_eq!(si.sig_type, SignatureType::SignatureEd25519);
680 }
681
682 #[test]
685 fn wire_data_builder_no_freshness_omits_metainfo() {
686 let wire = DataBuilder::new("/A", b"X").build();
687
688 assert_eq!(wire[0], 0x06);
689
690 assert_eq!(
692 wire[7], 0x15,
693 "Content should follow Name directly (no MetaInfo)"
694 );
695 }
696
697 #[test]
698 fn wire_data_builder_freshness_nni() {
699 let wire = DataBuilder::new("/A", b"X")
701 .freshness(Duration::from_secs(10))
702 .build();
703
704 let meta_pos = 7; assert_bytes_eq(
707 &wire[meta_pos..meta_pos + 6],
708 &[0x14, 0x04, 0x19, 0x02, 0x27, 0x10],
709 "MetaInfo with FreshnessPeriod=10000ms",
710 );
711 }
712
713 #[test]
714 fn wire_ed25519_sig_type() {
715 use std::pin::pin;
716 use std::task::{Context, Wake, Waker};
717
718 struct NoopWaker;
719 impl Wake for NoopWaker {
720 fn wake(self: std::sync::Arc<Self>) {}
721 }
722 let waker = Waker::from(std::sync::Arc::new(NoopWaker));
723 let mut cx = Context::from_waker(&waker);
724
725 let fut = DataBuilder::new("/A", b"X").sign(
726 SignatureType::SignatureEd25519,
727 None,
728 |_: &[u8]| std::future::ready(Bytes::from_static(&[0xFF; 64])),
729 );
730 let mut fut = pin!(fut);
731 let wire = match fut.as_mut().poll(&mut cx) {
732 std::task::Poll::Ready(b) => b,
733 std::task::Poll::Pending => panic!("should complete immediately"),
734 };
735
736 let sig_info_content = [0x1B, 0x01, 0x05];
738 assert!(
739 wire.windows(3).any(|w| w == sig_info_content),
740 "SignatureType=5 should be 1-byte NNI: 1B 01 05, got: {}",
741 hex(&wire),
742 );
743 }
744}