1use bytes::{Buf, BufMut, Bytes, BytesMut};
10use derive_more::{AsMut, AsRef, Constructor, From, Into};
11use ndn_tlv::{find_tlv, NonNegativeInteger, Tlv, TlvDecode, TlvEncode, VarNum};
12use rand::{Rng, SeedableRng};
13use sha2::{Digest, Sha256};
14
15use crate::{
16 error::{SignError, VerifyError},
17 name::ParametersSha256DigestComponent,
18 signature::{
19 InterestSignatureInfo, InterestSignatureValue, SignMethod, SignatureNonce, SignatureSeqNum,
20 SignatureVerifier,
21 },
22 Name, NameComponent, SignatureType,
23};
24
25#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, Hash, Default)]
28#[tlv(33)]
29pub struct CanBePrefix;
30
31#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, Hash, Default)]
34#[tlv(18)]
35pub struct MustBeFresh;
36
37#[derive(Debug, Tlv, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Constructor)]
40#[tlv(30)]
41pub struct ForwardingHint {
42 name: Name,
43}
44
45#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, From, Into, AsRef, AsMut, Hash)]
47#[tlv(10)]
48pub struct Nonce {
49 nonce: [u8; 4],
50}
51
52#[derive(
55 Debug,
56 Tlv,
57 PartialEq,
58 Eq,
59 Clone,
60 Copy,
61 Constructor,
62 From,
63 Into,
64 AsRef,
65 AsMut,
66 Hash,
67 PartialOrd,
68 Ord,
69)]
70#[tlv(12)]
71pub struct InterestLifetime {
72 lifetime: NonNegativeInteger,
73}
74
75#[derive(
78 Debug,
79 Tlv,
80 PartialEq,
81 Eq,
82 Clone,
83 Copy,
84 Constructor,
85 From,
86 Into,
87 AsRef,
88 AsMut,
89 Hash,
90 PartialOrd,
91 Ord,
92)]
93#[tlv(34)]
94pub struct HopLimit {
95 limit: u8,
96}
97
98#[derive(Debug, Tlv, PartialEq, Eq, Hash, From, AsRef, AsMut, Constructor, Clone)]
104#[tlv(36)]
105pub struct ApplicationParameters<T> {
106 data: T,
107}
108
109#[derive(Debug, Tlv, PartialEq, Eq, Hash, Clone)]
117#[tlv(5)]
118pub struct Interest<T> {
119 pub(crate) name: Name,
120 can_be_prefix: Option<CanBePrefix>,
121 must_be_fresh: Option<MustBeFresh>,
122 forwarding_hint: Option<ForwardingHint>,
123 nonce: Option<Nonce>,
124 interest_lifetime: Option<InterestLifetime>,
125 hop_limit: Option<HopLimit>,
126 application_parameters: Option<ApplicationParameters<T>>,
127 signature_info: Option<InterestSignatureInfo>,
128 signature_value: Option<InterestSignatureValue>,
129}
130
131#[derive(Debug, PartialEq, Eq, Clone, Copy, Constructor, Hash)]
134pub struct SignSettings {
135 pub include_time: bool,
137 pub include_seq_num: bool,
139 pub nonce_length: usize,
141}
142
143impl Default for SignSettings {
144 fn default() -> Self {
145 Self {
146 include_time: true,
147 include_seq_num: false,
148 nonce_length: 8,
149 }
150 }
151}
152
153impl Interest<Bytes> {
154 pub fn decode_application_parameters<T>(self) -> Interest<T>
160 where
161 T: TlvDecode,
162 {
163 Interest {
164 application_parameters: self
165 .application_parameters
166 .and_then(|mut x| T::decode(&mut x.data).ok())
167 .map(ApplicationParameters::new),
168 name: self.name,
169 can_be_prefix: self.can_be_prefix,
170 must_be_fresh: self.must_be_fresh,
171 forwarding_hint: self.forwarding_hint,
172 nonce: self.nonce,
173 interest_lifetime: self.interest_lifetime,
174 hop_limit: self.hop_limit,
175 signature_info: self.signature_info,
176 signature_value: self.signature_value,
177 }
178 }
179}
180
181impl<AppParamTy> Interest<AppParamTy> {
182 pub fn remove_application_parameters(self) -> Interest<()> {
184 Interest {
185 application_parameters: None,
186 name: self.name,
187 can_be_prefix: self.can_be_prefix,
188 must_be_fresh: self.must_be_fresh,
189 forwarding_hint: self.forwarding_hint,
190 nonce: self.nonce,
191 interest_lifetime: self.interest_lifetime,
192 hop_limit: self.hop_limit,
193 signature_info: self.signature_info,
194 signature_value: self.signature_value,
195 }
196 }
197}
198
199impl<AppParamTy> Interest<AppParamTy>
200where
201 AppParamTy: TlvEncode,
202{
203 pub fn make_parameters_digest(data: AppParamTy) -> ParametersSha256DigestComponent {
208 let mut hasher = Sha256::new();
209 hasher.update(VarNum::from(ApplicationParameters::<AppParamTy>::TYP).encode());
210 hasher.update(VarNum::from(data.size()).encode());
211 hasher.update(&data.encode());
212 ParametersSha256DigestComponent {
213 name: hasher.finalize().into(),
214 }
215 }
216
217 pub fn add_parameters_digest(&mut self) -> &mut Self
225 where
226 AppParamTy: Default,
227 AppParamTy: Clone,
228 {
229 if self.application_parameters.is_none() {
230 self.application_parameters = Some(ApplicationParameters {
231 data: AppParamTy::default(),
232 });
233 }
234
235 self.add_parameters_digest_unchecked()
236 }
237
238 pub fn add_parameters_digest_unchecked(&mut self) -> &mut Self
246 where
247 AppParamTy: Clone,
248 {
249 self.name
250 .components
251 .retain(|x| !matches!(x, NameComponent::ParametersSha256DigestComponent(_)));
252
253 self.name
254 .components
255 .push(NameComponent::ParametersSha256DigestComponent(
256 Self::make_parameters_digest(
257 self.application_parameters.as_ref().unwrap().data.clone(),
258 ),
259 ));
260 self
261 }
262
263 fn signable_portion(&self) -> Bytes {
264 let mut bytes = self.encode();
265 let _ = VarNum::decode(&mut bytes);
266 let _ = VarNum::decode(&mut bytes);
267 let _ = find_tlv::<ApplicationParameters<AppParamTy>>(&mut bytes, false);
268
269 let mut end = bytes.clone();
270 let _ = find_tlv::<InterestSignatureValue>(&mut end, false);
271 bytes.truncate(bytes.remaining() - end.remaining());
272
273 let mut signature_buffer =
274 BytesMut::with_capacity(self.name.inner_size() + bytes.remaining());
275 for component in &self.name.components {
276 if !matches!(component, NameComponent::ParametersSha256DigestComponent(_)) {
277 signature_buffer.put(component.encode());
278 }
279 }
280 signature_buffer.put(&mut bytes);
281 signature_buffer.freeze()
282 }
283
284 fn parameters_digest(&self) -> [u8; 32] {
285 let mut data = self.encode();
286 let _ = VarNum::decode(&mut data);
287 let _ = VarNum::decode(&mut data);
288 let _ = find_tlv::<ApplicationParameters<AppParamTy>>(&mut data, false);
289 let mut hasher = Sha256::new();
290 hasher.update(&data);
291 hasher.finalize().into()
292 }
293
294 pub fn sign<T>(&mut self, sign_method: &mut T, settings: SignSettings)
297 where
298 T: SignMethod,
299 AppParamTy: Default,
300 {
301 if self.application_parameters.is_none() {
302 self.set_application_parameters(Some(AppParamTy::default()));
303 }
304
305 self.sign_checked(sign_method, settings)
306 .expect("sign_checked failed from sign")
307 }
308
309 pub fn sign_checked<T>(
316 &mut self,
317 sign_method: &mut T,
318 settings: SignSettings,
319 ) -> Result<(), SignError>
320 where
321 T: SignMethod,
322 {
323 self.name
325 .components
326 .retain(|x| !matches!(x, NameComponent::ParametersSha256DigestComponent(_)));
327 if self.application_parameters.is_none() {
328 return Err(SignError::MissingApplicationParameters);
329 }
330
331 let nonce = if settings.nonce_length > 0 {
333 let mut rng = rand::rngs::StdRng::from_entropy();
334 let mut data = BytesMut::with_capacity(settings.nonce_length);
335 for _ in 0..settings.nonce_length {
336 data.put_u8(rng.gen());
337 }
338 Some(SignatureNonce::new(data.freeze()))
339 } else {
340 None
341 };
342
343 let seq_num = sign_method.next_seq_num();
345
346 self.signature_info = Some(InterestSignatureInfo {
347 signature_type: SignatureType::new(sign_method.signature_type().into()),
348 key_locator: sign_method.certificate().map(|x| x.name_locator()),
349 nonce,
350 time: settings.include_time.then(|| sign_method.time()),
351 seq_num: settings
352 .include_seq_num
353 .then(|| SignatureSeqNum::new(seq_num.into())),
354 });
355
356 self.signature_value = Some(InterestSignatureValue::new(
358 sign_method.sign(&self.signable_portion()),
359 ));
360
361 self.name
363 .components
364 .push(NameComponent::ParametersSha256DigestComponent(
365 ParametersSha256DigestComponent {
366 name: self.parameters_digest(),
367 },
368 ));
369 Ok(())
370 }
371
372 fn verify_param_digest(&self) -> Result<(), VerifyError> {
373 if self.is_signed() {
374 if self.application_parameters.is_none() {
375 return Err(VerifyError::MissingApplicationParameters);
376 }
377
378 let Some(NameComponent::ParametersSha256DigestComponent(param_digest)) =
379 self.name.components.last()
380 else {
381 return Err(VerifyError::InvalidParameterDigest);
382 };
383
384 if param_digest.name != self.parameters_digest() {
385 return Err(VerifyError::InvalidParameterDigest);
386 }
387 Ok(())
388 } else {
389 if self.application_parameters.is_some() {
390 for component in &self.name.components {
392 if let NameComponent::ParametersSha256DigestComponent(component) = component {
393 if component.name == self.parameters_digest() {
394 return Ok(());
395 } else {
396 return Err(VerifyError::InvalidParameterDigest);
397 }
398 }
399 }
400 Err(VerifyError::InvalidParameterDigest)
402 } else {
403 Ok(())
405 }
406 }
407 }
408
409 pub fn verify<T>(&self, verifier: &T) -> Result<(), VerifyError>
414 where
415 T: SignatureVerifier,
416 T: ?Sized,
417 {
418 self.verify_param_digest()?;
419
420 if self.signature_info.is_none() {
421 return Ok(());
423 }
424
425 let Some(ref sig_value) = self.signature_value else {
426 return Err(VerifyError::InvalidSignature);
428 };
429
430 verifier
431 .verify(&self.signable_portion(), sig_value.as_ref())
432 .then_some(())
433 .ok_or(VerifyError::InvalidSignature)
434 }
435
436 pub fn encode_application_parameters(self) -> Interest<Bytes> {
439 Interest {
440 name: self.name,
441 can_be_prefix: self.can_be_prefix,
442 must_be_fresh: self.must_be_fresh,
443 forwarding_hint: self.forwarding_hint,
444 nonce: self.nonce,
445 interest_lifetime: self.interest_lifetime,
446 hop_limit: self.hop_limit,
447 application_parameters: self.application_parameters.map(|params| {
448 ApplicationParameters {
449 data: params.data.encode(),
450 }
451 }),
452 signature_info: self.signature_info,
453 signature_value: self.signature_value,
454 }
455 }
456}
457
458impl Interest<()> {
459 pub fn new_u(name: Name) -> Self {
462 Self::new(name)
463 }
464}
465
466impl<AppParamTy> Interest<AppParamTy> {
467 pub fn new(name: Name) -> Self {
469 Self {
470 name,
471 can_be_prefix: None,
472 must_be_fresh: None,
473 forwarding_hint: None,
474 nonce: None,
475 interest_lifetime: None,
476 hop_limit: None,
477 application_parameters: None,
478 signature_info: None,
479 signature_value: None,
480 }
481 }
482
483 pub fn set_name(&mut self, name: Name) -> &mut Self {
485 self.name = name;
486 self
487 }
488
489 pub fn name(&self) -> &Name {
491 &self.name
492 }
493
494 pub fn set_can_be_prefix(&mut self, can_be_prefix: bool) -> &mut Self {
497 self.can_be_prefix = can_be_prefix.then_some(CanBePrefix);
498 self
499 }
500
501 pub fn can_be_prefix(&self) -> bool {
503 self.can_be_prefix.is_some()
504 }
505
506 pub fn set_must_be_fresh(&mut self, must_be_fresh: bool) -> &mut Self {
508 self.must_be_fresh = must_be_fresh.then_some(MustBeFresh);
509 self
510 }
511
512 pub fn must_be_fresh(&self) -> bool {
514 self.must_be_fresh.is_some()
515 }
516
517 pub fn set_forwarding_hint(&mut self, forwarding_hint: Option<Name>) -> &mut Self {
519 self.forwarding_hint = forwarding_hint.map(|name| ForwardingHint { name });
520 self
521 }
522
523 pub fn forwarding_hint(&self) -> Option<&Name> {
525 self.forwarding_hint.as_ref().map(|x| &x.name)
526 }
527
528 pub fn set_nonce(&mut self, nonce: Option<[u8; 4]>) -> &mut Self {
530 self.nonce = nonce.map(|nonce| Nonce { nonce });
531 self
532 }
533
534 pub fn nonce(&self) -> Option<&[u8; 4]> {
536 self.nonce.as_ref().map(|x| &x.nonce)
537 }
538
539 pub fn set_interest_lifetime(
542 &mut self,
543 interest_lifetime: Option<NonNegativeInteger>,
544 ) -> &mut Self {
545 self.interest_lifetime = interest_lifetime.map(|lifetime| InterestLifetime { lifetime });
546 self
547 }
548
549 pub fn interest_lifetime(&self) -> Option<NonNegativeInteger> {
551 self.interest_lifetime.as_ref().map(|x| x.lifetime)
552 }
553
554 pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) -> &mut Self {
556 self.hop_limit = hop_limit.map(|limit| HopLimit { limit });
557 self
558 }
559
560 pub fn hop_limit(&self) -> Option<u8> {
562 self.hop_limit.as_ref().map(|x| x.limit)
563 }
564
565 pub fn set_application_parameters(&mut self, params: Option<AppParamTy>) -> &mut Self {
567 self.application_parameters = params.map(|data| ApplicationParameters { data });
568 self
569 }
570
571 pub fn application_parameters(&self) -> Option<&AppParamTy> {
573 self.application_parameters.as_ref().map(|x| &x.data)
574 }
575
576 pub fn signature_info(&self) -> Option<&InterestSignatureInfo> {
578 self.signature_info.as_ref()
579 }
580
581 pub fn is_signed(&self) -> bool {
583 self.signature_info.is_some()
584 }
585}
586
587#[cfg(test)]
588mod tests {
589 use base64::Engine;
590
591 use crate::{signature::DigestSha256, RsaCertificate, SafeBag, SignatureSha256WithRsa};
592
593 use super::*;
594
595 #[test]
596 fn simple_usage() {
597 let mut interest = Interest::<()>::new(Name::from_str("ndn:/hello/world").unwrap());
598 interest
599 .set_can_be_prefix(true)
600 .set_hop_limit(Some(20))
601 .set_interest_lifetime(Some(10_000u16.into()));
602
603 assert_eq!(
604 interest,
605 Interest {
606 name: Name::from_str("ndn:/hello/world").unwrap(),
607 can_be_prefix: Some(CanBePrefix),
608 must_be_fresh: None,
609 forwarding_hint: None,
610 nonce: None,
611 interest_lifetime: Some(InterestLifetime {
612 lifetime: 10_000u16.into()
613 }),
614 hop_limit: Some(HopLimit { limit: 20 }),
615 application_parameters: None,
616 signature_info: None,
617 signature_value: None,
618 }
619 );
620 }
621
622 #[test]
623 fn sha256_interest() {
624 let mut interest = Interest::<()>::new(Name::from_str("ndn:/hello/world").unwrap());
625 let mut signer = DigestSha256::new();
626 interest.sign(
627 &mut signer,
628 SignSettings {
629 include_time: false,
630 nonce_length: 0,
631 include_seq_num: true,
632 },
633 );
634 assert!(interest.verify(&mut signer).is_ok());
635
636 let name_components = [
637 8, 5, b'h', b'e', b'l', b'l', b'o', 8, 5, b'w', b'o', b'r', b'l', b'd', ];
639
640 let app_params_plus = [
641 36, 0, 44, 6, 27, 1, 0, 42, 1, 0, ];
646
647 let mut hasher = Sha256::new();
648 hasher.update(name_components);
649 hasher.update(app_params_plus);
650 let signature = hasher.finalize();
651
652 hasher = Sha256::new();
653 hasher.update(app_params_plus);
654 hasher.update([46, 32]);
655 hasher.update(signature);
656 let param_digest = hasher.finalize();
657
658 let mut full_record = Vec::new();
659 full_record.extend([5, 94]);
660 full_record.extend([7, 48]);
661 full_record.extend(name_components);
662 full_record.extend([2, 32]);
663 full_record.extend(param_digest);
664 full_record.extend(app_params_plus);
665 full_record.extend([46, 32]);
666 full_record.extend(signature);
667
668 assert_eq!(<Vec<u8>>::from(interest.encode()), full_record);
669 }
670
671 #[test]
672 fn rsa_interest() {
673 const SAFEBAG: &[u8] = b"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\
707g==";
708
709 let safebag_data = base64::engine::general_purpose::STANDARD
710 .decode(SAFEBAG)
711 .unwrap();
712 let safebag = SafeBag::decode(&mut Bytes::from(safebag_data)).unwrap();
713
714 let cert = RsaCertificate::from_safebag(safebag, "test").unwrap();
715 let mut signer = SignatureSha256WithRsa::new(cert.clone());
716
717 let mut interest = Interest::<()>::new(Name::from_str("ndn:/test/test/asd").unwrap());
718 interest.sign(&mut signer, SignSettings::default());
719
720 assert!(interest.verify(&signer).is_ok());
721 }
722}