1use bytes::{Buf, BufMut, Bytes, BytesMut};
2use derive_more::{AsMut, AsRef, Constructor, From, Into};
3use ndn_tlv::{find_tlv, NonNegativeInteger, Tlv, TlvDecode, TlvEncode, VarNum};
4use rand::{Rng, SeedableRng};
5use sha2::{Digest, Sha256};
6
7use crate::{
8 error::{SignError, VerifyError},
9 name::ParametersSha256DigestComponent,
10 signature::{
11 InterestSignatureInfo, InterestSignatureValue, SignMethod, SignatureNonce, SignatureSeqNum,
12 SignatureVerifier,
13 },
14 Name, NameComponent, SignatureType,
15};
16
17#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, Hash, Default)]
18#[tlv(33)]
19pub struct CanBePrefix;
20
21#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, Hash, Default)]
22#[tlv(18)]
23pub struct MustBeFresh;
24
25#[derive(Debug, Tlv, PartialEq, Eq, Clone, PartialOrd, Ord, Hash, Constructor)]
26#[tlv(30)]
27pub struct ForwardingHint {
28 name: Name,
29}
30
31#[derive(Debug, Tlv, PartialEq, Eq, Clone, Copy, Constructor, From, Into, AsRef, AsMut, Hash)]
32#[tlv(10)]
33pub struct Nonce {
34 nonce: [u8; 4],
35}
36
37#[derive(
38 Debug,
39 Tlv,
40 PartialEq,
41 Eq,
42 Clone,
43 Copy,
44 Constructor,
45 From,
46 Into,
47 AsRef,
48 AsMut,
49 Hash,
50 PartialOrd,
51 Ord,
52)]
53#[tlv(12)]
54pub struct InterestLifetime {
55 lifetime: NonNegativeInteger,
56}
57
58#[derive(
59 Debug,
60 Tlv,
61 PartialEq,
62 Eq,
63 Clone,
64 Copy,
65 Constructor,
66 From,
67 Into,
68 AsRef,
69 AsMut,
70 Hash,
71 PartialOrd,
72 Ord,
73)]
74#[tlv(34)]
75pub struct HopLimit {
76 limit: u8,
77}
78
79#[derive(Debug, Tlv, PartialEq, Eq, Hash, From, AsRef, AsMut, Constructor, Clone)]
80#[tlv(36)]
81pub struct ApplicationParameters<T> {
82 data: T,
83}
84
85#[derive(Debug, Tlv, PartialEq, Eq, Hash, Clone)]
86#[tlv(5)]
87pub struct Interest<T> {
88 pub(crate) name: Name,
89 can_be_prefix: Option<CanBePrefix>,
90 must_be_fresh: Option<MustBeFresh>,
91 forwarding_hint: Option<ForwardingHint>,
92 nonce: Option<Nonce>,
93 interest_lifetime: Option<InterestLifetime>,
94 hop_limit: Option<HopLimit>,
95 application_parameters: Option<ApplicationParameters<T>>,
96 signature_info: Option<InterestSignatureInfo>,
97 signature_value: Option<InterestSignatureValue>,
98}
99
100#[derive(Debug, PartialEq, Eq, Clone, Copy, Constructor, Hash)]
101pub struct SignSettings {
102 pub include_time: bool,
103 pub include_seq_num: bool,
104 pub nonce_length: usize,
105}
106
107impl Default for SignSettings {
108 fn default() -> Self {
109 Self {
110 include_time: true,
111 include_seq_num: false,
112 nonce_length: 8,
113 }
114 }
115}
116
117impl Interest<Bytes> {
118 pub fn decode_application_parameters<T>(self) -> Interest<T>
119 where
120 T: TlvDecode,
121 {
122 Interest {
123 application_parameters: self
124 .application_parameters
125 .and_then(|mut x| T::decode(&mut x.data).ok())
126 .map(ApplicationParameters::new),
127 name: self.name,
128 can_be_prefix: self.can_be_prefix,
129 must_be_fresh: self.must_be_fresh,
130 forwarding_hint: self.forwarding_hint,
131 nonce: self.nonce,
132 interest_lifetime: self.interest_lifetime,
133 hop_limit: self.hop_limit,
134 signature_info: self.signature_info,
135 signature_value: self.signature_value,
136 }
137 }
138}
139
140impl<AppParamTy> Interest<AppParamTy> {
141 pub fn remove_application_parameters(self) -> Interest<()> {
142 Interest {
143 application_parameters: None,
144 name: self.name,
145 can_be_prefix: self.can_be_prefix,
146 must_be_fresh: self.must_be_fresh,
147 forwarding_hint: self.forwarding_hint,
148 nonce: self.nonce,
149 interest_lifetime: self.interest_lifetime,
150 hop_limit: self.hop_limit,
151 signature_info: self.signature_info,
152 signature_value: self.signature_value,
153 }
154 }
155}
156
157impl<AppParamTy> Interest<AppParamTy>
158where
159 AppParamTy: TlvEncode,
160{
161 pub fn make_parameters_digest(data: AppParamTy) -> ParametersSha256DigestComponent {
166 let mut hasher = Sha256::new();
167 hasher.update(VarNum::from(ApplicationParameters::<AppParamTy>::TYP).encode());
168 hasher.update(VarNum::from(data.size()).encode());
169 hasher.update(&data.encode());
170 ParametersSha256DigestComponent {
171 name: hasher.finalize().into(),
172 }
173 }
174
175 pub fn add_parameters_digest(&mut self) -> &mut Self
183 where
184 AppParamTy: Default,
185 AppParamTy: Clone,
186 {
187 if self.application_parameters.is_none() {
188 self.application_parameters = Some(ApplicationParameters {
189 data: AppParamTy::default(),
190 });
191 }
192
193 self.add_parameters_digest_unchecked()
194 }
195
196 pub fn add_parameters_digest_unchecked(&mut self) -> &mut Self
204 where
205 AppParamTy: Clone,
206 {
207 self.name
208 .components
209 .retain(|x| !matches!(x, NameComponent::ParametersSha256DigestComponent(_)));
210
211 self.name
212 .components
213 .push(NameComponent::ParametersSha256DigestComponent(
214 Self::make_parameters_digest(
215 self.application_parameters.as_ref().unwrap().data.clone(),
216 ),
217 ));
218 self
219 }
220
221 fn signable_portion(&self) -> Bytes {
222 let mut bytes = self.encode();
223 let _ = VarNum::decode(&mut bytes);
224 let _ = VarNum::decode(&mut bytes);
225 let _ = find_tlv::<ApplicationParameters<AppParamTy>>(&mut bytes, false);
226
227 let mut end = bytes.clone();
228 let _ = find_tlv::<InterestSignatureValue>(&mut end, false);
229 bytes.truncate(bytes.remaining() - end.remaining());
230
231 let mut signature_buffer =
232 BytesMut::with_capacity(self.name.inner_size() + bytes.remaining());
233 for component in &self.name.components {
234 if !matches!(component, NameComponent::ParametersSha256DigestComponent(_)) {
235 signature_buffer.put(component.encode());
236 }
237 }
238 signature_buffer.put(&mut bytes);
239 signature_buffer.freeze()
240 }
241
242 fn parameters_digest(&self) -> [u8; 32] {
243 let mut data = self.encode();
244 let _ = VarNum::decode(&mut data);
245 let _ = VarNum::decode(&mut data);
246 let _ = find_tlv::<ApplicationParameters<AppParamTy>>(&mut data, false);
247 let mut hasher = Sha256::new();
248 hasher.update(&data);
249 hasher.finalize().into()
250 }
251
252 pub fn sign<T>(&mut self, sign_method: &mut T, settings: SignSettings)
253 where
254 T: SignMethod,
255 AppParamTy: Default,
256 {
257 if self.application_parameters.is_none() {
258 self.set_application_parameters(Some(AppParamTy::default()));
259 }
260
261 self.sign_checked(sign_method, settings)
262 .expect("sign_checked failed from sign")
263 }
264
265 pub fn sign_checked<T>(
266 &mut self,
267 sign_method: &mut T,
268 settings: SignSettings,
269 ) -> Result<(), SignError>
270 where
271 T: SignMethod,
272 {
273 self.name
275 .components
276 .retain(|x| !matches!(x, NameComponent::ParametersSha256DigestComponent(_)));
277 if self.application_parameters.is_none() {
278 return Err(SignError::MissingApplicationParameters);
279 }
280
281 let nonce = if settings.nonce_length > 0 {
283 let mut rng = rand::rngs::StdRng::from_entropy();
284 let mut data = BytesMut::with_capacity(settings.nonce_length);
285 for _ in 0..settings.nonce_length {
286 data.put_u8(rng.gen());
287 }
288 Some(SignatureNonce::new(data.freeze()))
289 } else {
290 None
291 };
292
293 let seq_num = sign_method.next_seq_num();
295
296 self.signature_info = Some(InterestSignatureInfo {
297 signature_type: SignatureType::new(sign_method.signature_type().into()),
298 key_locator: sign_method.certificate().map(|x| x.name_locator()),
299 nonce,
300 time: settings.include_time.then(|| sign_method.time()),
301 seq_num: settings
302 .include_seq_num
303 .then(|| SignatureSeqNum::new(seq_num.into())),
304 });
305
306 self.signature_value = Some(InterestSignatureValue::new(
308 sign_method.sign(&self.signable_portion()),
309 ));
310
311 self.name
313 .components
314 .push(NameComponent::ParametersSha256DigestComponent(
315 ParametersSha256DigestComponent {
316 name: self.parameters_digest(),
317 },
318 ));
319 Ok(())
320 }
321
322 fn verify_param_digest(&self) -> Result<(), VerifyError> {
323 if self.is_signed() {
324 if self.application_parameters.is_none() {
325 return Err(VerifyError::MissingApplicationParameters);
326 }
327
328 let Some(NameComponent::ParametersSha256DigestComponent(param_digest)) =
329 self.name.components.last()
330 else {
331 return Err(VerifyError::InvalidParameterDigest);
332 };
333
334 if param_digest.name != self.parameters_digest() {
335 return Err(VerifyError::InvalidParameterDigest);
336 }
337 Ok(())
338 } else {
339 if self.application_parameters.is_some() {
340 for component in &self.name.components {
342 if let NameComponent::ParametersSha256DigestComponent(component) = component {
343 if component.name == self.parameters_digest() {
344 return Ok(());
345 } else {
346 return Err(VerifyError::InvalidParameterDigest);
347 }
348 }
349 }
350 Err(VerifyError::InvalidParameterDigest)
352 } else {
353 Ok(())
355 }
356 }
357 }
358
359 pub fn verify<T>(&self, verifier: &T) -> Result<(), VerifyError>
364 where
365 T: SignatureVerifier,
366 T: ?Sized,
367 {
368 self.verify_param_digest()?;
369
370 if self.signature_info.is_none() {
371 return Ok(());
373 }
374
375 let Some(ref sig_value) = self.signature_value else {
376 return Err(VerifyError::InvalidSignature);
378 };
379
380 verifier
381 .verify(&self.signable_portion(), sig_value.as_ref())
382 .then_some(())
383 .ok_or(VerifyError::InvalidSignature)
384 }
385
386 pub fn encode_application_parameters(self) -> Interest<Bytes> {
387 Interest {
388 name: self.name,
389 can_be_prefix: self.can_be_prefix,
390 must_be_fresh: self.must_be_fresh,
391 forwarding_hint: self.forwarding_hint,
392 nonce: self.nonce,
393 interest_lifetime: self.interest_lifetime,
394 hop_limit: self.hop_limit,
395 application_parameters: self.application_parameters.map(|params| {
396 ApplicationParameters {
397 data: params.data.encode(),
398 }
399 }),
400 signature_info: self.signature_info,
401 signature_value: self.signature_value,
402 }
403 }
404}
405
406impl Interest<()> {
407 pub fn new_u(name: Name) -> Self {
408 Self::new(name)
409 }
410}
411
412impl<AppParamTy> Interest<AppParamTy> {
413 pub fn new(name: Name) -> Self {
414 Self {
415 name,
416 can_be_prefix: None,
417 must_be_fresh: None,
418 forwarding_hint: None,
419 nonce: None,
420 interest_lifetime: None,
421 hop_limit: None,
422 application_parameters: None,
423 signature_info: None,
424 signature_value: None,
425 }
426 }
427
428 pub fn set_name(&mut self, name: Name) -> &mut Self {
429 self.name = name;
430 self
431 }
432
433 pub fn name(&self) -> &Name {
434 &self.name
435 }
436
437 pub fn set_can_be_prefix(&mut self, can_be_prefix: bool) -> &mut Self {
438 self.can_be_prefix = can_be_prefix.then_some(CanBePrefix);
439 self
440 }
441
442 pub fn can_be_prefix(&self) -> bool {
443 self.can_be_prefix.is_some()
444 }
445
446 pub fn set_must_be_fresh(&mut self, must_be_fresh: bool) -> &mut Self {
447 self.must_be_fresh = must_be_fresh.then_some(MustBeFresh);
448 self
449 }
450
451 pub fn must_be_fresh(&self) -> bool {
452 self.must_be_fresh.is_some()
453 }
454
455 pub fn set_forwarding_hint(&mut self, forwarding_hint: Option<Name>) -> &mut Self {
456 self.forwarding_hint = forwarding_hint.map(|name| ForwardingHint { name });
457 self
458 }
459
460 pub fn forwarding_hint(&self) -> Option<&Name> {
461 self.forwarding_hint.as_ref().map(|x| &x.name)
462 }
463
464 pub fn set_nonce(&mut self, nonce: Option<[u8; 4]>) -> &mut Self {
465 self.nonce = nonce.map(|nonce| Nonce { nonce });
466 self
467 }
468
469 pub fn nonce(&self) -> Option<&[u8; 4]> {
470 self.nonce.as_ref().map(|x| &x.nonce)
471 }
472
473 pub fn set_interest_lifetime(
474 &mut self,
475 interest_lifetime: Option<NonNegativeInteger>,
476 ) -> &mut Self {
477 self.interest_lifetime = interest_lifetime.map(|lifetime| InterestLifetime { lifetime });
478 self
479 }
480
481 pub fn interest_lifetime(&self) -> Option<NonNegativeInteger> {
482 self.interest_lifetime.as_ref().map(|x| x.lifetime)
483 }
484
485 pub fn set_hop_limit(&mut self, hop_limit: Option<u8>) -> &mut Self {
486 self.hop_limit = hop_limit.map(|limit| HopLimit { limit });
487 self
488 }
489
490 pub fn hop_limit(&self) -> Option<u8> {
491 self.hop_limit.as_ref().map(|x| x.limit)
492 }
493
494 pub fn set_application_parameters(&mut self, params: Option<AppParamTy>) -> &mut Self {
495 self.application_parameters = params.map(|data| ApplicationParameters { data });
496 self
497 }
498
499 pub fn application_parameters(&self) -> Option<&AppParamTy> {
500 self.application_parameters.as_ref().map(|x| &x.data)
501 }
502
503 pub fn signature_info(&self) -> Option<&InterestSignatureInfo> {
504 self.signature_info.as_ref()
505 }
506
507 pub fn is_signed(&self) -> bool {
508 self.signature_info.is_some()
509 }
510}
511
512#[cfg(test)]
513mod tests {
514 use base64::Engine;
515
516 use crate::{signature::DigestSha256, RsaCertificate, SafeBag, SignatureSha256WithRsa};
517
518 use super::*;
519
520 #[test]
521 fn simple_usage() {
522 let mut interest = Interest::<()>::new(Name::from_str("ndn:/hello/world").unwrap());
523 interest
524 .set_can_be_prefix(true)
525 .set_hop_limit(Some(20))
526 .set_interest_lifetime(Some(10_000u16.into()));
527
528 assert_eq!(
529 interest,
530 Interest {
531 name: Name::from_str("ndn:/hello/world").unwrap(),
532 can_be_prefix: Some(CanBePrefix),
533 must_be_fresh: None,
534 forwarding_hint: None,
535 nonce: None,
536 interest_lifetime: Some(InterestLifetime {
537 lifetime: 10_000u16.into()
538 }),
539 hop_limit: Some(HopLimit { limit: 20 }),
540 application_parameters: None,
541 signature_info: None,
542 signature_value: None,
543 }
544 );
545 }
546
547 #[test]
548 fn sha256_interest() {
549 let mut interest = Interest::<()>::new(Name::from_str("ndn:/hello/world").unwrap());
550 let mut signer = DigestSha256::new();
551 interest.sign(
552 &mut signer,
553 SignSettings {
554 include_time: false,
555 nonce_length: 0,
556 include_seq_num: true,
557 },
558 );
559 assert!(interest.verify(&mut signer).is_ok());
560
561 let name_components = [
562 8, 5, b'h', b'e', b'l', b'l', b'o', 8, 5, b'w', b'o', b'r', b'l', b'd', ];
564
565 let app_params_plus = [
566 36, 0, 44, 6, 27, 1, 0, 42, 1, 0, ];
571
572 let mut hasher = Sha256::new();
573 hasher.update(name_components);
574 hasher.update(app_params_plus);
575 let signature = hasher.finalize();
576
577 hasher = Sha256::new();
578 hasher.update(app_params_plus);
579 hasher.update([46, 32]);
580 hasher.update(signature);
581 let param_digest = hasher.finalize();
582
583 let mut full_record = Vec::new();
584 full_record.extend([5, 94]);
585 full_record.extend([7, 48]);
586 full_record.extend(name_components);
587 full_record.extend([2, 32]);
588 full_record.extend(param_digest);
589 full_record.extend(app_params_plus);
590 full_record.extend([46, 32]);
591 full_record.extend(signature);
592
593 assert_eq!(<Vec<u8>>::from(interest.encode()), full_record);
594 }
595
596 #[test]
597 fn rsa_interest() {
598 const SAFEBAG: &[u8] = b"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\
632g==";
633
634 let safebag_data = base64::engine::general_purpose::STANDARD
635 .decode(SAFEBAG)
636 .unwrap();
637 let safebag = SafeBag::decode(&mut Bytes::from(safebag_data)).unwrap();
638
639 let cert = RsaCertificate::from_safebag(safebag, "test").unwrap();
640 let mut signer = SignatureSha256WithRsa::new(cert.clone());
641
642 let mut interest = Interest::<()>::new(Name::from_str("ndn:/test/test/asd").unwrap());
643 interest.sign(&mut signer, SignSettings::default());
644
645 assert!(interest.verify(&signer).is_ok());
646 }
647}