dsf_core/page/
mod.rs

1//! Pages are a high level representation of pages stored in the database
2//! These can be converted into and from a base object for encoding and decoding.
3
4use core::convert::TryFrom;
5
6#[cfg(feature = "alloc")]
7use alloc::prelude::v1::*;
8
9use crate::base::{Base, BaseOptions, Body, Header, PrivateOptions};
10use crate::crypto;
11use crate::error::Error;
12use crate::options::Options;
13use crate::types::*;
14
15mod info;
16pub use info::PageInfo;
17
18//pub type Page = Base;
19//pub type PageBuilder = BaseBuilder;
20
21/// High level description of a database page
22#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
23#[derive(Debug, Clone)]
24pub struct Page {
25    // Page ID
26    pub id: Id,
27
28    // Page header
29    pub header: Header,
30
31    // Information associated with different object kinds
32    pub info: PageInfo,
33
34    // Page Body
35    pub body: Body,
36
37    // Common options
38    pub issued: Option<DateTime>,
39    pub expiry: Option<DateTime>,
40
41    pub public_options: Vec<Options>,
42
43    pub private_options: PrivateOptions,
44
45    // Previous page signature
46    pub previous_sig: Option<Signature>,
47
48    // Signature (if signed or decoded)
49    pub signature: Option<Signature>,
50
51    /// Verified flag
52    pub verified: bool,
53
54    // Raw (encoded) data
55    pub raw: Option<Vec<u8>>,
56
57    _extend: (),
58}
59
60#[derive(Debug, Clone, PartialEq)]
61pub struct PageOptions {
62    // Page issued time
63    pub issued: Option<DateTime>,
64    // Page expiry time
65    pub expiry: Option<DateTime>,
66
67    // Public options
68    pub public_options: Vec<Options>,
69    // Private options
70    pub private_options: PrivateOptions,
71
72    // Previous page signature
73    pub previous_sig: Option<Signature>,
74    // Signature (if signed or decoded)
75    pub signature: Option<Signature>,
76    // Raw (encoded) data
77    pub raw: Option<Vec<u8>>,
78}
79
80impl Default for PageOptions {
81    fn default() -> Self {
82        Self {
83            issued: None,
84            expiry: None,
85            public_options: vec![],
86            private_options: PrivateOptions::None,
87            previous_sig: None,
88            signature: None,
89            raw: None,
90        }
91    }
92}
93
94impl PartialEq for Page {
95    fn eq(&self, o: &Self) -> bool {
96        self.id == o.id
97            && self.header == o.header
98            && self.info == o.info
99            && self.body == o.body
100            && self.issued == o.issued
101            && self.expiry == o.expiry
102            && self.previous_sig == o.previous_sig
103            && self.public_options == o.public_options
104            && self.private_options == o.private_options
105            && self.signature == o.signature
106    }
107}
108
109impl Page {
110    /// Create a new page
111    pub fn new(id: Id, header: Header, info: PageInfo, body: Body, options: PageOptions) -> Self {
112        Page {
113            id,
114            header,
115            info,
116            body,
117
118            issued: options.issued,
119            expiry: options.expiry,
120
121            public_options: options.public_options,
122            private_options: options.private_options,
123
124            previous_sig: options.previous_sig,
125
126            signature: options.signature,
127            raw: options.raw,
128
129            verified: false,
130
131            _extend: (),
132        }
133    }
134
135    pub fn id(&self) -> &Id {
136        &self.id
137    }
138
139    pub fn header(&self) -> &Header {
140        &self.header
141    }
142
143    pub fn info(&self) -> &PageInfo {
144        &self.info
145    }
146
147    pub fn body(&self) -> &Body {
148        &self.body
149    }
150
151    pub fn issued(&self) -> Option<DateTime> {
152        self.issued
153    }
154
155    pub fn expiry(&self) -> Option<DateTime> {
156        self.expiry
157    }
158
159    #[cfg(feature = "std")]
160    pub fn valid(&self) -> bool {
161        use std::ops::Add;
162
163        // Convert issued and expiry times
164        let (issued, expiry): (Option<std::time::SystemTime>, Option<std::time::SystemTime>) = (
165            self.issued().map(|v| v.into()),
166            self.expiry().map(|v| v.into()),
167        );
168
169        // Compute validity
170        match (issued, expiry) {
171            // For fixed expiry, use this
172            (_, Some(expiry)) => std::time::SystemTime::now() > expiry,
173            // For no expiry, use 1h
174            (Some(issued), None) => {
175                std::time::SystemTime::now() < issued.add(std::time::Duration::from_secs(3600))
176            }
177            // Otherwise default to true
178            // TODO: should we allow services _without_ valid time records?
179            _ => true,
180        }
181    }
182
183    pub fn public_options(&self) -> &[Options] {
184        &self.public_options
185    }
186
187    pub fn private_options(&self) -> &PrivateOptions {
188        &self.private_options
189    }
190
191    pub fn signature(&self) -> Option<Signature> {
192        self.signature.clone()
193    }
194
195    pub fn set_signature(&mut self, sig: Signature) {
196        self.signature = Some(sig);
197    }
198
199    pub fn raw(&self) -> &Option<Vec<u8>> {
200        &self.raw
201    }
202
203    pub fn clean(&mut self) {
204        self.raw = None;
205    }
206}
207
208impl Page {
209    pub fn decode_pages<V>(buff: &[u8], key_source: V) -> Result<Vec<Page>, Error>
210    where
211        V: Fn(&Id) -> Option<PublicKey>,
212    {
213        let mut pages = vec![];
214        let mut i = 0;
215
216        // Last key used to cache the previous primary key to decode secondary pages published by a service in a single message.
217        let mut last_key: Option<(Id, PublicKey)> = None;
218
219        while i < buff.len() {
220            // TODO: validate signatures against existing services!
221            let (b, n) = Base::parse(
222                &buff[i..],
223                |id| {
224                    // Try key_source first
225                    if let Some(key) = (key_source)(id) {
226                        return Some(key);
227                    };
228
229                    // Check for last entry second
230                    if let Some(prev) = &last_key {
231                        if &prev.0 == id {
232                            return Some(prev.1.clone());
233                        }
234                    }
235
236                    // Fail if no public key is found
237                    None
238                },
239                // No encryption key source available here
240                |_id| None,
241            )?;
242
243            i += n;
244
245            let page = match Page::try_from(b) {
246                Ok(p) => p,
247                Err(e) => {
248                    error!("Error loading page from message: {:?}", e);
249                    continue;
250                }
251            };
252
253            // Cache key for next run
254            if let Some(key) = page.info().pub_key() {
255                last_key = Some((page.id().clone(), key));
256            }
257
258            // Push page to parsed list
259            pages.push(page);
260        }
261
262        Ok(pages)
263    }
264
265    pub fn encode_pages(pages: &[Page], buff: &mut [u8]) -> Result<usize, Error> {
266        let mut i = 0;
267
268        for p in pages {
269            // Check page has associated signature
270            match (&p.signature, &p.raw) {
271                (None, None) => {
272                    error!("cannot encode page without associated signature or private key");
273                    continue;
274                }
275                _ => (),
276            };
277
278            // Convert and encode
279            let mut b = Base::from(p);
280            let n = b.encode(None, None, &mut buff[i..])?;
281
282            i += n;
283        }
284
285        Ok(i)
286    }
287}
288
289impl From<&Page> for Base {
290    fn from(page: &Page) -> Base {
291        let sig = page.signature().clone();
292
293        // Insert default options
294        let mut default_options = vec![];
295
296        if let Some(issued) = page.issued {
297            default_options.push(Options::issued(issued));
298        }
299
300        if let Some(expiry) = page.expiry {
301            default_options.push(Options::expiry(expiry));
302        }
303
304        if let Some(prev_sig) = &page.previous_sig {
305            default_options.push(Options::prev_sig(prev_sig));
306        }
307
308        // Add public fields for different object types
309        match &page.info {
310            PageInfo::Primary(primary) => {
311                default_options.push(Options::public_key(primary.pub_key.clone()));
312            }
313            PageInfo::Secondary(secondary) => {
314                default_options.push(Options::peer_id(secondary.peer_id.clone()));
315            }
316            PageInfo::Data(_data) => {}
317        }
318
319        // Add additional public options
320        // TODO: ideally these should be specified by type rather than an arbitrary list
321        let mut public_options = page.public_options.clone();
322        public_options.append(&mut default_options);
323
324        // Generate base object
325        let mut b = Base::new(
326            page.id.clone(),
327            page.header.clone(),
328            page.body.clone(),
329            BaseOptions {
330                public_options,
331                private_options: page.private_options.clone(),
332                ..Default::default()
333            },
334        );
335
336        if let Some(sig) = sig {
337            b.set_signature(sig);
338        }
339
340        if let Some(raw) = &page.raw {
341            b.set_raw(raw.clone());
342        }
343
344        b.verified = page.verified;
345
346        b
347    }
348}
349
350impl TryFrom<Base> for Page {
351    type Error = Error;
352
353    fn try_from(base: Base) -> Result<Self, Error> {
354        let header = base.header();
355        let signature = base.signature();
356
357        let flags = header.flags();
358        let kind = header.kind();
359
360        if !kind.is_page() && !kind.is_data() {
361            return Err(Error::InvalidPageKind);
362        }
363
364        let (mut issued, mut expiry, mut previous_sig, mut peer_id) = (None, None, None, None);
365        let public_options = base
366            .public_options()
367            .iter()
368            .filter_map(|o| match &o {
369                Options::Issued(v) => {
370                    issued = Some(v.when);
371                    None
372                }
373                Options::Expiry(v) => {
374                    expiry = Some(v.when);
375                    None
376                }
377                Options::PrevSig(v) => {
378                    previous_sig = Some(v.sig.clone());
379                    None
380                }
381                Options::PeerId(v) => {
382                    peer_id = Some(v.peer_id.clone());
383                    None
384                }
385                _ => Some(o),
386            })
387            .map(|o| o.clone())
388            .collect();
389
390        let peer_id = base.peer_id.clone();
391
392        // TODO: parse out private options too?
393        let _private_options = base.private_options();
394
395        let info = if kind.is_page() && !flags.contains(Flags::SECONDARY) {
396            // Handle primary page parsing
397
398            // Fetch public key from options
399            let public_key: PublicKey = match &base.public_key {
400                Some(pk) => Ok(pk.clone()),
401                None => Err(Error::NoPublicKey),
402            }?;
403
404            // Check public key and ID match
405            let hash: Id = crypto::hash(&public_key).unwrap().into();
406            if &hash != base.id() {
407                return Err(Error::KeyIdMismatch);
408            }
409
410            PageInfo::primary(public_key)
411        } else if kind.is_page() && flags.contains(Flags::SECONDARY) {
412            // Handle secondary page parsing
413            let peer_id = match peer_id {
414                Some(id) => Ok(id),
415                None => Err(Error::NoPeerId),
416            }?;
417
418            PageInfo::secondary(peer_id)
419        } else if kind.is_data() {
420            PageInfo::Data(())
421        } else {
422            error!(
423                "Attempted to convert non-page base object ({:?}) to page",
424                kind
425            );
426            return Err(Error::UnexpectedPageType);
427        };
428
429        Ok(Page {
430            id: base.id().clone(),
431            header: header.clone(),
432            info,
433            body: base.body.clone(),
434            issued,
435            expiry,
436
437            previous_sig,
438
439            public_options,
440            private_options: base.private_options.clone(),
441            signature: signature.clone(),
442            verified: base.verified,
443
444            raw: base.raw().clone(),
445            _extend: (),
446        })
447    }
448}