1use aes_gcm::{
2 aead::{Aead, KeyInit, OsRng},
3 Aes256Gcm, Key, Nonce,
4};
5use argon2::{self, Argon2};
6use rand::RngCore;
7use serde::{de::DeserializeOwned, Deserialize, Serialize};
8use std::{
9 ffi::{OsStr, OsString},
10 fmt,
11 fs::{self, File},
12 io::{self, Read, Write},
13 ops::{Deref, DerefMut},
14 path::{Path, PathBuf},
15 sync::{RwLock, RwLockReadGuard, RwLockWriteGuard},
16};
17use tracing::{error, info};
18
19const SALT_LEN: usize = 16;
20const NONCE_LEN: usize = 12;
21
22#[derive(Serialize, Deserialize)]
24pub struct EncryptedData {
25 salt: Vec<u8>,
26 nonce: Vec<u8>,
27 ciphertext: Vec<u8>,
28}
29
30pub trait EncryptedDataStore: Default + Serialize {
34 fn open<P>(db: P, password: &str) -> io::Result<EncryptedAtomicDatabase<Self>>
37 where
38 P: AsRef<Path>,
39 Self: DeserializeOwned,
40 {
41 let db_path = db.as_ref();
42 if db_path.exists() {
43 EncryptedAtomicDatabase::load(db_path, password)
44 } else {
45 EncryptedAtomicDatabase::create_new(db_path, password)
46 }
47 }
48
49 fn create_from_str<P>(
53 data: &str,
54 path: P,
55 password: &str,
56 ) -> io::Result<EncryptedAtomicDatabase<Self>>
57 where
58 P: AsRef<Path>,
59 Self: DeserializeOwned,
60 {
61 let db_path = path.as_ref();
62 if !db_path.exists() {
63 EncryptedAtomicDatabase::create_from_str(data, path, password)
64 } else {
65 Err(io::Error::new(
66 io::ErrorKind::AlreadyExists,
67 "A file already exists at the provided path!",
68 ))
69 }
70 }
71
72 fn load_encrypted(file: impl Read, key: &Key<Aes256Gcm>) -> io::Result<Self>
74 where
75 Self: DeserializeOwned,
76 {
77 let encrypted: EncryptedData = serde_json::from_reader(file).map_err(|e| {
78 io::Error::new(
79 io::ErrorKind::InvalidData,
80 format!("Failed to deserialize encrypted data: {}", e),
81 )
82 })?;
83
84 Self::decrypt(&encrypted, key)
85 }
86
87 fn save_encrypted(
89 &self,
90 file: impl Write,
91 key: &Key<Aes256Gcm>,
92 salt: &[u8],
93 ) -> io::Result<()> {
94 let encrypted = self.encrypt(key, salt)?;
95
96 serde_json::to_writer(file, &encrypted).map_err(|e| {
97 io::Error::new(
98 io::ErrorKind::Other,
99 format!("Failed to write encrypted data to file: {}", e),
100 )
101 })
102 }
103
104 fn encrypt(&self, key: &Key<Aes256Gcm>, salt: &[u8]) -> io::Result<EncryptedData> {
106 let mut nonce_bytes = vec![0u8; NONCE_LEN];
107 OsRng.fill_bytes(&mut nonce_bytes);
108
109 let cipher = Aes256Gcm::new(key);
110 let nonce = Nonce::from_slice(&nonce_bytes);
111
112 let plaintext = serde_json::to_vec(self).map_err(|e| {
113 io::Error::new(
114 io::ErrorKind::InvalidData,
115 format!("Serialization failed: {}", e),
116 )
117 })?;
118
119 let ciphertext = cipher.encrypt(nonce, plaintext.as_ref()).map_err(|e| {
120 io::Error::new(io::ErrorKind::Other, format!("Encryption failed: {}", e))
121 })?;
122
123 Ok(EncryptedData {
124 salt: salt.to_vec(),
125 nonce: nonce_bytes,
126 ciphertext,
127 })
128 }
129
130 fn decrypt(encrypted: &EncryptedData, key: &Key<Aes256Gcm>) -> io::Result<Self>
132 where
133 Self: DeserializeOwned,
134 {
135 let cipher = Aes256Gcm::new(key);
136 let nonce = Nonce::from_slice(&encrypted.nonce);
137
138 let decrypted_bytes = cipher
139 .decrypt(nonce, encrypted.ciphertext.as_ref())
140 .map_err(|e| {
141 io::Error::new(
142 io::ErrorKind::InvalidData,
143 format!(
144 "Decryption failed: Incorrect password or corrupted data. {}",
145 e
146 ),
147 )
148 })?;
149
150 let data = serde_json::from_slice(&decrypted_bytes).map_err(|e| {
151 io::Error::new(
152 io::ErrorKind::InvalidData,
153 format!("Failed to deserialize decrypted data: {}", e),
154 )
155 })?;
156
157 Ok(data)
158 }
159}
160
161fn derive_key(password: &str, salt: &[u8]) -> io::Result<Key<Aes256Gcm>> {
163 let mut key = [0u8; 32]; Argon2::default()
165 .hash_password_into(password.as_bytes(), salt, &mut key)
166 .map_err(|_| io::Error::new(io::ErrorKind::Other, "Key derivation failed"))?;
167
168 Ok(*Key::<Aes256Gcm>::from_slice(&key))
169}
170
171pub struct EncryptedAtomicDatabase<T: EncryptedDataStore> {
173 path: PathBuf,
174 tmp: PathBuf,
175 data: RwLock<T>,
176 key: RwLock<Key<Aes256Gcm>>,
177 salt: RwLock<Vec<u8>>,
178}
179
180impl<T: EncryptedDataStore + DeserializeOwned> EncryptedAtomicDatabase<T> {
181 pub fn load<P: AsRef<Path>>(path: P, password: &str) -> io::Result<Self> {
183 let new_path = path.as_ref().to_path_buf();
184 let tmp = Self::tmp_path(&new_path)?;
185
186 let file = File::open(&new_path)?;
187 let encrypted: EncryptedData = serde_json::from_reader(&file).map_err(|e| {
189 io::Error::new(
190 io::ErrorKind::InvalidData,
191 format!("Failed to deserialize encrypted data: {}", e),
192 )
193 })?;
194 let key = derive_key(password, &encrypted.salt)?;
195 let file = File::open(&new_path)?;
197 let data = T::load_encrypted(file, &key)?;
198
199 Ok(Self {
201 path: new_path,
202 tmp,
203 data: RwLock::new(data),
204 key: RwLock::new(key),
205 salt: RwLock::new(encrypted.salt),
206 })
207 }
208
209 pub fn create_from_str<P: AsRef<Path>>(
212 data: &str,
213 path: P,
214 password: &str,
215 ) -> io::Result<Self> {
216 let new_path = path.as_ref().to_path_buf();
217 let tmp = Self::tmp_path(&new_path)?;
218
219 let encrypted: EncryptedData = serde_json::from_str(data).map_err(|e| {
220 io::Error::new(
221 io::ErrorKind::InvalidData,
222 format!("Failed to deserialize encrypted data: {}", e),
223 )
224 })?;
225
226 let key = derive_key(password, &encrypted.salt)?;
227
228 let data = T::decrypt(&encrypted, &key)?;
229 atomic_write_encrypted(&tmp, &new_path, &data, &key, &encrypted.salt)?;
230
231 Ok(Self {
232 path: new_path,
233 tmp,
234 data: RwLock::new(data),
235 key: RwLock::new(key),
236 salt: RwLock::new(encrypted.salt),
237 })
238 }
239
240 pub fn create_new<P: AsRef<Path>>(path: P, password: &str) -> io::Result<Self> {
242 let new_path = path.as_ref().to_path_buf();
243 let tmp = Self::tmp_path(&new_path)?;
244
245 let mut salt = vec![0u8; SALT_LEN];
247 OsRng.fill_bytes(&mut salt);
248 let key = derive_key(password, &salt)?;
249
250 let data = Default::default();
251 atomic_write_encrypted(&tmp, &new_path, &data, &key, &salt)?;
252
253 Ok(Self {
254 path: new_path,
255 tmp,
256 data: RwLock::new(data),
257 key: RwLock::new(key),
258 salt: RwLock::new(salt),
259 })
260 }
261
262 pub fn read(&self) -> EncryptedAtomicDatabaseRead<'_, T> {
264 EncryptedAtomicDatabaseRead {
265 data: self.data.read().unwrap(),
266 }
267 }
268
269 pub fn write(&self) -> EncryptedAtomicDatabaseWrite<'_, T> {
271 let key = *self.key.read().unwrap();
273 let salt = self.salt.read().unwrap().clone();
274
275 EncryptedAtomicDatabaseWrite {
276 path: self.path.as_ref(),
277 tmp: self.tmp.as_ref(),
278 data: self.data.write().unwrap(),
279 key,
280 salt,
281 }
282 }
283
284 pub fn change_password(&self, new_password: &str) -> io::Result<()> {
286 let data_guard = self.data.read().unwrap();
287
288 let mut new_salt = vec![0u8; SALT_LEN];
289 OsRng.fill_bytes(&mut new_salt);
290
291 let new_key = derive_key(new_password, &new_salt)?;
292
293 atomic_write_encrypted(&self.tmp, &self.path, &*data_guard, &new_key, &new_salt)?;
294
295 {
296 let mut key_lock = self.key.write().unwrap();
297 *key_lock = new_key;
298 }
299 {
300 let mut salt_lock = self.salt.write().unwrap();
301 *salt_lock = new_salt;
302 }
303
304 Ok(())
305 }
306
307 fn tmp_path(path: &Path) -> io::Result<PathBuf> {
308 let mut tmp_name = OsString::from(".");
309 tmp_name.push(path.file_name().unwrap_or(OsStr::new("db")));
310 tmp_name.push("~");
311 let tmp = path.with_file_name(tmp_name);
312 if tmp.exists() {
313 error!(
314 "Found orphaned database temporary file '{tmp:?}'. The server has recently crashed or is already running. Delete this before continuing!"
315 );
316 return Err(io::Error::new(
317 io::ErrorKind::AlreadyExists,
318 "Orphaned temporary file exists",
319 ));
320 }
321 Ok(tmp)
322 }
323}
324
325fn atomic_write_encrypted<T: EncryptedDataStore>(
327 tmp: &Path,
328 path: &Path,
329 data: &T,
330 key: &Key<Aes256Gcm>,
331 salt: &[u8],
332) -> io::Result<()> {
333 {
334 let tmpfile = File::create(tmp)?;
335 data.save_encrypted(tmpfile, key, salt)?;
336 }
337 fs::rename(tmp, path)?;
338 Ok(())
339}
340
341impl<T: EncryptedDataStore> fmt::Debug for EncryptedAtomicDatabase<T> {
342 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
343 f.debug_struct("EncryptedAtomicDatabase")
344 .field("file", &self.path)
345 .finish()
346 }
347}
348
349impl<T: EncryptedDataStore> Drop for EncryptedAtomicDatabase<T> {
350 fn drop(&mut self) {
351 info!("Saving database");
352 let data_guard = self.data.read().unwrap();
353 let key = self.key.read().unwrap();
354 let salt = self.salt.read().unwrap();
355 if let Err(e) = atomic_write_encrypted(&self.tmp, &self.path, &*data_guard, &key, &salt) {
356 error!("Failed to save database: {}", e);
357 }
358 }
359}
360
361pub struct EncryptedAtomicDatabaseRead<'a, T: EncryptedDataStore> {
362 data: RwLockReadGuard<'a, T>,
363}
364
365impl<'a, T: EncryptedDataStore> Deref for EncryptedAtomicDatabaseRead<'a, T> {
366 type Target = T;
367 fn deref(&self) -> &Self::Target {
368 &self.data
369 }
370}
371
372pub struct EncryptedAtomicDatabaseWrite<'a, T: EncryptedDataStore> {
373 tmp: &'a Path,
374 path: &'a Path,
375 data: RwLockWriteGuard<'a, T>,
376 key: Key<Aes256Gcm>,
377 salt: Vec<u8>,
378}
379
380impl<'a, T: EncryptedDataStore> Deref for EncryptedAtomicDatabaseWrite<'a, T> {
381 type Target = T;
382 fn deref(&self) -> &Self::Target {
383 &self.data
384 }
385}
386
387impl<'a, T: EncryptedDataStore> DerefMut for EncryptedAtomicDatabaseWrite<'a, T> {
388 fn deref_mut(&mut self) -> &mut Self::Target {
389 &mut self.data
390 }
391}
392
393impl<'a, T: EncryptedDataStore> Drop for EncryptedAtomicDatabaseWrite<'a, T> {
394 fn drop(&mut self) {
395 info!("Saving database");
396 if let Err(e) =
397 atomic_write_encrypted(self.tmp, self.path, &*self.data, &self.key, &self.salt)
398 {
399 error!("Failed to save database: {}", e);
400 }
401 }
402}