ndn_protocol/
data.rs

1use bytes::{Buf, Bytes};
2use derive_more::{AsMut, AsRef, Display, From, Into};
3use ndn_tlv::{find_tlv, NonNegativeInteger, Tlv, TlvDecode, TlvEncode, VarNum};
4use sha2::{Digest, Sha256};
5
6use crate::{
7    error::VerifyError,
8    signature::{SignMethod, SignatureVerifier, ValidityPeriod},
9    Interest, Name, NameComponent, SignatureInfo, SignatureType, SignatureValue,
10};
11
12#[derive(Debug, Tlv, PartialEq, Eq, Clone, Hash, Display, Default, From, Into, AsRef, AsMut)]
13#[tlv(24)]
14#[display(fmt = "{}", content_type)]
15pub struct ContentType {
16    pub content_type: NonNegativeInteger,
17}
18
19#[derive(
20    Debug,
21    Tlv,
22    PartialEq,
23    Eq,
24    Clone,
25    Hash,
26    PartialOrd,
27    Ord,
28    Display,
29    Default,
30    From,
31    Into,
32    AsRef,
33    AsMut,
34)]
35#[tlv(25)]
36#[display(fmt = "{}", freshness_period)]
37pub struct FreshnessPeriod {
38    pub freshness_period: NonNegativeInteger,
39}
40
41#[derive(Debug, Tlv, PartialEq, Eq, Clone, Hash, From, Into, AsRef, AsMut)]
42#[tlv(26)]
43pub struct FinalBlockId {
44    pub final_block_id: NameComponent,
45}
46
47#[derive(Debug, Tlv, PartialEq, Eq, Clone, Hash, Default, From, AsRef, AsMut)]
48#[tlv(21)]
49pub struct Content<T> {
50    pub data: T,
51}
52
53#[derive(Debug, Tlv, PartialEq, Eq, Default, Clone, Hash)]
54#[tlv(20)]
55pub struct MetaInfo {
56    pub content_type: Option<ContentType>,
57    pub freshness_period: Option<FreshnessPeriod>,
58    pub final_block_id: Option<FinalBlockId>,
59}
60
61#[derive(Debug, Tlv, PartialEq, Eq, Clone, Hash)]
62#[tlv(6)]
63pub struct Data<T> {
64    name: Name,
65    meta_info: Option<MetaInfo>,
66    content: Option<Content<T>>,
67    signature_info: Option<SignatureInfo>,
68    signature_value: Option<SignatureValue>,
69}
70
71impl ContentType {
72    pub const BLOB: Self = Self::new(0);
73    pub const LINK: Self = Self::new(1);
74    pub const KEY: Self = Self::new(2);
75    pub const NACK: Self = Self::new(3);
76
77    pub const fn new(typ: u64) -> Self {
78        Self {
79            content_type: NonNegativeInteger::new(typ),
80        }
81    }
82}
83
84impl FreshnessPeriod {
85    pub fn new(period: u64) -> Self {
86        Self {
87            freshness_period: NonNegativeInteger::new(period),
88        }
89    }
90}
91
92impl Data<Bytes> {
93    pub fn decode_content<U>(self) -> Data<U>
94    where
95        U: TlvDecode,
96    {
97        Data {
98            content: self
99                .content
100                .and_then(|mut x| U::decode(&mut x.data).ok())
101                .map(|data| Content { data }),
102            name: self.name,
103            meta_info: self.meta_info,
104            signature_info: self.signature_info,
105            signature_value: self.signature_value,
106        }
107    }
108}
109
110impl<T> Data<T>
111where
112    T: TlvEncode,
113{
114    pub const fn new(name: Name, content: T) -> Self {
115        Data {
116            name,
117            meta_info: Some(MetaInfo {
118                content_type: Some(ContentType {
119                    content_type: NonNegativeInteger::U8(0),
120                }),
121                freshness_period: None,
122                final_block_id: None,
123            }),
124            content: Some(Content { data: content }),
125            signature_info: None,
126            signature_value: None,
127        }
128    }
129
130    pub fn name(&self) -> &Name {
131        &self.name
132    }
133
134    pub fn set_name(&mut self, name: Name) -> &mut Self {
135        self.name = name;
136        self
137    }
138
139    pub fn meta_info(&self) -> &Option<MetaInfo> {
140        &self.meta_info
141    }
142
143    pub fn set_meta_info(&mut self, meta_info: Option<MetaInfo>) -> &mut Self {
144        self.meta_info = meta_info;
145        self
146    }
147
148    pub fn content(&self) -> Option<&T> {
149        self.content.as_ref().map(|x| &x.data)
150    }
151
152    pub fn set_content(&mut self, content: Option<T>) -> &mut Self {
153        self.content = content.map(|data| Content { data });
154        self
155    }
156
157    pub fn encode_content(self) -> Data<Bytes> {
158        Data {
159            name: self.name,
160            meta_info: self.meta_info,
161            content: self.content.map(|x| Content {
162                data: x.data.encode(),
163            }),
164            signature_info: self.signature_info,
165            signature_value: self.signature_value,
166        }
167    }
168
169    fn signable_portion(&self) -> Bytes {
170        let mut data = self.encode();
171        let _ = VarNum::decode(&mut data);
172        let _ = VarNum::decode(&mut data);
173
174        let mut end = data.clone();
175        let _ = find_tlv::<SignatureValue>(&mut end, false);
176
177        data.truncate(data.len() - end.remaining());
178        data
179    }
180
181    fn sign_internal<S>(&mut self, sign_method: &S, signature_info: SignatureInfo)
182    where
183        S: SignMethod,
184    {
185        self.signature_info = Some(signature_info);
186
187        let mut signed_portion = self.encode();
188
189        // Skip TLV-Type and TLV-Length
190        let _ = VarNum::decode(&mut signed_portion);
191        let _ = VarNum::decode(&mut signed_portion);
192
193        let signature = sign_method.sign(&signed_portion);
194        self.signature_value = Some(SignatureValue::new(signature));
195    }
196
197    pub fn sign<S>(&mut self, sign_method: &mut S)
198    where
199        S: SignMethod,
200    {
201        self.sign_internal(
202            sign_method,
203            SignatureInfo::new(
204                SignatureType::new(VarNum::from(sign_method.signature_type())),
205                sign_method.certificate().map(|x| x.name_locator()),
206                None,
207            ),
208        )
209    }
210
211    pub fn sign_cert<S>(&mut self, sign_method: &S, validity_period: ValidityPeriod)
212    where
213        S: SignMethod,
214    {
215        self.sign_internal(
216            sign_method,
217            SignatureInfo::new(
218                SignatureType::new(VarNum::from(sign_method.signature_type())),
219                sign_method.certificate().map(|x| x.name_locator()),
220                Some(validity_period),
221            ),
222        )
223    }
224
225    pub fn signature_info(&self) -> Option<&SignatureInfo> {
226        self.signature_info.as_ref()
227    }
228
229    pub fn is_signed(&self) -> bool {
230        self.signature_info.is_some()
231    }
232
233    pub fn matches_interest<D>(&self, interest: &Interest<D>) -> bool
234    where
235        D: TlvEncode + TlvDecode,
236    {
237        // If `name` contains an ImplicitSha256DigestComponent, check that it's correct
238        if let Some(NameComponent::ImplicitSha256DigestComponent(component)) =
239            interest.name().components.last()
240        {
241            let mut hasher = Sha256::new();
242            hasher.update(self.encode());
243            let hash = hasher.finalize();
244            let hash: &[u8] = &hash;
245            if &component.name != hash {
246                return false;
247            }
248        }
249
250        // If CanBePrefix is set, just check if name is a prefix
251        if interest.can_be_prefix() {
252            self.name.has_prefix(interest.name())
253        } else {
254            for (c1, c2) in self.name().iter().zip(interest.name().iter()) {
255                if matches!(c2, NameComponent::ImplicitSha256DigestComponent(_)) {
256                    continue;
257                }
258                if c1 != c2 {
259                    return false;
260                }
261            }
262            true
263        }
264    }
265
266    /// Verify the signature of this Data packet with the given SignMethod
267    pub fn verify<S>(&self, sign_method: &S) -> Result<(), VerifyError>
268    where
269        S: SignatureVerifier,
270        S: ?Sized,
271    {
272        let Some(ref signature_value) = self.signature_value else {
273            return Err(VerifyError::MissingSignatureInfo);
274        };
275
276        let signable_portion = self.signable_portion();
277
278        let success = sign_method.verify(&signable_portion, signature_value.as_ref());
279
280        success.then_some(()).ok_or(VerifyError::InvalidSignature)
281    }
282}
283
284#[cfg(test)]
285mod tests {
286    use base64::Engine;
287    use bytes::Bytes;
288    use ndn_tlv::TlvDecode;
289
290    use crate::{Content, Data, Name, RsaCertificate, SafeBag, SignatureSha256WithRsa};
291
292    #[test]
293    fn rsa_signature() {
294        const SAFEBAG: &[u8] = b"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\
328g==";
329
330        let safebag_data = base64::engine::general_purpose::STANDARD
331            .decode(SAFEBAG)
332            .unwrap();
333        let safebag = SafeBag::decode(&mut Bytes::from(safebag_data)).unwrap();
334
335        let cert = RsaCertificate::from_safebag(safebag, "test").unwrap();
336
337        let mut data = Data {
338            name: Name::from_str("ndn:/test/test/asd").unwrap(),
339            meta_info: None,
340            content: Some(Content { data: () }),
341            signature_info: None,
342            signature_value: None,
343        };
344
345        let mut signer = SignatureSha256WithRsa::new(cert.clone());
346        data.sign(&mut signer);
347
348        assert!(data.verify(&signer).is_ok());
349    }
350}