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
14#[allow(dead_code)]
15impl Aes256GcmSivConfig {
16    pub fn set_hash_algorithm(&mut self, hash_algorithm: super::hasher::pbkdf2::Algorithm) {
17        self.hash_algorithm = hash_algorithm;
18    }
19
20    pub fn set_hash_rounds(&mut self, hash_rounds: u32) {
21        self.hash_rounds = hash_rounds;
22    }
23}
24
25/// Default configuration for Aes256GcmSiv with 20 rounds of PBKDF2 SHA-512
26impl Default for Aes256GcmSivConfig {
27    fn default() -> Self {
28        Self {
29            hash_rounds: 600_000,
30            hash_algorithm: super::hasher::pbkdf2::Algorithm::Pbkdf2Sha512,
31        }
32    }
33}
34
35pub enum DecrypterCipher {
36    Aes256GcmSiv(Aes256GcmSivConfig),
37}
38
39pub trait DecryptProvider {
40    type Cipher;
41
42    fn decrypt(
43        &self,
44        input: &mut DecryptData,
45        cipher: Self::Cipher,
46    ) -> Result<DecryptionResult, DefaultError>;
47}
48
49pub struct PBKDF2DecryptProvide;
50
51impl DecryptProvider for PBKDF2DecryptProvide {
52    type Cipher = DecrypterCipher;
53
54    fn decrypt(
55        &self,
56        input: &mut DecryptData,
57        cipher: Self::Cipher,
58    ) -> Result<DecryptionResult, DefaultError> {
59        match cipher {
60            DecrypterCipher::Aes256GcmSiv(config) => {
61                tracing::info!("Decrypting: Aes256GcmSiv");
62
63                // Convert hex strings to bytes
64                let salt = String::from_utf8(input.salt().clone())?;
65                let salt = SaltString::from_b64(salt.as_str()).expect("salt is base64 encoded");
66
67                let nonce = Nonce::from_slice(input.nonce());
68                let ciphertext = input.ciphertext();
69
70                // Derive a 32-byte key using PBKDF2 with SHA-512 and 20 rounds
71                let hasher = super::hasher::pbkdf2::Hasher::hash(
72                    "password",
73                    &config.hash_rounds,
74                    config.hash_algorithm,
75                    Some(salt),
76                )
77                .unwrap();
78
79                // Convert the key to a fixed-size array
80                let key = hex::decode(hasher.hash().as_str()).unwrap();
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}