idea_crypto 0.2.9

Pure Rust implementation of the idea algorithm
Documentation
//! Pure Rust implementation of the idea algorithm.

#![cfg_attr(docsrs, feature(doc_cfg))]
#![allow(clippy::many_single_char_names)]

#[warn(rustdoc::broken_intra_doc_links)]
#[allow(warnings)]
pub use cipher;
use cipher::{consts::U16, AlgorithmName, Key, KeyInit, KeySizeUser};
use core::fmt;
use useful_macro::*;

#[cfg(feature = "zeroize")]
use cipher::zeroize::{Zeroize, ZeroizeOnDrop};

/// The International Data Encryption Algorithm (IDEA) block cipher.
#[derive(Clone, Copy)]
pub struct Idea {
    pub enc_keys: [u16; 52],
    pub dec_keys: [u16; 52],
}

impl Idea {
    pub fn expand_key(&mut self, key: &Key<Self>) {
        let length_key = key.len();
        for i in 0..(length_key / 2) {
            self.enc_keys[i] = (u16::from(key[2 * i]) << 8) + u16::from(key[2 * i + 1]);
        }

        let mut a: u16;
        let mut b: u16;
        for i in (length_key / 2)..52 {
            if (i + 1) % 8 == 0 {
                a = self.enc_keys[i - 15];
            } else {
                a = self.enc_keys[i - 7];
            }

            if (i + 2) % 8 < 2 {
                b = self.enc_keys[i - 14];
            } else {
                b = self.enc_keys[i - 6];
            }

            self.enc_keys[i] = (a << 9) + (b >> 7);
        }
    }

    pub fn invert_sub_keys(&mut self) {
        let mut k = 8 * 6;
        for i in 0..=8 {
            let j = i * 6;
            let l = k - j;

            let (m, n) = if i > 0 && i < 8 { (2, 1) } else { (1, 2) };

            self.dec_keys[j] = self.mul_inv(self.enc_keys[l]);
            self.dec_keys[j + 1] = self.add_inv(self.enc_keys[l + m]);
            self.dec_keys[j + 2] = self.add_inv(self.enc_keys[l + n]);
            self.dec_keys[j + 3] = self.mul_inv(self.enc_keys[l + 3]);
        }

        k = (8 - 1) * 6;
        for i in 0..8 {
            let j = i * 6;
            let l = k - j;
            self.dec_keys[j + 4] = self.enc_keys[l + 4];
            self.dec_keys[j + 5] = self.enc_keys[l + 5];
        }
    }
    #[allow(warnings)]
    pub fn crypt(&self, block: Vec<u8>, sub_keys: &[u16; 52]) -> Vec<u8> {
        let b = block.clone();
        let mut x1 = u16::from_be_bytes(b[0..2].try_into().unwrap());
        let mut x2 = u16::from_be_bytes(b[2..4].try_into().unwrap());
        let mut x3 = u16::from_be_bytes(b[4..6].try_into().unwrap());
        let mut x4 = u16::from_be_bytes(b[6..8].try_into().unwrap());

        for i in 0..8 {
            let j = i * 6;
            let y1 = self.mul(x1, sub_keys[j]);
            let y2 = self.add(x2, sub_keys[j + 1]);
            let y3 = self.add(x3, sub_keys[j + 2]);
            let y4 = self.mul(x4, sub_keys[j + 3]);

            let t0 = self.mul(y1 ^ y3, sub_keys[j + 4]);
            let _t = self.add(y2 ^ y4, t0);
            let t1 = self.mul(_t, sub_keys[j + 5]);
            let t2 = self.add(t0, t1);

            x1 = y1 ^ t1;
            x2 = y3 ^ t1;
            x3 = y2 ^ t2;
            x4 = y4 ^ t2;
        }

        let y1 = self.mul(x1, sub_keys[48]);
        let y2 = self.add(x3, sub_keys[49]);
        let y3 = self.add(x2, sub_keys[50]);
        let y4 = self.mul(x4, sub_keys[51]);

        let mut block = block.clone();
        block[0..2].copy_from_slice(&y1.to_be_bytes());
        block[2..4].copy_from_slice(&y2.to_be_bytes());
        block[4..6].copy_from_slice(&y3.to_be_bytes());
        block[6..8].copy_from_slice(&y4.to_be_bytes());
        block
    }

