lockbook_shared/
file_metadata.rs

1use std::fmt::{self, Debug, Formatter};
2use std::hash::{Hash, Hasher};
3use std::str::FromStr;
4
5use libsecp256k1::PublicKey;
6use serde::{Deserialize, Serialize};
7use uuid::Uuid;
8
9use crate::access_info::{EncryptedFolderAccessKey, UserAccessInfo, UserAccessMode};
10use crate::account::Account;
11use crate::clock::get_time;
12use crate::crypto::AESKey;
13use crate::file_like::FileLike;
14use crate::secret_filename::SecretFileName;
15use crate::signed_file::SignedFile;
16use crate::{pubkey, symkey, SharedResult};
17
18pub type DocumentHmac = [u8; 32];
19
20#[derive(Serialize, Deserialize, Clone, Debug)]
21pub struct FileMetadata {
22    pub id: Uuid,
23    pub file_type: FileType,
24    pub parent: Uuid,
25    pub name: SecretFileName,
26    pub owner: Owner,
27    pub is_deleted: bool,
28    pub document_hmac: Option<DocumentHmac>,
29    pub user_access_keys: Vec<UserAccessInfo>,
30    pub folder_access_key: EncryptedFolderAccessKey,
31}
32
33impl FileMetadata {
34    pub fn create_root(account: &Account) -> SharedResult<Self> {
35        let id = Uuid::new_v4();
36        let key = symkey::generate_key();
37        let pub_key = account.public_key();
38
39        Ok(FileMetadata {
40            id,
41            file_type: FileType::Folder,
42            parent: id,
43            name: SecretFileName::from_str(&account.username, &key, &key)?,
44            owner: Owner(pub_key),
45            is_deleted: false,
46            document_hmac: None,
47            user_access_keys: vec![UserAccessInfo::encrypt(
48                account,
49                &pub_key,
50                &pub_key,
51                &key,
52                UserAccessMode::Write,
53            )?],
54            folder_access_key: symkey::encrypt(&key, &key)?,
55        })
56    }
57
58    pub fn create(
59        id: Uuid, key: AESKey, owner: &PublicKey, parent: Uuid, parent_key: &AESKey, name: &str,
60        file_type: FileType,
61    ) -> SharedResult<Self> {
62        Ok(FileMetadata {
63            id,
64            file_type,
65            parent,
66            name: SecretFileName::from_str(name, &key, parent_key)?,
67            owner: Owner(*owner),
68            is_deleted: false,
69            document_hmac: None,
70            user_access_keys: Default::default(),
71            folder_access_key: symkey::encrypt(parent_key, &key)?,
72        })
73    }
74
75    pub fn sign(self, account: &Account) -> SharedResult<SignedFile> {
76        pubkey::sign(&account.private_key, self, get_time)
77    }
78}
79
80// This is impl'd to avoid comparing encrypted values
81impl PartialEq for FileMetadata {
82    fn eq(&self, other: &Self) -> bool {
83        self.id == other.id
84            && self.file_type == other.file_type
85            && self.parent == other.parent
86            && self.name == other.name
87            && self.owner == other.owner
88            && self.is_deleted == other.is_deleted
89            && self.document_hmac == other.document_hmac
90            && self.user_access_keys == other.user_access_keys
91    }
92}
93
94impl fmt::Display for FileMetadata {
95    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
96        write!(f, "{}", self.display())
97    }
98}
99
100#[derive(Serialize, Deserialize, Eq, Clone, Copy)]
101pub struct Owner(pub PublicKey);
102
103impl Hash for Owner {
104    fn hash<H: Hasher>(&self, state: &mut H) {
105        self.0.serialize().hash(state)
106    }
107}
108
109impl PartialEq for Owner {
110    fn eq(&self, other: &Self) -> bool {
111        self.0.serialize() == other.0.serialize()
112    }
113}
114
115impl Debug for Owner {
116    fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
117        write!(f, "pub_key:{}", base64::encode(self.0.serialize_compressed()))
118    }
119}
120
121#[derive(Clone, PartialEq, Eq, Hash, Debug, Deserialize, Serialize, Copy)]
122pub enum FileType {
123    Document,
124    Folder,
125    Link { target: Uuid },
126}
127
128impl FromStr for FileType {
129    type Err = ();
130    fn from_str(input: &str) -> Result<FileType, Self::Err> {
131        match input {
132            "Document" => Ok(FileType::Document),
133            "Folder" => Ok(FileType::Folder),
134            _ => Err(()),
135        }
136    }
137}
138
139impl fmt::Display for FileType {
140    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
141        write!(f, "{:?}", self)
142    }
143}
144
145#[derive(Serialize, Deserialize, PartialEq, Eq, Clone)]
146pub struct FileDiff<F: FileLike> {
147    pub old: Option<F>,
148    pub new: F,
149}
150
151impl<F: FileLike> Debug for FileDiff<F> {
152    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
153        let mut result = &mut f.debug_struct("FileDiff");
154        result = result.field("id", self.id());
155        for diff in self.diff() {
156            result = match diff {
157                Diff::New => result.field("is_new", &true),
158                Diff::Parent => result.field("new_parent", &self.new.parent()),
159                Diff::Name => result.field("new_name", &self.new.secret_name()),
160                Diff::Owner => result.field("new_owner", &self.new.owner()),
161                Diff::Deleted => result.field("new_deleted", &self.new.explicitly_deleted()),
162                Diff::Hmac => result.field("new_hmac", &self.new.document_hmac()),
163                Diff::UserKeys => result.field("new_user_keys", &true),
164            };
165        }
166        result.finish()
167    }
168}
169
170#[derive(PartialEq, Eq, Debug)]
171pub enum Diff {
172    New,
173    Parent,
174    Name,
175    Owner,
176    Deleted,
177    Hmac,
178    UserKeys,
179}
180
181impl<F: FileLike> FileDiff<F> {
182    pub fn id(&self) -> &Uuid {
183        self.new.id()
184    }
185
186    pub fn diff(&self) -> Vec<Diff> {
187        let new = &self.new;
188        use Diff::*;
189        match &self.old {
190            None => vec![New],
191            Some(old) => {
192                let mut changes = vec![];
193
194                if old.parent() != new.parent() {
195                    changes.push(Parent)
196                }
197
198                if old.secret_name() != new.secret_name() {
199                    changes.push(Name)
200                }
201
202                if old.owner() != new.owner() {
203                    changes.push(Owner)
204                }
205
206                if old.explicitly_deleted() != new.explicitly_deleted() {
207                    changes.push(Deleted)
208                }
209
210                if old.document_hmac() != new.document_hmac() {
211                    changes.push(Hmac);
212                }
213
214                if old.user_access_keys() != new.user_access_keys() {
215                    changes.push(UserKeys);
216                }
217
218                changes
219            }
220        }
221    }
222
223    pub fn new(new: &F) -> Self {
224        let old = None;
225        let new = new.clone();
226        Self { old, new }
227    }
228
229    pub fn edit(old: &F, new: &F) -> Self {
230        let old = Some(old.clone());
231        let new = new.clone();
232        Self { old, new }
233    }
234}