ndn_protocol/
interest.rs

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    /// Creates the `ParametersSha256DigestComponent` part of the name.
162    ///
163    /// The component will be automatically added to the name when signing the interest, so this is
164    /// only useful for unsigned interests.
165    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    /// Adds a `ParametersSha256DigestComponent` to the end of the name
176    ///
177    /// The component will be automatically added to the name when signing the interest, so this is
178    /// only useful for unsigned interests.
179    ///
180    /// Empty application parameters will be set if none are set currently.
181    /// Any existing `ParametersSha256DigestComponent` will be removed.
182    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    /// Adds a `ParametersSha256DigestComponent` to the end of the name, assuming application
197    /// parameters already exist
198    ///
199    /// The component will be automatically added to the name when signing the interest, so this is
200    /// only useful for unsigned interests.
201    ///
202    /// Any existing `ParametersSha256DigestComponent` will be removed.
203    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        // Delete existing params-sha256
274        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        // Generate nonce
282        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        // Generate sequence number
294        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        // Create signature
307        self.signature_value = Some(InterestSignatureValue::new(
308            sign_method.sign(&self.signable_portion()),
309        ));
310
311        // Add new params-sha256
312        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                // Not signed, application parameters present - check parameter digest
341                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                // No digest present
351                Err(VerifyError::InvalidParameterDigest)
352            } else {
353                // Not signed, no application parameters - nothing to check
354                Ok(())
355            }
356        }
357    }
358
359    /// Verify the interest with a given signature verifier
360    ///
361    /// Returns `Ok(())` if the signature and the `ParametersSha256DigestComponent` of the name are
362    /// valid
363    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            // Not signed
372            return Ok(());
373        }
374
375        let Some(ref sig_value) = self.signature_value else {
376            // Signature missing
377            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', //
563        ];
564
565        let app_params_plus = [
566            36, 0, // ApplicationParameters
567            44, 6, // SignatureInfo
568            27, 1, 0, // Signature Type
569            42, 1, 0, // seq num
570        ];
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}