Tests, benchmarks, input stream

This commit is contained in:
Maya Herrscher committed 2024-07-23 14:00:25 +02:00
1 parent 1fd272c010
commit dc59f69e80
6 files changed
+838 -173

No files matched your search

+218
View File
@@ -0,0 +1,218 @@
use modinverse::modinverse;
struct Vr {
v1: u16,
v2: u16,
v3: u16,
v4: u16,
}
impl Vr {
fn split(v: u64) -> Self {
let v4 = (v & 0xFFFF) as u16;
let v3 = ((v >> 16) & 0xFFFF) as u16;
let v2 = ((v >> 32) & 0xFFFF) as u16;
let v1 = ((v >> 48) & 0xFFFF) as u16;
Self {
v1: v1,
v2: v2,
v3: v3,
v4: v4,
}
}
fn expand(&self) -> u64 {
(self.v1 as u64 * 2_u64.pow(48))
+ (self.v2 as u64 * 2_u64.pow(32))
+ (self.v3 as u64 * 2_u64.pow(16))
+ self.v4 as u64
}
}
struct Keys {
z1: u16,
z2: u16,
z3: u16,
z4: u16,
z5: u16,
z6: u16,
}
fn derive_keys(mut k: u128) -> Vec<Keys> {
let mut keys: Vec<Keys> = Vec::new();
let mut subkeys = Vec::new();
while subkeys.len() < 52 {
subkeys.push((k & 0xFFFF) as u16);
subkeys.push(((k >> 16) & 0xFFFF) as u16);
subkeys.push(((k >> 32) & 0xFFFF) as u16);
subkeys.push(((k >> 48) & 0xFFFF) as u16);
subkeys.push(((k >> 64) & 0xFFFF) as u16);
subkeys.push(((k >> 80) & 0xFFFF) as u16);
subkeys.push(((k >> 96) & 0xFFFF) as u16);
subkeys.push(((k >> 112) & 0xFFFF) as u16);
k = k.rotate_left(25);
}
let mut i = 0;
while keys.len() < 8 {
keys.push(Keys {
z1: subkeys[0 + i],
z2: subkeys[1 + i],
z3: subkeys[2 + i],
z4: subkeys[3 + i],
z5: subkeys[4 + i],
z6: subkeys[5 + i],
});
i = i + 1;
}
keys.push(Keys {
z1: subkeys[0 + i],
z2: subkeys[1 + i],
z3: subkeys[2 + i],
z4: subkeys[3 + i],
z5: 0,
z6: 0,
});
keys
}
fn add(a: u16, b: u16) -> u16 {
let (a, b, m) = (a as i32, b as i32, 0x10000 as i32);
((a + b) % m) as u16
}
// 0010 0110 % 10001 -> 0010 * (-0001) + 0110 -> 0110
fn slow_mul(a: u16, b: u16) -> u16 {
let (a, b, m) = (a as u32, b as u32, 0x10001 as u32);
let a = if a == 0 {0x10000} else {a};
let b = if b == 0 {0x10000} else {b};
((a * b) % m) as u16
}
fn mul(a: u16, b: u16) -> u16 {
let (a, b) = (a as u32, b as u32);
if a == 0 && b == 0 { return 1 }
let a = if a == 0 { 0x10000 } else { a };
let b = if b == 0 { 0x10000 } else { b };
let ab = a * b;
let ab_high = ab >> 16;
let ab_low = ab & 0xffff;
let m = if ab_high > ab_low { 0x10001 } else { 0 };
(ab_low + m - ab_high) as u16
}
fn idea_round(ks: &Keys, xs: Vr) -> Vr {
let u1 = mul(ks.z1, xs.v1);
let u2 = mul(ks.z2, xs.v2);
let u3 = add(ks.z3, xs.v3);
let u4 = add(ks.z4, xs.v4);
let u13 = u1 ^ u3;
let u24 = u2 ^ u4;
let u5 = mul(ks.z5, u13);
let c1 = mul(ks.z6, add(u24, u5));
let c2 = add(u5, c1);
Vr {
v1: c1 ^ u3,
v2: c2 ^ u4,
v3: c1 ^ u1,
v4: c2 ^ u2,
}
}
fn idea_finish(ks: &Keys, xs: Vr) -> Vr {
let u1 = mul(ks.z1, xs.v1);
let u2 = mul(ks.z2, xs.v2);
let u3 = add(ks.z3, xs.v3);
let u4 = add(ks.z4, xs.v4);
Vr {
v1: u1,
v2: u2,
v3: u3,
v4: u4,
}
}
fn invert_keys(ks: &Keys, z5: u16, z6: u16) -> Keys {
let mut z1 = ks.z1 as i32;
let mut z2 = ks.z2 as i32;
if ks.z1 == 0 {
z1 = 0x010000 as i32;
}
if ks.z2 == 0 {
z2 = 0x010000 as i32;
}
Keys {
z1: modinverse(z1 as i32, 0x0010001 as i32).expect("Modular inverse of z1 doesn't exist!")
as u16,
z2: modinverse(z2 as i32, 0x0010001 as i32).expect("Modular inverse of z2 doesn't exist!")
as u16,
z3: (0x10000 as u32 - ks.z3 as u32) as u16,
z4: (0x10000 as u32 - ks.z4 as u32) as u16,
z5: z5,
z6: z6,
}
}
pub fn encrypt(k: u128, m: u64) -> u64 {
let xr = Vr::split(m);
assert_eq!(m, xr.expand());
// Derive keys
let kr = derive_keys(k);
let mut bs = xr;
let mut i = 0;
while i < 8 {
// println!("Round {i}:");
bs = idea_round(&kr[i], bs);
// println!("intermediate result: {:?}\n", bs.expand());
i = i + 1;
}
let ys = idea_finish(&kr[8], bs);
ys.expand()
}
pub fn decrypt(k: u128, m: u64) -> u64 {
let xr = Vr::split(m);
assert_eq!(m, xr.expand());
// Derive and invert keys
let kr = derive_keys(k);
let mut dr: Vec<Keys> = Vec::new();
let mut i = 8;
while i > 0 {
let ds = invert_keys(&kr[i], kr[i - 1].z5, kr[i - 1].z6);
i = i - 1;
dr.push(ds);
}
let ds = invert_keys(&kr[0], 0, 0);
dr.push(ds);
let mut bs = xr;
while i < 8 {
// println!("Round {i}:");
bs = idea_round(&dr[i], bs);
// println!("intermediate result: {:?}\n", bs.expand());
i = i + 1;
}
let ys = idea_finish(&dr[8], bs);
ys.expand()
}
#[cfg(test)]
#[macro_use]
extern crate quickcheck;
#[cfg(test)]
mod tests {
use super::{decrypt, encrypt};
quickcheck! {
fn decrypt_encrypt(ks: u128, mes: u64) -> bool {
mes == decrypt(ks, encrypt(ks, mes))
}
}
use super::{slow_mul, mul};
quickcheck! {
fn correct_mul(a: u16, b: u16) -> bool {
mul(a, b) == slow_mul(a, b)
}
}
}
+54 -170
View File
@@ -1,8 +1,12 @@
// ca 5h
// ca 5h initial coding
use clap::Parser;
use clap::ArgAction;
use clap_num::maybe_hex;
use modinverse::modinverse;
use idea::{decrypt, encrypt};
use std::cmp::min;
use std::io::BufRead;
use std::io::Write;
#[derive(Parser)]
struct Cli {
@@ -12,177 +16,57 @@ struct Cli {
// the key used for encryption/decryption
#[arg(short = 'k', long = "key", value_parser=maybe_hex::<u128>, default_value_t = 123456789012345678901234567890)]
key: u128,
#[clap(long = "decrypt", short = 'd', action=ArgAction::SetFalse)]
decrypt: bool,
}
struct Vr {
v1: u16,
v2: u16,
v3: u16,
v4: u16,
}
impl Vr {
fn split(v: u64) -> Self {
let v4 = (v & 0xFFFF) as u16;
let v3 = ((v >> 16) & 0xFFFF) as u16;
let v2 = ((v >> 32) & 0xFFFF) as u16;
let v1 = ((v >> 48) & 0xFFFF) as u16;
Self{ v1: v1, v2: v2, v3: v3, v4: v4 }
}
fn expand(&self) -> u64 {
(self.v1 as u64 * 2_u64.pow(48)) + (self.v2 as u64 * 2_u64.pow(32)) + (self.v3 as u64 * 2_u64.pow(16)) + self.v4 as u64
}
}
struct Keys {
z1: u16, z2: u16, z3: u16, z4: u16, z5: u16, z6: u16,
}
fn derive_keys(mut k: u128) -> Vec<Keys> {
let mut keys: Vec<Keys> = Vec::new();
let mut subkeys = Vec::new();
while subkeys.len() < 52 {
subkeys.push((k & 0xFFFF) as u16);
subkeys.push(((k >> 16) & 0xFFFF) as u16);
subkeys.push(((k >> 32) & 0xFFFF) as u16);
subkeys.push(((k >> 48) & 0xFFFF) as u16);
subkeys.push(((k >> 64) & 0xFFFF) as u16);
subkeys.push(((k >> 80) & 0xFFFF) as u16);
subkeys.push(((k >> 96) & 0xFFFF) as u16);
subkeys.push(((k >> 112) & 0xFFFF) as u16);
k = k.rotate_left(25);
}
let mut i = 0;
while keys.len() < 8 {
keys.push(
Keys{ z1: subkeys[0+i], z2: subkeys[1+i], z3: subkeys[2+i], z4: subkeys[3+i], z5: subkeys[4+i], z6: subkeys[5+i] }
);
i = i+1;
}
keys.push(
Keys{ z1: subkeys[0+i], z2: subkeys[1+i], z3: subkeys[2+i], z4: subkeys[3+i], z5: 0, z6: 0 }
);
keys
}
fn add(a: u16, b: u16) -> u16 {
let (a, b, m) = (a as i32, b as i32, 0x10000 as i32);
((a + b) % m) as u16
}
fn mul(a: u16, b: u16) -> u16 {
let (a, b, m) = (a as u32, b as u32, 0x10001 as u32);
if a == 0 {
let a = 0x10000;
((a * b) % m) as u16
}
else if b == 0 {
let b = 0x10000;
((a * b) % m) as u16
}
else{
((a * b) % m) as u16
}
}
fn idea_round(ks: &Keys, xs: Vr) -> Vr {
let u1 = mul(ks.z1, xs.v1);
let u2 = mul(ks.z2, xs.v2);
let u3 = add(ks.z3, xs.v3);
let u4 = add(ks.z4, xs.v4);
let u13 = u1 ^ u3;
let u24 = u2 ^ u4;
let u5 = mul(ks.z5, u13);
let c1 = mul(ks.z6, add(u24, u5));
let c2 = add(u5, c1);
Vr { v1: c1 ^ u3, v2: c2 ^ u4, v3: c1 ^ u1, v4: c2 ^ u2 }
}
fn idea_finish(ks: &Keys, xs: Vr) -> Vr {
let u1 = mul(ks.z1, xs.v1);
let u2 = mul(ks.z2, xs.v2);
let u3 = add(ks.z3, xs.v3);
let u4 = add(ks.z4, xs.v4);
Vr { v1: u1, v2: u2, v3: u3, v4: u4 }
}
fn invert_keys(ks: &Keys, z5: u16, z6: u16) -> Keys {
let mut z1 = ks.z1 as i32;
let mut z2 = ks.z2 as i32;
if ks.z1 == 0 {
z1 = 0x010000 as i32;
}
if ks.z2 == 0 {
z2 = 0x010000 as i32;
}
Keys {
z1: modinverse(z1 as i32, 0x0010001 as i32).expect("Modular inverse of z1 doesn't exist!") as u16,
z2: modinverse(z2 as i32, 0x0010001 as i32).expect("Modular inverse of z2 doesn't exist!") as u16,
z3: (0x10000 as u32 - ks.z3 as u32) as u16,
z4: (0x10000 as u32 - ks.z4 as u32) as u16,
z5: z5,
z6: z6 }
}
fn encrypt(k: u128, m: u64) -> u64 {
let xr = Vr::split(m);
assert_eq!(m, xr.expand());
// Derive keys
let kr = derive_keys(k);
let mut bs = xr;
let mut i = 0;
while i < 8 {
println!("Round {i}:");
bs = idea_round(&kr[i], bs);
println!("intermediate result: {:?}\n", bs.expand());
i = i+1;
}
let ys = idea_finish(&kr[8], bs);
ys.expand()
}
fn decrypt(k: u128, m: u64) -> u64 {
let xr = Vr::split(m);
assert_eq!(m, xr.expand());
// Derive and invert keys
let kr = derive_keys(k);
let mut dr: Vec<Keys> = Vec::new();
let mut i = 8;
while i > 0 {
let ds = invert_keys(&kr[i], kr[i-1].z5, kr[i-1].z6);
i = i-1;
dr.push(ds);
}
let ds = invert_keys(&kr[0], 0, 0);
dr.push(ds);
let mut bs = xr;
while i < 8 {
println!("Round {i}:");
bs = idea_round(&dr[i], bs);
println!("intermediate result: {:?}\n", bs.expand());
i = i+1;
}
let ys = idea_finish(&dr[8], bs);
ys.expand()
}
fn main() {
println!("Welcome to your IDEA encryption tool!\n");
fn main() -> Result<(), Box<dyn std::error::Error>> {
let args = Cli::parse();
let key: u128 = args.key;
let key2: u128 = args.key;
let message: u64 = args.message;
// let message: u64 = args.message;
// println!("message: {:?}, key: {:?}\n", message, key);
println!("message: {:?}, key: {:?}\n", message, key);
let cipher: u64 = encrypt(key, message);
println!("Ciphertext: {:?}\n", cipher);
let plain: u64 = decrypt(key2, cipher);
println!("Computed plaintext: {:?}", plain);
let zeros = [0,0,0,0,0,0,0,0];
let mut inp = std::io::stdin().lock();
let mut out = std::io::stdout();
let mut buf: Vec<u8> = vec![0u8; 4096];
let mut amt = 0;
loop {
while amt < 8 {
let buff = inp.fill_buf()?;
if buff.is_empty() {
break;
}
let to_copy = min(buf.len() - amt, buff.len());
buf[amt..(amt + to_copy)].copy_from_slice(&buff[0..to_copy]);
amt += to_copy;
inp.consume(to_copy);
}
if amt < 8 {
let pad = 8 - amt;
buf[amt..8].copy_from_slice(&zeros[0..pad]);
amt += pad;
}
let mut offset = 0;
while amt >= offset + 8 {
let array: [u8; 8] = buf[offset..offset + 8].try_into().unwrap();
let plain = u64::from_le_bytes(array);
let cipher: u64;
if args.decrypt {
cipher = decrypt(key, plain);
}
else {
cipher = encrypt(key, plain);
}
out.write_all(&cipher.to_le_bytes())?;
offset += 8;
}
buf.copy_within(offset..amt, 0);
amt = amt - offset;
if amt == 0 {
return Ok(());
}
}
}
//