    pub fn mul(&self, a: u16, b: u16) -> u16 {
        let x = u32::from(a);
        let y = u32::from(b);
        let mut r: i32;

        if x == 0 {
            r = (0x10001 - y) as i32;
        } else if y == 0 {
            r = (0x10001 - x) as i32;
        } else {
            let c: u32 = x * y;
            r = ((c & 0xffff) as i32) - ((c >> 16) as i32);
            if r < 0 {
                r += 0x10001 as i32;
            }
        }

        (r & (0xffff as i32)) as u16
    }

    pub fn add(&self, a: u16, b: u16) -> u16 {
        ((u32::from(a) + u32::from(b)) & 0xffff) as u16
    }

    pub fn mul_inv(&self, a: u16) -> u16 {
        if a <= 1 {
            a
        } else {
            let mut x = u32::from(a);
            let mut y = 0x10001;
            let mut t0 = 1u32;
            let mut t1 = 0u32;
            loop {
                t1 += y / x * t0;
                y %= x;
                if y == 1 {
                    return (0x10001 - t1) as u16;
                }
                t0 += x / y * t1;
                x %= y;
                if x == 1 {
                    return t0 as u16;
                }
            }
        }
    }

    pub fn add_inv(&self, a: u16) -> u16 {
        ((0x10000 - (u32::from(a))) & 0xffff) as u16
    }
}

// impl BlockCipher for Idea {}

impl KeySizeUser for Idea {
    type KeySize = U16;
}

impl KeyInit for Idea {
    fn new(key: &Key<Self>) -> Self {
        let mut cipher = Self {
            enc_keys: [0u16; 52],
            dec_keys: [0u16; 52],
        };
        cipher.expand_key(key);
        cipher.invert_sub_keys();
        cipher
    }
}

impl fmt::Debug for Idea {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Idea { ... }")
    }
}

impl AlgorithmName for Idea {
    fn write_alg_name(f: &mut fmt::Formatter<'_>) -> fmt::Result {
        f.write_str("Idea")
    }
}

#[cfg(feature = "zeroize")]
#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
impl Drop for Idea {
    fn drop(&mut self) {
        self.enc_keys.zeroize();
        self.dec_keys.zeroize();
    }
}

#[cfg(feature = "zeroize")]
#[cfg_attr(docsrs, doc(cfg(feature = "zeroize")))]
impl ZeroizeOnDrop for Idea {}

fn en(data: &[u8], key: &[u8]) -> Vec<u8> {
    let idea = Idea::new_from_slice(&key).unwrap();
    let en = idea.crypt(data.to_vec(), &idea.enc_keys);
    en
}
fn de(cipher: &[u8], key: &[u8]) -> Vec<u8> {
    let idea = Idea::new_from_slice(&key).unwrap();
    let de = idea.crypt(cipher.to_vec(), &idea.dec_keys);
    de
}

#[allow(warnings)]
pub fn encrypt_not_any_key(data: &[u8], key: &[u8]) -> Vec<Vec<u8>> {
    let data = data.to_vec();
    let key = key.to_vec();
    let mut re = vec![];
    let mut count_push = 0;
    if data.len() == 8 {
        re.push(en(&data, &key));
    } else if data.len() < 8 {
        let mut new_data = data.clone().to_vec();
        for i in data.len()..8 {
            new_data.push(0);
            count_push += 1;
        }
        new_data.push(count_push);
        // println!("{:?}", new_data);
        re.push(en(&new_data, &key));
    } else if data.len() > 8 {
        let mut new_data = data.clone().to_vec();
        let mut new_vec_list = new_data
            .chunks(8)
            .into_iter()
            .map(|s| {
                let mut re = s.clone().to_vec();
                let ss = s.clone();
                if ss.len() < 8 {
                    for i in re.len()..8 {
                        re.push(0);
                        count_push += 1;
                    }
                    re.push(count_push);
                } else {
                    re = ss.to_vec();
                }
                re
            })
            .collect::<Vec<Vec<u8>>>();

        if new_vec_list
            .clone()
            .into_iter()
            .map(|s| s.len())
            .collect::<Vec<_>>()[new_vec_list.len() - 1]
            == 8
        {
            let mut nn = new_vec_list.clone();
            let mut last = vec_element_clone!(nn, nn.len() - 1);
            last.push(0);
            new_vec_list.remove(new_vec_list.len() - 1);
            new_vec_list.push(last);
        }

        for item in new_vec_list {
            re.push(en(&item, &key));
        }
    }
    re
}

