mod util;
mod rand;
use base64::{engine::general_purpose::URL_SAFE, Engine as _};
use std::error::Error;
use std::iter::repeat;
use crate::rand::SimpleRng;
use crate::util::{generate_password, mix, shuffle, unmix, unshuffle};
pub struct Cryptor {
matrix: usize,
}
const RANDOM_LEN: usize = 3;
impl Cryptor {
pub fn new() -> Self {
Self { matrix: 32 }
}
pub fn encrypt(&self, data: &[u8], key: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let matrix_size=self.matrix;
let pad = (matrix_size - ((10 + data.len()) % matrix_size)) % matrix_size;
let key_bytes = generate_password(matrix_size,key.as_bytes());
let data_len = data.len();
if data_len>u32::MAX as usize {
return Err("Data too Big".into());
}
let data_size = (data_len as u32).to_be_bytes();
let random_prefix = SimpleRng::new_with_time_seed().get_random_bytes(6);
let seed_random = random_prefix.iter().map(|&b| b as u16).sum::<u16>() as u64;
let mut padded_text = Vec::with_capacity(10 + data.len()+pad);
padded_text.extend_from_slice(&data_size);
padded_text.extend_from_slice(&random_prefix);
padded_text.extend_from_slice(data);
padded_text.extend(repeat(1).take(pad));
let seed_sum: u64 = key_bytes.iter().map(|&b| b as u64).sum();
shuffle(&mut padded_text,seed_sum.wrapping_add(seed_random),5);
let mut matrix = padded_text.chunks_exact_mut(matrix_size).collect::<Vec<_>>();
let matrix_len=matrix.len();
if matrix_len==0 {
return Err("Invalid Padding Length".into());
}
for i in 0..matrix_len {
let seed = matrix.get(i+1)
.map(|b| b[0] as u64)
.unwrap_or(key_bytes[0] as u64);
shuffle(&mut matrix[i], seed.wrapping_add(seed_random),2);
}
mix(matrix_size,&mut padded_text, &key_bytes);
let seed_random=(seed_random as u16).to_be_bytes();
padded_text.push(seed_random[0]);
padded_text.push(seed_random[1]);
Ok(padded_text)
}
pub fn encrypt_text(&self, text: &str, key: &str) -> Result<String, Box<dyn Error>> {
Ok(URL_SAFE.encode(self.encrypt(text.as_bytes(), key)?).trim_end_matches('=').to_string())
}
pub fn decrypt(&self, encoded: &Vec<u8>, key: &str) -> Result<Vec<u8>, Box<dyn Error>> {
let len=encoded.len();
if len < 6 {
return Err("Invalid Token Matrix Length".into());
}
let seed_random=u16::from_be_bytes([encoded[len - 2],encoded[len - 1]]) as u64;
let mut decoded = encoded[..len-2].to_vec();
let len=len-2;
let matrix_size=self.matrix;
let key_bytes = generate_password(matrix_size,key.as_bytes());
unmix(matrix_size,&mut decoded, &key_bytes);
let mut matrix = decoded.chunks_exact_mut(matrix_size).collect::<Vec<_>>();
let matrix_len=matrix.len();
for i in (0..matrix_len).rev() {
let seed = matrix.get(i+1)
.map(|b| b[0] as u64)
.unwrap_or(key_bytes[0] as u64);
unshuffle(&mut matrix[i], seed.wrapping_add(seed_random),2);
}
let seed_sum: u64 = key_bytes.iter().map(|&b| b as u64).sum();
unshuffle(&mut decoded, seed_sum.wrapping_add(seed_random),5);
let data_size = u32::from_be_bytes([decoded[0], decoded[1], decoded[2], decoded[3]]) as usize;
if len < data_size+10 {
return Err("Invalid Token Matrix Length".into());
}
let result_bytes = &decoded[10..data_size+10];
Ok(result_bytes.to_vec())
}
pub fn decrypt_text(&self, encoded: &str, key: &str) -> Result<String, Box<dyn Error>> {
let mut input = encoded.to_string();
let padding = input.len() % 4;
if padding != 0 {
input.push_str(&"=".repeat(4 - padding));
}
let data = URL_SAFE.decode(&input)?;
let result = String::from_utf8(self.decrypt(&data, &key)?)?
.to_string();
Ok(result)
}
pub fn set_matrix(&mut self, size: usize) {
if size>0{
self.matrix = size;
}
}
}