Skip to main content

tequel/encrypt/
mod.rs

1/*
2 * Tequel-rs: High-Density 384-bit Cryptographic Hash Engine
3 * Copyright (C) 2026 Gabriel Xavier (dotxav)
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as published
7 * by the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 */
15
16
17use std::num::ParseIntError;
18
19use crate::hash::TequelHash;
20use crate::error::TequelError;
21use crate::rng::TequelRng;
22
23#[cfg(target_arch = "x86_64")]
24use std::arch::x86_64::*;
25
26use crate::avx2_inline::{ add_i8, xor, storeu, loadu, sub_i8, setone_i32 };
27
28use zeroize::{Zeroize, ZeroizeOnDrop};
29
30#[cfg(feature = "serde")]
31use serde::{Serialize, Deserialize};
32
33
34/// TequelEncrypt is a struct that controls Encryption, it uses `Salt` and `Custom Iterations`.
35/// 
36/// You use this struct to use encrypt in Tequel.
37/// ```rust
38/// use tequel::encrypt::TequelEncrypt;
39/// 
40/// fn main() {
41///     let mut teq_encrypt: TequelEncrypt = TequelEncrypt::new();
42/// }
43/// ```
44#[derive(Debug, Zeroize, ZeroizeOnDrop, Clone, PartialEq, Eq)]
45#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
46pub struct TequelEncrypt {
47    pub salt: String,
48    pub iterations: u32,
49}
50
51
52/// `TequelEncryption` is a struct that represent final encrypt, when `TequelEncrypt` is finish, it generates a `TequelEncryption` with MAC, Salt and Encrypted Data.
53/// 
54/// ```rust
55/// use tequel::encrypt::{ TequelEncrypt, TequelEncryption };
56/// 
57/// fn main() -> Result<(), Box<dyn std::error::Error>> {
58/// 
59///     let mut teq_encrypt: TequelEncrypt = TequelEncrypt::new();
60/// 
61///     // It returns a 'TequelEncryption'
62///     let encrypted: TequelEncryption = teq_encrypt.encrypt("my_data".as_bytes(), "my_key_123")?;
63/// 
64///     Ok(())
65/// }
66/// ```
67#[derive(Debug, Zeroize, ZeroizeOnDrop, Clone, PartialEq, Eq)]
68#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
69pub struct TequelEncryption {
70    pub encrypted_data: String,
71    pub salt: String,
72    pub mac: String
73}
74
75
76
77impl TequelEncrypt {
78
79    pub fn new() -> Self {
80        Self {
81            salt: "".to_string(),
82            iterations: 30
83        }
84    }
85
86    pub fn with_salt(mut self, salt: &str) -> Self {
87        self.salt = salt.to_string();
88        self
89    }
90
91    pub fn with_iteration(mut self, value: u32) -> Self{
92        self.iterations = value;
93        self
94    }
95
96    /// Encrypts a byte slice using the Tequel protocol with AVX2 acceleration.
97    ///
98    /// This function implements a multi-layered transformation "ladder" based on 
99    /// internal constants, a random salt, and a user-provided key. It ensures 
100    /// data integrity by generating a MAC (Message Authentication Code) as part 
101    /// of the encryption process.
102    ///
103    /// # Arguments
104    ///
105    /// * `data` - A byte slice (`&[u8]`) containing the plaintext to be encrypted.
106    /// * `key` - A string slice that serves as the master key for the cipher derivation.
107    ///
108    /// # Returns
109    ///
110    /// * `Ok(TequelEncryption)` - A struct containing the hex-encoded encrypted data, 
111    ///   the salt used, and the integrity MAC.
112    /// * `Err(TequelError::EmptyKey)` - If the provided key string is empty.
113    ///
114    /// # Performance
115    ///
116    /// On x86_64 systems supporting **AVX2**, this function processes data in 
117    /// 32-byte chunks using SIMD instructions. It automatically falls back to 
118    /// a scalar implementation for the remaining bytes or unsupported hardware.
119    ///
120    /// # Example
121    ///
122    /// ```rust
123    /// use tequel::encrypt::TequelEncrypt;
124    ///
125    /// let mut teq = TequelEncrypt::new();
126    /// let secret_data = b"Tequel: Weighted and Measured";
127    /// let master_key = "guard_the_gate";
128    ///
129    /// if let Ok(encryption) = teq.encrypt(secret_data, master_key) {
130    ///     println!("Ciphertext: {}", encryption.encrypted_data);
131    ///     println!("MAC: {}", encryption.mac);
132    /// }
133    /// ```
134    pub fn encrypt(&mut self, data: &[u8], key: &str) -> Result<TequelEncryption, TequelError> {
135
136        // If salt is 0 then generate a own
137        if self.salt.as_bytes().len() == 0 {
138            let tequel_rng = TequelRng::new();
139            self.salt = tequel_rng.rand_by_nano().to_string();
140        }
141        
142        let key_salt = self.salt.as_bytes(); // SALT
143        let key_crypt = key.as_bytes(); // KEY
144
145        // if key is empty raise an KeyError
146        if key_crypt.len() == 0 {
147            return Err(TequelError::KeyError("Key is empty".to_string()))
148        }
149
150        let a = 0x107912fau32.to_be_bytes(); // KEY_C
151        let b = 0x220952eau32.to_be_bytes(); // KEY_D
152        let c = 0x3320212au32.to_be_bytes(); // KEY_E
153        let d = 0x4324312fu32.to_be_bytes(); // KEY_E
154        let e = 0x5320212au32.to_be_bytes(); // KEY_E
155        
156        let mut res_bytes = Vec::with_capacity(data.len());
157
158        let val_a = u32::from_be_bytes(a);
159        let val_b = u32::from_be_bytes(b);
160        let val_c = u32::from_be_bytes(c);
161        let val_d = u32::from_be_bytes(d);
162        let val_e = u32::from_be_bytes(e);
163
164        // AVX2
165        unsafe {
166
167            if is_x86_feature_detected!("avx2") {
168
169                let chunks = data.chunks_exact(32);
170                let remainder = chunks.remainder();
171                
172
173                let ymm_c_a = setone_i32(val_a as i32);
174                let ymm_c_b = setone_i32(val_b as i32);
175                let ymm_c_c = setone_i32(val_c as i32);
176                let ymm_c_d = setone_i32(val_d as i32);
177                let ymm_c_e = setone_i32(val_e as i32);
178
179
180                for (c_idx, chunk) in chunks.enumerate() {
181
182                    let byte_offset = c_idx * 32; // 0 -> 32 -> 64 -> 128 ...
183                    let mut ymm1 = loadu(chunk.as_ptr() as *const __m256i);
184
185
186                    
187                    ymm1 = add_i8(ymm1, ymm_c_a);
188                    ymm1 = xor(ymm1, ymm_c_b);
189                    ymm1 = add_i8(ymm1, ymm_c_c);
190                    ymm1 = xor(ymm1, ymm_c_d);
191                    ymm1 = add_i8(ymm1, ymm_c_e);
192                    
193
194
195                    let mut expanded_key = [0u8; 32];
196                    
197                    for i in 0..32 {
198                        expanded_key[i] = key_crypt[(byte_offset + i) % key_crypt.len()];
199                    }
200
201                    ymm1 = xor(ymm1, loadu(expanded_key.as_ptr() as *const __m256i));
202
203
204
205
206                    let mut out = [0u8; 32];
207                    storeu(out.as_mut_ptr() as *mut __m256i, ymm1);
208
209                    res_bytes.extend_from_slice(&out);
210                
211                }
212
213                let processed_bytes = data.len() - remainder.len();
214
215                for (i, &byte) in remainder.iter().enumerate() {
216                    
217                    let g_idx = processed_bytes + i;
218                    let mut curr = byte;
219
220                    curr = curr.wrapping_add(a[g_idx % 4]);
221                    curr = curr ^ b[g_idx % 4];
222                    curr = curr.wrapping_add(c[g_idx % 4]);
223                    curr = curr ^ d[g_idx % 4];
224                    curr = curr.wrapping_add(e[g_idx % 4]);
225
226                    curr = curr ^ key_crypt[g_idx % key_crypt.len()];
227
228                    res_bytes.push(curr)
229                
230                }
231
232            }
233        }
234
235
236
237        let res = res_bytes.iter()
238            .map(|b| format!("{:02x}", b))
239            .collect::<String>();
240
241        let salt_res = key_salt.iter()
242            .map(|b| format!("{:02x}", b))
243            .collect::<String>();
244
245
246        let mut mixmac_buffer = Vec::with_capacity(20 + res_bytes.len() + key_salt.len() + key_crypt.len());
247
248        mixmac_buffer.extend_from_slice(&a);
249        mixmac_buffer.extend_from_slice(&res_bytes);
250        mixmac_buffer.extend_from_slice(&b);
251        mixmac_buffer.extend_from_slice(&c);
252        mixmac_buffer.extend_from_slice(&salt_res.as_bytes());
253        mixmac_buffer.extend_from_slice(&d);
254        mixmac_buffer.extend_from_slice(&key_crypt);
255        mixmac_buffer.extend_from_slice(&e);
256
257        let mut cus_teq_hash = TequelHash::new();
258        let comb_mixmac = cus_teq_hash.tqlhash(&mixmac_buffer).to_lowercase();
259
260
261        Ok(TequelEncryption { encrypted_data: res, salt: salt_res, mac: comb_mixmac })
262
263    }
264
265
266    /// Decrypts a Tequel-encrypted structure and verifies its integrity.
267    ///
268    /// This function performs a reverse transformation of the Tequel protocol,
269    /// using AVX2 SIMD instructions when available. Before decryption, it 
270    /// reconstructs the MAC (Message Authentication Code) to ensure the 
271    /// ciphertext, salt, and key have not been tampered with.
272    ///
273    /// # Arguments
274    ///
275    /// * `tequel_encryption` - A reference to the [`TequelEncryption`] struct 
276    ///   containing the hex-encoded data, salt, and MAC.
277    /// * `key` - The string slice key used during the original encryption process.
278    ///
279    /// # Returns
280    ///
281    /// * `Ok(String)` - The original plaintext as a UTF-8 string.
282    /// * `Err(TequelError::InvalidMac)` - If the calculated MAC does not match 
283    ///   the provided one (Integrity violation).
284    /// * `Err(TequelError::InvalidUtf8)` - If the decrypted bytes are not 
285    ///   valid UTF-8.
286    /// * `Err(TequelError::InvalidHex)` - If the input strings are not valid hex.
287    ///
288    /// # Security
289    ///
290    /// The function follows a "Verify-then-Decrypt" pattern. If the MAC check 
291    /// fails, the decryption logic is never executed, protecting against 
292    /// certain types of side-channel attacks.
293    ///
294    /// # Example
295    ///
296    /// ```rust
297    /// use tequel::encrypt::TequelEncrypt;
298    /// 
299    /// fn main() {
300    /// 
301    ///     let mut teq = TequelEncrypt::new();
302    ///     
303    ///     let encrypted_obj = teq.encrypt(b"secret", "master_key").expect("Failed to encrypt");
304    ///     let decrypted = teq.decrypt(&encrypted_obj, "master_key")
305    ///         .expect("Failed to decrypt or verify data");
306    ///
307    ///     println!("Decrypted content: {}", decrypted);
308    /// }
309    /// ```
310    pub fn decrypt(&mut self, tequel_encryption: &TequelEncryption, key: &str) -> Result<String, TequelError> {
311            
312        if key.is_empty() { return Err(TequelError::KeyError("Key is empty".to_string())); }
313        
314        // CONSTANTS            
315        let a = 0x107912fau32.to_be_bytes(); // KEY_A
316        let b = 0x220952eau32.to_be_bytes(); // KEY_B
317        let c = 0x3320212au32.to_be_bytes(); // KEY_C
318        let d = 0x4324312fu32.to_be_bytes(); // KEY_D
319        let e = 0x5320212au32.to_be_bytes(); // KEY_E
320
321
322        let encrypted_data = self.decode_hex(&tequel_encryption.encrypted_data).map_err(|e| {
323            TequelError::InvalidHex(e.to_string())
324        })?;
325
326        let salt_hex = self.decode_hex(&tequel_encryption.salt).map_err(|e| {
327            TequelError::InvalidHex(e.to_string())
328        })?;
329
330        let mut mixmac_buffer = Vec::with_capacity(20 + encrypted_data.len() + tequel_encryption.salt.len() + key.len());
331
332        mixmac_buffer.extend_from_slice(&a);
333        mixmac_buffer.extend_from_slice(&encrypted_data);
334        mixmac_buffer.extend_from_slice(&b);
335        mixmac_buffer.extend_from_slice(&c);
336        mixmac_buffer.extend_from_slice(&tequel_encryption.salt.as_bytes());
337        mixmac_buffer.extend_from_slice(&d);
338        mixmac_buffer.extend_from_slice(&key.as_bytes());
339        mixmac_buffer.extend_from_slice(&e);
340
341        let mut cus_teq_hash = TequelHash::new();
342        let comb_mixmac = cus_teq_hash.tqlhash(&mixmac_buffer).to_lowercase();
343
344        let is_valid = self.compare_macs(tequel_encryption.mac.to_lowercase().as_bytes(), comb_mixmac.as_bytes());
345
346        if !is_valid {
347            return Err(TequelError::InvalidMac);
348        }
349
350        // SIMD  
351
352        let encrypted_data = self.decode_hex(&tequel_encryption.encrypted_data).map_err(|e| {
353            TequelError::InvalidHex(e.to_string())
354        })?;
355
356        let mut res_bytes = Vec::with_capacity(encrypted_data.len());
357        let key_encrypt_input = key.as_bytes(); 
358
359
360        let val_a = u32::from_be_bytes(a);
361        let val_b = u32::from_be_bytes(b);
362        let val_c = u32::from_be_bytes(c);
363        let val_d = u32::from_be_bytes(d);
364        let val_e = u32::from_be_bytes(e);
365
366        unsafe {
367
368            if is_x86_feature_detected!("avx2") {
369
370                let chunks = encrypted_data.chunks_exact(32);
371                let remainder = chunks.remainder();
372
373                let ymm_c_a = setone_i32(val_a as i32);
374                let ymm_c_b = setone_i32(val_b as i32);
375                let ymm_c_c = setone_i32(val_c as i32);
376                let ymm_c_d = setone_i32(val_d as i32);
377                let ymm_c_e = setone_i32(val_e as i32);
378
379                for (c_idx, chunk) in chunks.enumerate() {
380
381                    let byte_offset = c_idx * 32;
382                    let mut ymm1 = loadu(chunk.as_ptr() as *const __m256i);
383
384
385                    let mut expanded_key = [0u8; 32];
386                    for i in 0..32 {
387                        expanded_key[i] = key_encrypt_input[(byte_offset + i) % key_encrypt_input.len()];
388                    }
389
390                    ymm1 = xor(ymm1, loadu(expanded_key.as_ptr() as *const __m256i));
391
392
393                    ymm1 = sub_i8(ymm1, ymm_c_e);
394                    ymm1 = xor(ymm1,    ymm_c_d);
395                    ymm1 = sub_i8(ymm1, ymm_c_c);
396                    ymm1 = xor(ymm1, ymm_c_b);
397                    ymm1 = sub_i8(ymm1, ymm_c_a);
398
399
400                    let mut out = [0u8; 32];
401                    storeu(out.as_mut_ptr() as *mut __m256i, ymm1);
402
403                    res_bytes.extend_from_slice(&out);
404
405                }
406
407                // Remainder (Ensuring that global_idx)
408                let processed_bytes = encrypted_data.len() - remainder.len();
409
410                for (i, &byte) in remainder.iter().enumerate() {
411        
412                    let g_idx = processed_bytes + i;
413                    let mut curr = byte;
414        
415                    curr ^= key_encrypt_input[g_idx % key_encrypt_input.len()];
416
417                    curr = curr.wrapping_sub(e[g_idx % 4]);
418                    curr ^= d[g_idx % 4];
419                    curr = curr.wrapping_sub(c[g_idx % 4]);
420                    curr ^= b[g_idx % 4];
421                    curr = curr.wrapping_sub(a[g_idx % 4]);
422        
423                    res_bytes.push(curr)
424        
425                }
426
427            }
428
429        }
430
431        let res = String::from_utf8(res_bytes).map_err(|_| TequelError::InvalidUtf8)?;
432
433        Ok(res)
434
435    }
436
437
438
439
440
441
442    fn decode_hex(&self, val: &str) -> Result<Vec<u8>, ParseIntError> {
443        if val.len() % 2 != 0 {
444            return Err("Hex string has an odd length".parse::<u8>().unwrap_err()); 
445        }
446
447        (0..val.len())
448            .step_by(2)
449            .map(|i| u8::from_str_radix(&val[i..i + 2], 16))
450            .collect()
451    }
452
453    fn compare_macs(&self, mac_a: &[u8], mac_b: &[u8]) -> bool {
454        let mut acc = 0;
455
456        for (i, &byte) in mac_a.iter().enumerate() {
457            acc = acc | byte ^ mac_b[i];
458        }
459
460        acc == 0
461    }
462}