refactor: rename des_impl.rs

This commit is contained in:
鲁树人 2024-09-02 21:11:17 +01:00
parent d52819b4b3
commit 0806a97fb2
3 changed files with 124 additions and 119 deletions

View File

@ -1,109 +1,109 @@
use super::{constants, helper};
use crate::KuwoCryptoError;
use anyhow::Result;
use itertools::Either;
/// Encrypt or Decrypt?
pub enum Mode {
Encrypt,
Decrypt,
}
#[derive(Debug, Clone, Copy)]
pub struct KuwoDes {
subkeys: [u64; 16],
}
fn des_subkey_expansion(key: &[u8; 8], mode: Mode) -> [u64; 16] {
let key = u64::from_le_bytes(*key);
let mut param = helper::map_u64(key, &constants::KEY_PERMUTATION_TABLE);
let mut subkeys = [0u64; 16];
let subkey_iter = match mode {
Mode::Decrypt => Either::Left(subkeys.iter_mut().rev()),
Mode::Encrypt => Either::Right(subkeys.iter_mut()),
};
for ((subkey, shl), shl_mask) in subkey_iter
.zip(constants::KEY_RND_SHIFTS)
.zip(constants::KEY_SHIFT_LEFT_MASKS)
{
param = ((param & shl_mask) << (28 - shl)) | ((param & !shl_mask) >> (shl));
*subkey = helper::map_u64(param, &constants::KEY_COMPRESSION);
}
subkeys
}
fn des_subkey_round(state: u64, subkey: u64) -> u64 {
let old_left = helper::u64_get_hi32(state);
let old_right = helper::u64_get_lo32(state);
// Key expansion
let state = helper::map_u64(old_left as u64, &constants::KEY_EXPANSION);
let state = state ^ subkey;
// SBox transformation
let right = constants::SBOXES
.iter()
.zip(state.to_be_bytes())
.fold(0u32, |next, (sbox, b)| {
(next << 4) | (sbox[b as usize] as u32)
});
let right = helper::map_u32(right, &constants::PBOX);
let right = right ^ old_right;
helper::make_u64(right, old_left)
}
impl KuwoDes {
pub fn new(key: &[u8; 8], mode: Mode) -> Self {
Self {
subkeys: des_subkey_expansion(key, mode),
}
}
pub fn transform_block(&self, data: u64) -> u64 {
let mut state = helper::map_u64(data, &constants::IP);
state = self
.subkeys
.iter()
.fold(state, |state, &subkey| des_subkey_round(state, subkey));
// Swap data hi32/lo32
state = helper::swap_u64_side(state);
// Final permutation
state = helper::map_u64(state, &constants::IP_INV);
state
}
pub fn transform(&self, data: &mut [u8]) -> Result<()> {
if data.len() % 8 != 0 {
Err(KuwoCryptoError::InvalidDesDataSize(data.len()))?
}
for block in data.chunks_exact_mut(8) {
let value = u64::from_le_bytes(block.try_into()?);
let value = self.transform_block(value);
block.copy_from_slice(&value.to_le_bytes());
}
Ok(())
}
}
#[test]
fn test_des_decrypt() {
let mut input = [
0x36, 0x3C, 0x3E, 0x0D, 0x30, 0x31, 0xA4, 0x6C, 0xA0, 0xF0, 0x3A, 0xEC, 0x7F, 0x26, 0xF6,
0xF4,
];
let des = KuwoDes::new(b"ylzsxkwm", Mode::Decrypt);
des.transform(&mut input).unwrap();
assert_eq!(&input, b"12345678ABCDEFGH");
}
use super::{constants, helper};
use crate::KuwoCryptoError;
use anyhow::Result;
use itertools::Either;
/// Encrypt or Decrypt?
pub enum Mode {
Encrypt,
Decrypt,
}
#[derive(Debug, Clone, Copy)]
pub struct KuwoDes {
subkeys: [u64; 16],
}
fn des_subkey_expansion(key: &[u8; 8], mode: Mode) -> [u64; 16] {
let key = u64::from_le_bytes(*key);
let mut param = helper::map_u64(key, &constants::KEY_PERMUTATION_TABLE);
let mut subkeys = [0u64; 16];
let subkey_iter = match mode {
Mode::Decrypt => Either::Left(subkeys.iter_mut().rev()),
Mode::Encrypt => Either::Right(subkeys.iter_mut()),
};
for ((subkey, shl), shl_mask) in subkey_iter
.zip(constants::KEY_RND_SHIFTS)
.zip(constants::KEY_SHIFT_LEFT_MASKS)
{
param = ((param & shl_mask) << (28 - shl)) | ((param & !shl_mask) >> (shl));
*subkey = helper::map_u64(param, &constants::KEY_COMPRESSION);
}
subkeys
}
fn des_subkey_round(state: u64, subkey: u64) -> u64 {
let old_left = helper::u64_get_hi32(state);
let old_right = helper::u64_get_lo32(state);
// Key expansion
let state = helper::map_u64(old_left as u64, &constants::KEY_EXPANSION);
let state = state ^ subkey;
// SBox transformation
let right = constants::SBOXES
.iter()
.zip(state.to_be_bytes())
.fold(0u32, |next, (sbox, b)| {
(next << 4) | (sbox[b as usize] as u32)
});
let right = helper::map_u32(right, &constants::PBOX);
let right = right ^ old_right;
helper::make_u64(right, old_left)
}
impl KuwoDes {
pub fn new(key: &[u8; 8], mode: Mode) -> Self {
Self {
subkeys: des_subkey_expansion(key, mode),
}
}
pub fn transform_block(&self, data: u64) -> u64 {
let mut state = helper::map_u64(data, &constants::IP);
state = self
.subkeys
.iter()
.fold(state, |state, &subkey| des_subkey_round(state, subkey));
// Swap data hi32/lo32
state = helper::swap_u64_side(state);
// Final permutation
state = helper::map_u64(state, &constants::IP_INV);
state
}
pub fn transform(&self, data: &mut [u8]) -> Result<()> {
if data.len() % 8 != 0 {
Err(KuwoCryptoError::InvalidDesDataSize(data.len()))?
}
for block in data.chunks_exact_mut(8) {
let value = u64::from_le_bytes(block.try_into()?);
let value = self.transform_block(value);
block.copy_from_slice(&value.to_le_bytes());
}
Ok(())
}
}
#[test]
fn test_des_decrypt() {
let mut input = [
0x36, 0x3C, 0x3E, 0x0D, 0x30, 0x31, 0xA4, 0x6C, 0xA0, 0xF0, 0x3A, 0xEC, 0x7F, 0x26, 0xF6,
0xF4,
];
let des = KuwoDes::new(b"ylzsxkwm", Mode::Decrypt);
des.transform(&mut input).unwrap();
assert_eq!(&input, b"12345678ABCDEFGH");
}

