use std::num::ParseIntError;
use crate::hash::TequelHash;
use crate::error::TequelError;
use crate::rng::TequelRng;
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
use crate::avx2_inline::{ add, xor, storeu, loadu, setone, sub };
use zeroize::{Zeroize, ZeroizeOnDrop};
#[cfg(feature = "serde")]
use serde::{Serialize, Deserialize};
#[derive(Debug, Zeroize, ZeroizeOnDrop, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TequelEncrypt {
pub salt: String,
pub iterations: u32,
}
#[derive(Debug, Zeroize, ZeroizeOnDrop, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct TequelEncryption {
pub encrypted_data: String,
pub salt: String,
pub mac: String
}
impl TequelEncrypt {
pub fn new() -> Self {
Self {
salt: "".to_string(),
iterations: 30
}
}
pub fn with_salt(mut self, salt: &str) -> Self {
self.salt = salt.to_string();
self
}
pub fn with_iteration(mut self, value: u32) -> Self{
self.iterations = value;
self
}
pub fn encrypt(&mut self, data: &[u8], key: &str) -> Result<TequelEncryption, TequelError> {
if self.salt.as_bytes().len() == 0 {
let tequel_rng = TequelRng::new();
self.salt = tequel_rng.rand_by_nano().to_string();
}
let key_salt = self.salt.as_bytes(); let key_crypt = key.as_bytes();
if key_crypt.len() == 0 {
return Err(TequelError::EmptyKey("Key is empty".to_string()))
}
let a = 0x107912fau32.to_be_bytes(); let b = 0x220952eau32.to_be_bytes(); let c = 0x3320212au32.to_be_bytes(); let d = 0x4324312fu32.to_be_bytes(); let e = 0x5320212au32.to_be_bytes();
let mut res_bytes = Vec::with_capacity(data.len());
unsafe {
if is_x86_feature_detected!("avx2") {
let chunks = data.chunks_exact(64);
let remainder = chunks.remainder();
for (chunk_idx, chunk) in chunks.enumerate() {
let byte_offset = chunk_idx * 32; let mut ymm1 = loadu(chunk.as_ptr() as *const __m256i);
ymm1 = add(ymm1, setone(a[0] as i8));
ymm1 = xor(ymm1, setone(b[0] as i8));
ymm1 = add(ymm1, setone(c[0] as i8));
ymm1 = xor(ymm1, setone(d[0] as i8));
ymm1 = add(ymm1, setone(e[0] as i8));
let mut expanded_key = [0u8; 32];
for i in 0..32 {
expanded_key[i] = key_crypt[(byte_offset + i) % key_crypt.len()];
}
ymm1 = xor(ymm1, loadu(expanded_key.as_ptr() as *const __m256i));
let mut out = [0u8; 32];
storeu(out.as_mut_ptr() as *mut __m256i, ymm1);
res_bytes.extend_from_slice(&out);
}
let processed_bytes = data.len() - remainder.len();
for (i, &byte) in remainder.iter().enumerate() {
let g_idx = processed_bytes + i;
let mut curr = byte;
curr = curr.wrapping_add(a[g_idx % 4]);
curr = curr ^ b[g_idx % 4];
curr = curr.wrapping_add(c[g_idx % 4]);
curr = curr ^ d[g_idx % 4];
curr = curr.wrapping_add(e[g_idx % 4]);
curr = curr ^ key_crypt[g_idx % key_crypt.len()];
res_bytes.push(curr)
}
}
}
let res = res_bytes.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>();
let salt_res = key_salt.iter()
.map(|b| format!("{:02x}", b))
.collect::<String>();
let mut mixmac_buffer = Vec::with_capacity(20 + res_bytes.len() + key_salt.len() + key_crypt.len());
mixmac_buffer.extend_from_slice(&a);
mixmac_buffer.extend_from_slice(&res_bytes);
mixmac_buffer.extend_from_slice(&b);
mixmac_buffer.extend_from_slice(&c);
mixmac_buffer.extend_from_slice(&key_salt);
mixmac_buffer.extend_from_slice(&d);
mixmac_buffer.extend_from_slice(&key_crypt);
mixmac_buffer.extend_from_slice(&e);
let mut cus_teq_hash = TequelHash::new();
let comb_mixmac = cus_teq_hash.tqlhash(&mixmac_buffer);
Ok(TequelEncryption { encrypted_data: res, salt: salt_res, mac: comb_mixmac })
}
pub fn decrypt(&mut self, tequel_encryption: &TequelEncryption, key: &str) -> Result<String, TequelError> {
if key.is_empty() { return Err(TequelError::EmptyKey("Key is empty".to_string())); }
let a = 0x107912fau32.to_be_bytes(); let b = 0x220952eau32.to_be_bytes(); let c = 0x3320212au32.to_be_bytes(); let d = 0x4324312fu32.to_be_bytes(); let e = 0x5320212au32.to_be_bytes();
let encrypted_data = self.decode_hex(&tequel_encryption.encrypted_data).map_err(|e| {
TequelError::InvalidHex(e.to_string())
})?;
let salt_hex = self.decode_hex(&tequel_encryption.salt).map_err(|e| {
TequelError::InvalidHex(e.to_string())
})?;
let mut mixmac_buffer = Vec::with_capacity(20 + encrypted_data.len() + key.len() + salt_hex.len());
mixmac_buffer.extend_from_slice(&a);
mixmac_buffer.extend_from_slice(&encrypted_data);
mixmac_buffer.extend_from_slice(&b);
mixmac_buffer.extend_from_slice(&c);
mixmac_buffer.extend_from_slice(&salt_hex);
mixmac_buffer.extend_from_slice(&d);
mixmac_buffer.extend_from_slice(&key.as_bytes());
mixmac_buffer.extend_from_slice(&e);
let mut cus_teq_hash = TequelHash::new();
let comb_mixmac = cus_teq_hash.tqlhash(&mixmac_buffer).to_lowercase();
if !self.compare_macs(tequel_encryption.mac.to_lowercase().as_bytes(), comb_mixmac.as_bytes()) {
return Err(TequelError::InvalidMac)
}
let encrypted_data = self.decode_hex(&tequel_encryption.encrypted_data).map_err(|e| {
TequelError::InvalidHex(e.to_string())
})?;
let mut res_bytes = Vec::with_capacity(encrypted_data.len());
let key_encrypt_input = key.as_bytes();
unsafe {
if is_x86_feature_detected!("avx2") {
let chunks = encrypted_data.chunks_exact(32);
let remainder = chunks.remainder();
for (chunk_idx, chunk) in chunks.enumerate() {
let byte_offset = chunk_idx * 32;
let mut ymm1 = loadu(chunk.as_ptr() as *const __m256i);
let mut expanded_key = [0u8; 32];
for i in 0..32 {
expanded_key[i] = key_encrypt_input[(byte_offset + i) % key_encrypt_input.len()];
}
ymm1 = xor(ymm1, loadu(expanded_key.as_ptr() as *const __m256i));
ymm1 = sub(ymm1, setone(e[0] as i8));
ymm1 = xor(ymm1, setone(d[0] as i8));
ymm1 = sub(ymm1, setone(c[0] as i8));
ymm1 = xor(ymm1, setone(b[0] as i8));
ymm1 = sub(ymm1, setone(a[0] as i8));
let mut out = [0u8; 32];
storeu(out.as_mut_ptr() as *mut __m256i, ymm1);
res_bytes.extend_from_slice(&out);
}
let processed_bytes = encrypted_data.len() - remainder.len();
for (i, &byte) in remainder.iter().enumerate() {
let g_idx = processed_bytes + i;
let mut curr = byte;
curr ^= key_encrypt_input[g_idx % key_encrypt_input.len()];
curr = curr.wrapping_sub(e[g_idx % 4]);
curr ^= d[g_idx % 4];
curr = curr.wrapping_sub(c[g_idx % 4]);
curr ^= b[g_idx % 4];
curr = curr.wrapping_sub(a[g_idx % 4]);
res_bytes.push(curr)
}
}
}
let res = String::from_utf8(res_bytes).map_err(|_| TequelError::InvalidUtf8)?;
Ok(res)
}
fn decode_hex(&self, val: &str) -> Result<Vec<u8>, ParseIntError> {
if val.len() % 2 != 0 {
return Err("Hex string has an odd length".parse::<u8>().unwrap_err());
}
(0..val.len())
.step_by(2)
.map(|i| u8::from_str_radix(&val[i..i + 2], 16))
.collect()
}
fn compare_macs(&self, mac_a: &[u8], mac_b: &[u8]) -> bool {
let mut acc = 0;
for (i, &byte) in mac_a.iter().enumerate() {
acc = acc | byte ^ mac_b[i];
}
acc == 0
}
}