#[allow(warnings)]
fn decrypt_not_any_key(cipher_vec: Vec<Vec<u8>>, key: &[u8]) -> Vec<Vec<u8>> {
    use useful_macro::*;
    let count_push: u8 = vec_element_clone!(
        &cipher_vec[cipher_vec.len() - 1],
        &cipher_vec[cipher_vec.len() - 1].len() - 1
    );
    let key = key.to_vec();
    let mut re = vec![];
    let mut last: Vec<u8> = Vec::new();
    for cipher in cipher_vec {
        let ss = de(&cipher, &key).into_iter().collect::<Vec<u8>>();
        re.push(ss);
    }

    let mut new_re_last: Vec<u8> = vec_element_clone!(re, re.len() - 1);
    for i in 0..count_push + 1 {
        new_re_last.remove(8 - i as usize);
    }
    re.remove(re.len() - 1);
    re.push(new_re_last);
    re
}

/// use display_decrypt() to print decrypted code
/// ```rust
///use idea_crypto::*;
///let data = "This is plaintext".as_bytes();
/////key length must be equal to 16
///let key = "this is password".as_bytes();
///let enc = encrypt(data, key);
///let dec = decrypt(enc.clone(), key);
///assert_eq!(enc,vec![vec![115, 247, 88, 166, 138, 225, 25, 243],vec![194, 236, 21, 196, 218, 159, 127, 117],vec![119, 7, 4, 154, 98, 218, 123, 31, 7]]);
///assert_eq!(display_decrypt(dec), format!("This is plaintext"));
///let data1 = "This is another plaintext".as_bytes();
///let key1 = "key length can be any length key length can be any length ".as_bytes();
///let enc1 = encrypt(data1, key1);
///let dec1 = decrypt(enc1, key1);
///assert_eq!(display_decrypt(dec1), format!("This is another plaintext"));
///encrypt_file("./0.txt", key1);
///decrypt_file("./0.txt", key1);
/// ```
pub fn display_decrypt(dec_data: Vec<Vec<u8>>) -> String {
    let mut re = String::new();
    for item in &dec_data {
        re.push_str(&String::from_utf8_lossy(item));
    }
    re
}

/// use encrypt_file() to encrypt file
/// ```rust
///use idea_crypto::encrypt_file;
///let key = "this is password".as_bytes();
///encrypt_file("./demo.tar.lzma", key);
/// ```
#[allow(warnings)]
pub fn encrypt_file(file_path: &str, key: &[u8]) {
    use std::fs;
    let data = fs::read(file_path).unwrap();

    let enc = encrypt(&data, &key);

    let mut new_enc = vec![];
    for i in enc {
        for j in i {
            new_enc.push(j);
        }
    }
    fs::write(file_path, &new_enc).unwrap();
}

/// use decrypt_file() to decrypt file
/// ```rust
///use idea_crypto::decrypt_file;
///let key = "this is password".as_bytes();
///decrypt_file("./demo.tar.lzma", key);
/// ```
#[allow(warnings)]
pub fn decrypt_file(file_path: &str, key: &[u8]) {
    use std::fs;
    let data = fs::read(file_path).unwrap();
    let mut enc = data
        .chunks(8)
        .into_iter()
        .map(|s| s.to_vec())
        .collect::<Vec<Vec<u8>>>();
    let count_push: u8 = vec_element_clone!(&enc[enc.len() - 1], &enc[enc.len() - 1].len() - 1);

    enc.remove(enc.len() - 1);
    let mut last = vec_element_clone!(enc, enc.len() - 1);
    last.push(count_push);
    enc.remove(enc.len() - 1);
    enc.push(last);

    let dec = decrypt(enc.clone(), key);
    let mut new_dec = vec![];
    for i in &dec {
        for j in i {
            new_dec.push(*j);
        }
    }

    fs::write(file_path, &new_dec).unwrap();
}