View File

@ -1,20 +1,20 @@
pub(super) const fn make_u64(hi32: u32, lo32: u32) -> u64 {
pub const fn make_u64(hi32: u32, lo32: u32) -> u64 {
((hi32 as u64) << 32) | (lo32 as u64)
}
pub(super) const fn swap_u64_side(value: u64) -> u64 {
pub const fn swap_u64_side(value: u64) -> u64 {
(value.wrapping_shr(32)) | (value.wrapping_shl(32))
}
pub(super) const fn u64_get_lo32(value: u64) -> u32 {
pub const fn u64_get_lo32(value: u64) -> u32 {
value as u32
}
pub(super) const fn u64_get_hi32(value: u64) -> u32 {
pub const fn u64_get_hi32(value: u64) -> u32 {
value.wrapping_shr(32) as u32
}
pub(super) fn get_u64_by_shift_idx(value: u8) -> u64 {
pub fn get_u64_by_shift_idx(value: u8) -> u64 {
if value == 255 {
return 0;
}
@ -35,7 +35,7 @@ fn test_get_u64_by_shift_idx() {
assert_eq!(get_u64_by_shift_idx(63), 0x8000000000000000);
}
pub(super) fn map_u64(src_value: u64, table: &[u8]) -> u64 {
pub fn map_u64(src_value: u64, table: &[u8]) -> u64 {
table.iter().enumerate().fold(0u64, |acc, (i, &idx)| {
match get_u64_by_shift_idx(idx) & src_value {
0 => acc,
@ -44,6 +44,6 @@ pub(super) fn map_u64(src_value: u64, table: &[u8]) -> u64 {
})
}
pub(super) fn map_u32(src_value: u32, table: &[u8]) -> u32 {
pub fn map_u32(src_value: u32, table: &[u8]) -> u32 {
map_u64(src_value as u64, table) as u32
}

View File

@ -4,9 +4,9 @@ use base64::engine::{DecodePaddingMode, GeneralPurpose as Base64Engine, GeneralP
use base64::prelude::*;
mod constants;
mod des_impl;
mod core;
mod helper;
pub use des_impl::{KuwoDes, Mode};
use core::{KuwoDes, Mode};
/// Don't add padding when encoding, and require no padding when decoding.
const B64: Base64Engine = Base64Engine::new(
@ -38,12 +38,17 @@ pub fn encrypt_ksing<T: AsRef<[u8]>>(data: T, key: &[u8; 8]) -> Result<String> {
Ok(B64.encode(data))
}
pub fn decode_ekey<T: AsRef<[u8]>>(data: &str, key: &[u8; 8]) -> Result<String> {
let decoded = decrypt_ksing(data, key)?;
Ok(decoded[16..].to_string())
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn test_decode() {
fn test_ksing_decode() {
let expected = "hello world";
let decoded =
decrypt_ksing("tx5ct5ilzeLs7pN1C4RI6w==", b"12345678").expect("decrypt failed");