extensible_encrypter/
decrypter.rs

1use crate::error::{self, DefaultError};
2use crate::prelude::*;
3use aes_gcm_siv::aead::Aead;
4use aes_gcm_siv::{aead::KeyInit, Aes256GcmSiv, Nonce};
5use pbkdf2::password_hash::SaltString;
6
7pub mod builder;
8
9pub struct Aes256GcmSivConfig {
10    hash_rounds: u32,
11    hash_algorithm: super::hasher::pbkdf2::Algorithm,
12}
13
14impl Aes256GcmSivConfig {
15    pub fn set_hash_algorithm(&mut self, hash_algorithm: super::hasher::pbkdf2::Algorithm) {
16        self.hash_algorithm = hash_algorithm;
17    }
18
19    pub fn set_hash_rounds(&mut self, hash_rounds: u32) {
20        self.hash_rounds = hash_rounds;
21    }
22}
23
24/// Default configuration for Aes256GcmSiv with 20 rounds of PBKDF2 SHA-512
25impl Default for Aes256GcmSivConfig {
26    fn default() -> Self {
27        Self {
28            hash_rounds: 600_000,
29            hash_algorithm: super::hasher::pbkdf2::Algorithm::Pbkdf2Sha512,
30        }
31    }
32}
33
34pub enum DecrypterCipher {
35    Aes256GcmSiv(Aes256GcmSivConfig),
36}
37
38pub trait DecryptProvider {
39    type Cipher;
40
41    fn decrypt(
42        &self,
43        input: &mut DecryptData,
44        cipher: Self::Cipher,
45    ) -> Result<DecryptionResult, DefaultError>;
46}
47
48pub struct PBKDF2DecryptProvide;
49
50impl DecryptProvider for PBKDF2DecryptProvide {
51    type Cipher = DecrypterCipher;
52
53    fn decrypt(
54        &self,
55        input: &mut DecryptData,
56        cipher: Self::Cipher,
57    ) -> Result<DecryptionResult, DefaultError> {
58        match cipher {
59            DecrypterCipher::Aes256GcmSiv(config) => {
60                tracing::info!("Decrypting: Aes256GcmSiv");
61
62                // Convert hex strings to bytes
63                let salt = String::from_utf8(input.salt().clone())?;
64                let salt = SaltString::from_b64(salt.as_str()).expect("salt is base64 encoded");
65
66                let nonce = Nonce::try_from(&input.nonce()[..]).map_err(|_err| {
67                    DefaultError::ErrorMessage("Failed parsing nonce from slice".to_string())
68                })?;
69                let ciphertext = input.ciphertext();
70
71                // Derive a 32-byte key using PBKDF2 with SHA-512 and 20 rounds
72                let hasher = super::hasher::pbkdf2::Hasher::hash(
73                    "password",
74                    &config.hash_rounds,
75                    config.hash_algorithm,
76                    Some(salt),
77                )?;
78
79                // Convert the key to a fixed-size array
80                let key = hex::decode(hasher.hash().as_str())?;
81                let decryption_key_array: [u8; 32] = key.try_into().unwrap();
82
83                // Initialize the AES-GCM-SIV cipher for decryption
84                let decryption_cipher =
85                    Aes256GcmSiv::new_from_slice(&decryption_key_array).unwrap();
86
87                // Decrypt the ciphertext
88                let decrypted_message = match decryption_cipher.decrypt(&nonce, ciphertext.as_ref())
89                {
90                    Ok(message) => message,
91                    Err(err) => {
92                        return Err(DefaultError::ErrorMessage(format!(
93                            "Failed to decrypt due to {}.",
94                            err
95                        )))
96                    }
97                };
98
99                // Convert the decrypted message to a string
100                let decrypted_message = String::from_utf8(decrypted_message).unwrap();
101
102                // Output the decryption result
103                println!("Decrypted message: {}", decrypted_message);
104
105                Ok(DecryptionResult::new(decrypted_message))
106            }
107        }
108    }
109}
110
111pub struct DecryptionResult {
112    plaintext: String,
113}
114
115impl DecryptionResult {
116    pub fn new(plaintext: String) -> Self {
117        Self { plaintext }
118    }
119
120    pub fn plaintext(&self) -> &str {
121        &self.plaintext
122    }
123}
124
125pub struct Decrypter;
126
127impl Decrypter {
128    ///  Uses impl trait to accept any type that implements DecrypterPayload and converts it to DecryptData, passing this to the provider to perform the decryption
129    pub fn decrypt<CipherType>(
130        input: impl DecrypterPayload,
131        provider: impl DecryptProvider<Cipher = CipherType>,
132        cipher: CipherType,
133    ) -> error::Result<DecryptionResult> {
134        let input = &mut DecryptData::from_payload(&input);
135
136        provider.decrypt(input, cipher)
137    }
138}
139
140#[cfg(test)]
141mod tests {
142    use super::*;
143    use tracing_test::traced_test;
144
145    #[traced_test]
146    #[test]
147    fn aes256_gcm_siv_pbkdf2_sha256() {
148        let ciphertext = "e7550de30e76d4546082d17e762032b6dfcc650e2d4072cc6e52bf";
149        let nonce = "66444888d4f0e1a69f387dfe";
150        let salt = "30656e4d7a36716534452b414837384d4a4946635967";
151
152        let ciphertext = hex::decode(ciphertext).unwrap();
153        let nonce = hex::decode(nonce).unwrap();
154        let salt = hex::decode(salt).unwrap();
155
156        let input = &mut builder::DecrypterBuilder::new()
157            .salt(salt)
158            .nonce(nonce)
159            .ciphertext(ciphertext)
160            .build();
161
162        let provider = PBKDF2DecryptProvide {};
163
164        let mut cipher_config = Aes256GcmSivConfig::default();
165        cipher_config.set_hash_algorithm(crate::hasher::pbkdf2::Algorithm::Pbkdf2Sha256);
166        cipher_config.set_hash_rounds(20); // low number of rounds for testing
167
168        let result = Decrypter::decrypt(
169            input,
170            provider,
171            DecrypterCipher::Aes256GcmSiv(cipher_config),
172        );
173        let result = result.expect("Decryption failed");
174
175        assert_eq!(result.plaintext, "hello there");
176    }
177}