extensible_encrypter/
encrypter.rs1use crate::error::{self, DefaultError};
2use aes_gcm_siv::aead::rand_core::RngCore;
3use aes_gcm_siv::{
4 aead::{Aead, KeyInit, OsRng},
5 Aes256GcmSiv, Nonce,
6};
7use base64::{engine::general_purpose, Engine as _};
8use pbkdf2::password_hash::SaltString;
9use std::io::Write;
10
11pub struct Aes256GcmSivConfig {
12 hash_rounds: u32,
13 hash_algorithm: super::hasher::pbkdf2::Algorithm,
14}
15
16#[allow(dead_code)]
17impl Aes256GcmSivConfig {
18 pub fn set_hash_algorithm(&mut self, hash_algorithm: super::hasher::pbkdf2::Algorithm) {
19 self.hash_algorithm = hash_algorithm;
20 }
21
22 pub fn set_hash_rounds(&mut self, hash_rounds: u32) {
23 self.hash_rounds = hash_rounds;
24 }
25}
26impl Default for Aes256GcmSivConfig {
28 fn default() -> Self {
29 Self {
30 hash_rounds: 600_000,
31 hash_algorithm: super::hasher::pbkdf2::Algorithm::Pbkdf2Sha512,
32 }
33 }
34}
35
36pub enum Cipher {
37 Aes256GcmSiv(Aes256GcmSivConfig),
38}
39
40pub trait EncryptProvider {
41 type Cipher;
42
43 fn encrypt(
44 &self,
45 plaintext: &str,
46 password: &str,
47 ek: Self::Cipher,
48 ) -> Result<EncryptionResult, DefaultError>;
49}
50
51pub struct Aes256GcmSivEncryptProvide;
52
53impl EncryptProvider for Aes256GcmSivEncryptProvide {
54 type Cipher = Cipher;
55
56 fn encrypt(
57 &self,
58 plaintext: &str,
59 password: &str,
60 encryption_kind: Self::Cipher,
61 ) -> Result<EncryptionResult, DefaultError> {
62 match encryption_kind {
63 Cipher::Aes256GcmSiv(config) => {
64 tracing::info!("Encrypting: Aes256GcmSiv");
65
66 let mut salt_result = Vec::new();
68 let salt = SaltString::generate(&mut OsRng);
69 let salt_str = salt.as_str().as_bytes();
70 salt_result
71 .write_all(salt_str)
72 .expect("Failed copying salt into buffer");
73
74 let hasher = super::hasher::pbkdf2::Hasher::hash(
76 password,
77 &config.hash_rounds,
78 config.hash_algorithm,
79 Some(salt),
80 )
81 .unwrap();
82
83 let key = hex::decode(hasher.hash().as_str()).unwrap();
85 let key_array: [u8; 32] = key.try_into().unwrap();
86
87 let cipher = Aes256GcmSiv::new_from_slice(&key_array).unwrap();
89
90 let mut nonce = [0u8; 12]; OsRng.fill_bytes(&mut nonce);
93 let mut nonce_result = Vec::new();
94 nonce_result
95 .write_all(&nonce)
96 .expect("Failed copying nonce into buffer");
97
98 let nonce = Nonce::from_slice(&nonce); let ciphertext = cipher
102 .encrypt(nonce, plaintext.as_bytes())
103 .expect("Encryption failed"); tracing::debug!("Nonce: {:?}", nonce);
105 tracing::debug!("Ciphertext: {:?}", ciphertext);
106
107 Ok(EncryptionResult {
108 ciphertext,
109 nonce: nonce_result,
110 salt: salt_result,
111 })
112 }
113 }
114 }
115}
116
117#[derive(Debug)]
118pub struct EncryptionResult {
119 pub ciphertext: Vec<u8>,
120 pub nonce: Vec<u8>, pub salt: Vec<u8>,
122}
123
124impl EncryptionResult {
125 pub fn ciphertext(&self) -> &[u8] {
126 &self.ciphertext
127 }
128
129 pub fn nonce(&self) -> &[u8] {
130 &self.nonce
131 }
132
133 pub fn salt(&self) -> &[u8] {
134 &self.salt
135 }
136
137 pub fn ciphertext_b64(&self) -> String {
138 general_purpose::STANDARD.encode(self.ciphertext())
139 }
140
141 pub fn nonce_b64(&self) -> String {
142 general_purpose::STANDARD.encode(self.nonce())
143 }
144
145 pub fn salt_b64(&self) -> String {
146 general_purpose::STANDARD.encode(self.salt())
147 }
148}
149
150pub struct Encrypter;
151
152impl Encrypter {
153 pub fn encrypt<C>(
155 plaintext: &str,
156 password: &str,
157 provider: impl EncryptProvider<Cipher = C>,
158 cipher: C,
159 ) -> error::Result<EncryptionResult> {
160 provider.encrypt(plaintext, password, cipher)
161 }
162}
163
164#[cfg(test)]
165mod tests {
166 use super::*;
167 use tracing_test::traced_test;
168
169 #[traced_test]
170 #[test]
171 fn aes256_gcm_siv_e2e() {
172 let provider = Aes256GcmSivEncryptProvide;
173
174 let plaintext = "secret nuke codes go inside the football";
175 let mut cipher_config = Aes256GcmSivConfig::default();
176 cipher_config.set_hash_rounds(20); let result = Encrypter::encrypt(
179 plaintext,
180 "password",
181 provider,
182 Cipher::Aes256GcmSiv(cipher_config),
183 );
184 let result = result.expect("Encryption failed");
185 tracing::info!("Result: {:?}", result);
186
187 let input = &mut crate::decrypter::builder::DecrypterBuilder::new()
188 .salt(result.salt)
189 .nonce(result.nonce)
190 .ciphertext(result.ciphertext)
191 .build();
192
193 let provider = crate::decrypter::PBKDF2DecryptProvide;
194 let mut cipher_config = crate::decrypter::Aes256GcmSivConfig::default();
195 cipher_config.set_hash_rounds(20); let result = crate::decrypter::Decrypter::decrypt(
198 input,
199 provider,
200 crate::decrypter::DecrypterCipher::Aes256GcmSiv(cipher_config),
201 );
202 let result = result.expect("Decryption failed");
203
204 assert_eq!(
205 result.plaintext(),
206 "secret nuke codes go inside the football"
207 );
208 }
209}