refactor: use u64 instead of u8[8] buffer; allow user to override salt.

This commit is contained in:
Jixun Wu
2024-09-12 00:18:12 +01:00
parent 8fcc07199b
commit f592acb220
3 changed files with 59 additions and 38 deletions

View File

@@ -17,12 +17,13 @@ categories = ["cryptography"]
maintenance = { status = "as-is" } maintenance = { status = "as-is" }
[dependencies] [dependencies]
byteorder = "1.5.0"
thiserror = "1.0.63" thiserror = "1.0.63"
rand = { version = "0.8.5" } rand = { version = "0.8.5", optional = true }
rand_chacha = { version = "0.3.1", optional = true } rand_chacha = { version = "0.3.1", optional = true }
rand_pcg = { version = "0.3.1", optional = true } rand_pcg = { version = "0.3.1", optional = true }
byteorder = "1.5.0"
[features] [features]
default = ["rand_pcg"] default = ["random", "random_secure"]
secure_random = ["rand/getrandom", "rand/rand_chacha", "rand_chacha"] random = ["rand", "rand_pcg"]
random_secure = ["rand/getrandom", "rand/rand_chacha", "rand_chacha"]

View File

@@ -53,7 +53,7 @@ pub fn encrypt<'a>(
let copy_to_header_len = min(16 - header_len, plain.len()); let copy_to_header_len = min(16 - header_len, plain.len());
let (plain_header, plain) = plain.split_at(copy_to_header_len); let (plain_header, plain) = plain.split_at(copy_to_header_len);
header[0] = (header[0] & 0b1111_1000) | ((pad_len as u8) & 0b0000_0111); header[0] = (header[0] & !7) | ((pad_len as u8) & 7);
header[header_len..header_len + copy_to_header_len].copy_from_slice(plain_header); header[header_len..header_len + copy_to_header_len].copy_from_slice(plain_header);
// Access to slice of "cipher" from inner scope // Access to slice of "cipher" from inner scope

View File

@@ -3,13 +3,11 @@
//! Notably, it uses a different round number and uses a "tweaked" CBC mode. //! Notably, it uses a different round number and uses a "tweaked" CBC mode.
use byteorder::{ByteOrder, BE}; use byteorder::{ByteOrder, BE};
use rand::RngCore;
use thiserror::Error; use thiserror::Error;
pub mod cbc; pub mod cbc;
pub mod ecb; pub mod ecb;
mod ecb_impl; mod ecb_impl;
use rand::prelude::*;
#[derive(Error, Debug, PartialEq)] #[derive(Error, Debug, PartialEq)]
pub enum TcTeaError { pub enum TcTeaError {
@@ -50,48 +48,70 @@ pub fn parse_key(key: &[u8]) -> Result<[u32; 4], TcTeaError> {
Ok(parsed) Ok(parsed)
} }
/// Encrypts an arbitrary length sized data in the following way: /// Generate salt for encryption function (or fixed salt if we are not using them)
/// fn generate_salt() -> [u8; 10] {
/// * PadLen (1 byte) #[cfg(not(feature = "random"))]
/// * Padding (variable, 0-7byte) {
/// * Salt (2 bytes) // Chosen by fair dice roll.
/// * Body (? bytes) // Guaranteed to be random.
/// * Zero (7 bytes) [0xA5, 0x6E, 0x35, 0xBC, 0x7C, 0x31, 0x04, 0x55, 0xA0, 0xBF]
/// }
/// Returned bytes will always have a length multiple of 8.
/// #[cfg(feature = "random")]
/// PadLen/Padding/Salt are randomly bytes, with a minimum of 21 bits (3 * 8 - 3) randomness. {
use rand::RngCore;
use rand::prelude::*;
let mut salt = [0u8; 10];
#[cfg(not(feature = "random_secure"))]
rand_pcg::Pcg32::from_entropy().fill_bytes(&mut salt);
#[cfg(feature = "random_secure")]
rand_chacha::ChaCha20Rng::from_entropy().fill_bytes(&mut salt);
salt
}
}
/// Encrypts given plain text using tc_tea.
/// ///
/// # Panics /// # Panics
/// ///
/// If random number generator fails, it will panic. /// If random number generator fails, it will panic.
pub fn encrypt<T: AsRef<[u8]>>(plaintext: T, key: &[u8]) -> Result<Vec<u8>, TcTeaError> { pub fn encrypt<T, K>(plaintext: T, key: K) -> Result<Vec<u8>, TcTeaError>
let key = parse_key(key)?; where
T: AsRef<[u8]>,
K: AsRef<[u8]>,
{
encrypt_with_salt(plaintext, key, &generate_salt())
}
/// Encrypts given plain text using tc_tea.
///
/// # Panics
///
/// If random number generator fails, it will panic.
pub fn encrypt_with_salt<T, K>(plaintext: T, key: K, salt: &[u8; 10]) -> Result<Vec<u8>, TcTeaError>
where
T: AsRef<[u8]>,
K: AsRef<[u8]>,
{
let key = parse_key(key.as_ref())?;
let plaintext = plaintext.as_ref(); let plaintext = plaintext.as_ref();
let cipher_len = get_encrypted_size(plaintext.len()); let cipher_len = get_encrypted_size(plaintext.len());
let mut cipher = vec![0u8; cipher_len]; let mut cipher = vec![0u8; cipher_len];
let mut salt = [0u8; 10];
#[cfg(feature = "secure_random")]
rand_chacha::ChaCha20Rng::from_entropy().fill_bytes(&mut salt);
#[cfg(not(feature = "secure_random"))]
rand_pcg::Pcg32::from_entropy().fill_bytes(&mut salt);
cbc::encrypt(&mut cipher, plaintext, &key, &salt)?; cbc::encrypt(&mut cipher, plaintext, &key, &salt)?;
Ok(cipher) Ok(cipher)
} }
/// Decrypts a byte array containing the following: /// Decrypts tc_tea encrypted data.
/// pub fn decrypt<T, K>(encrypted: T, key: K) -> Result<Vec<u8>, TcTeaError>
/// * PadLen (1 byte) where
/// * Padding (variable, 0-7byte) T: AsRef<[u8]>,
/// * Salt (2 bytes) K: AsRef<[u8]>,
/// * Body (? bytes) {
/// * Zero (7 bytes) let key = parse_key(key.as_ref())?;
///
/// PadLen is taken from the last 3 bit of the first byte.
pub fn decrypt<T: AsRef<[u8]>>(encrypted: T, key: &[u8]) -> Result<Vec<u8>, TcTeaError> {
let key = parse_key(key)?;
let encrypted = encrypted.as_ref(); let encrypted = encrypted.as_ref();
let mut plain = vec![0u8; encrypted.len()]; let mut plain = vec![0u8; encrypted.len()];
let result = cbc::decrypt(&mut plain, encrypted, &key)?; let result = cbc::decrypt(&mut plain, encrypted, &key)?;