#[allow(warnings)]
pub fn make_key_length_any(key: &[u8]) -> Vec<Vec<u8>> {
    let l = key.len();
    let mut re = vec![key.clone().to_vec()];
    if key.len() == 16 {
    } else if key.len() < 16 {
        let mut new_key = key.clone().to_vec();
        for i in key.len()..16 {
            new_key.push(255);
        }
        re = vec![vec_slice!(new_key, [..])];
    } else if key.len() > 16 {
        let mut v_len16 = key
            .clone()
            .chunks(16)
            .into_iter()
            .map(|s| {
                let l = s.len();
                s.to_vec()
            })
            .collect::<Vec<_>>();
        let mut last = vec_element_clone!(v_len16, v_len16.len() - 1);
        for i in last.len()..16 {
            last.push(255);
        }
        v_len16.remove(v_len16.len() - 1);
        v_len16.push(last);
        re = v_len16.clone();
    }
    vec![vec_element_clone!(re, 0)]
}
/// use encrypt() to encrypt data,the password key length must be equal to 16
/// ```rust
///use idea_crypto::*;
///let data = "This is plaintext".as_bytes();
/////key length must be equal to 16
///let key = "this is password".as_bytes();
///let enc = encrypt(data, key);
///let dec = decrypt(enc.clone(), key);
///assert_eq!(enc,vec![vec![115, 247, 88, 166, 138, 225, 25, 243],vec![194, 236, 21, 196, 218, 159, 127, 117],vec![119, 7, 4, 154, 98, 218, 123, 31, 7]]);
///assert_eq!(display_decrypt(dec), format!("This is plaintext"));
///let data1 = "This is another plaintext".as_bytes();
///let key1 = "key length can be any length key length can be any length ".as_bytes();
///let enc1 = encrypt(data1, key1);
///let dec1 = decrypt(enc1, key1);
///assert_eq!(display_decrypt(dec1), format!("This is another plaintext"));
///encrypt_file("./0.txt", key1);
///decrypt_file("./0.txt", key1);
/// ```
#[allow(warnings)]
pub fn encrypt(data: &[u8], key: &[u8]) -> Vec<Vec<u8>> {
    let new_key = make_key_length_any(key.clone());
    let mut re = encrypt_not_any_key(data, &new_key[0]);
    if new_key.len() == 2 {
        re = encrypt_not_any_key(
            format!("{:?}", encrypt_not_any_key(data, &new_key[0])).as_bytes(),
            &new_key[1],
        );
    } else if new_key.len() == 3 {
        re = encrypt_not_any_key(
            format!(
                "{:?}",
                encrypt_not_any_key(
                    format!("{:?}", encrypt_not_any_key(data, &new_key[0])).as_bytes(),
                    &new_key[1],
                )
            )
            .as_bytes(),
            &new_key[2],
        );
    }
    re
}
/// use decrypt() to decrypt encrypted data
/// ```rust
///use idea_crypto::*;
///let data = "This is plaintext".as_bytes();
/////key length must be equal to 16
///let key = "this is password".as_bytes();
///let enc = encrypt(data, key);
///let dec = decrypt(enc.clone(), key);
///assert_eq!(enc,vec![vec![115, 247, 88, 166, 138, 225, 25, 243],vec![194, 236, 21, 196, 218, 159, 127, 117],vec![119, 7, 4, 154, 98, 218, 123, 31, 7]]);
///assert_eq!(display_decrypt(dec), format!("This is plaintext"));
///let data1 = "This is another plaintext".as_bytes();
///let key1 = "key length can be any length key length can be any length ".as_bytes();
///let enc1 = encrypt(data1, key1);
///let dec1 = decrypt(enc1, key1);
///assert_eq!(display_decrypt(dec1), format!("This is another plaintext"));
///encrypt_file("./0.txt", key1);
///decrypt_file("./0.txt", key1);
/// ```
#[allow(warnings)]
pub fn decrypt(cipher_vec: Vec<Vec<u8>>, key: &[u8]) -> Vec<Vec<u8>> {
    let new_key = make_key_length_any(key.clone());
    let mut re = decrypt_not_any_key(cipher_vec.clone(), &new_key[0]);
    if new_key.len() == 2 {
        re = decrypt_not_any_key(
            parse_u8(display_decrypt(decrypt_not_any_key(
                cipher_vec.clone(),
                &new_key[1],
            ))),
            &new_key[0],
        );
    } else if new_key.len() == 3 {
        re = decrypt_not_any_key(
            parse_u8(display_decrypt(decrypt_not_any_key(
                parse_u8(display_decrypt(decrypt_not_any_key(
                    cipher_vec.clone(),
                    &new_key[2],
                ))),
                &new_key[1],
            ))),
            &new_key[0],
        );
    }
    re
}

#[allow(warnings)]
fn parse_u8(v: String) -> Vec<Vec<u8>> {
    let mut v1 = v.replace("[[", "");
    v1 = v1.replace("\"", "");
    v1 = v1.replace(" ", "");
    v1 = v1.replace("[", "");
    v1 = v1.replace(" \"", "");
    v1.split("]")
        .filter(|s| !s.is_empty())
        .map(|s| {
            s.split(",")
                .filter(|s| !s.is_empty())
                .map(|s| s.parse::<u8>().unwrap())
                .collect::<Vec<u8>>()
        })
        .collect::<Vec<_>>()
}