1use std::convert::TryFrom;
2
3use aes_gcm::{Aes256Gcm, Key, Nonce};
4use aes_gcm::aead::{Aead, NewAead};
5use argon2::{Argon2, PasswordHasher};
6use argon2::password_hash::SaltString;
7use rand::Rng;
8use rand::rngs::OsRng;
9
10use crate::header_binary_v0::HeaderBinaryV0;
11
12pub struct Entry {
13 pub header: [u8; 1024],
14 pub salt: [u8; 22],
15 pub nonce: [u8; 12],
16 pub ciphertext: Vec<u8>,
17}
18
19pub struct EntryUnlocked {
20 pub header: [u8; 1024],
21 pub salt: [u8; 22],
22 pub nonce: [u8; 12],
23 pub text: Vec<u8>,
24}
25
26impl Entry {
27 #[must_use]
31 pub fn encrypt(value: &[u8], header: &HeaderBinaryV0, password: &str) -> Self {
32 let salt = SaltString::generate(&mut OsRng);
33
34 let password_hash = Argon2::default().hash_password(password.as_bytes(), &salt).unwrap().hash.unwrap();
35
36 let cipher = Aes256Gcm::new(Key::from_slice(password_hash.as_bytes()));
37
38 let random_bytes = rand::thread_rng().gen::<[u8; 12]>();
39 let nonce = Nonce::from_slice(&random_bytes);
40
41 Self {
42 header: <[u8; 1024]>::try_from(header.to_bytes()).unwrap(),
43 salt: <[u8; 22]>::try_from(salt.as_bytes()).unwrap(),
44 nonce: <[u8; 12]>::try_from(nonce.as_slice()).unwrap(),
45 ciphertext: cipher.encrypt(nonce, value).unwrap(),
46 }
47 }
48
49 #[must_use]
53 pub fn decrypt(&self, password: &str) -> EntryUnlocked {
54 let nonce = Nonce::from_slice(&self.nonce);
55
56 let password_hash = Argon2::default().hash_password(password.as_bytes(), &String::from_utf8(Vec::from(self.salt)).unwrap()).unwrap().hash.unwrap();
57 let cipher = Aes256Gcm::new(Key::from_slice(password_hash.as_bytes()));
58
59 let ciphertext = &self.ciphertext;
60
61 EntryUnlocked {
62 header: self.header,
63 salt: self.salt,
64 nonce: self.nonce,
65 text: cipher.decrypt(nonce, ciphertext.as_slice()).unwrap(),
66 }
67 }
68
69 #[must_use]
73 pub fn from_bytes(file: &[u8]) -> Self {
74 let header_and_rest = file.split_at(1024);
75 let salt_and_rest = header_and_rest.1.split_at(22);
76 let nonce_and_rest = salt_and_rest.1.split_at(12);
77 Self {
78 header: <[u8; 1024]>::try_from(header_and_rest.0).unwrap(),
79 salt: <[u8; 22]>::try_from(salt_and_rest.0).unwrap(),
80 nonce: <[u8; 12]>::try_from(nonce_and_rest.0).unwrap(),
81 ciphertext: Vec::from(nonce_and_rest.1),
82 }
83 }
84 #[must_use]
85 pub fn to_bytes(&self) -> Vec<u8> {
86 let mut file = Vec::new();
87 file.extend_from_slice(&self.header);
88 file.extend_from_slice(&self.salt);
89 file.extend_from_slice(&self.nonce);
90 file.extend_from_slice(&self.ciphertext);
91 file
92 }
93}