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 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 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 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 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}