slpm_file/
payload.rs

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	/// # Panics
28	///
29	/// Panics when any of the values exceed natural limitations
30	#[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	/// # Panics
50	///
51	///Panics when the password is wrong
52	#[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	/// # Panics
70	///
71	/// Panics when the file is too short to be split
72	#[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}