fdn 0.8.6

A tool for uniformly change file or directory names and support rollback these operations
Documentation
use anyhow::{anyhow, Result};
use crypto::{
    aes::{self, KeySize},
    blockmodes,
    buffer::{self, BufferResult, ReadBuffer, WriteBuffer},
    digest::Digest,
    sha2::Sha256,
};
use rustc_serialize::hex::{FromHex, ToHex};

const IV: [u8; 16] = [0; 16];

pub fn hashed_name(s: &str) -> String {
    let mut sha = Sha256::new();
    sha.input_str(s);
    sha.result_str()
}

fn key_bytes(key_plain: &str, ks: KeySize) -> Vec<u8> {
    match ks {
        KeySize::KeySize128 => {
            let mut out = [0_u8; 32];
            let mut sha = Sha256::new();
            sha.input_str(key_plain);
            sha.result(&mut out);

            out[..16].to_vec()
        }
        KeySize::KeySize192 => {
            let mut out = [0_u8; 32];
            let mut sha = Sha256::new();
            sha.input_str(key_plain);
            sha.result(&mut out);

            out[..24].to_vec()
        }
        KeySize::KeySize256 => {
            let mut out = [0_u8; 32];
            let mut sha = Sha256::new();
            sha.input_str(key_plain);
            sha.result(&mut out);

            out[..].to_vec()
        }
    }
}

pub fn encrypted(plain: &str, key_plain: &str) -> Result<String> {
    let key_size = KeySize::KeySize128;

    let mut encryptor = aes::cbc_encryptor(
        key_size,
        &key_bytes(key_plain, key_size),
        &IV,
        blockmodes::PkcsPadding,
    );

    let mut final_result = Vec::<u8>::new();
    let mut read_buffer = buffer::RefReadBuffer::new(plain.as_bytes());
    let mut buffer = [0; 4096];
    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);

    loop {
        let result = encryptor
            .encrypt(&mut read_buffer, &mut write_buffer, true)
            .map_err(|e| anyhow!("Encryption failed: {:?}", e))?;

        final_result.extend(
            write_buffer
                .take_read_buffer()
                .take_remaining()
                .iter()
                .copied(),
        );

        match result {
            BufferResult::BufferUnderflow => break,
            BufferResult::BufferOverflow => {}
        }
    }

    Ok(final_result.to_hex())
}

pub fn decrypted(encrypted: &str, key_plain: &str) -> Result<String> {
    let key_size = KeySize::KeySize128;

    let mut decryptor = aes::cbc_decryptor(
        key_size,
        &key_bytes(key_plain, key_size),
        &IV,
        blockmodes::PkcsPadding,
    );

    let mut final_result = Vec::<u8>::new();
    let enc = encrypted.from_hex()?;
    let mut read_buffer = buffer::RefReadBuffer::new(&enc);
    let mut buffer = [0; 4096];
    let mut write_buffer = buffer::RefWriteBuffer::new(&mut buffer);

    loop {
        let result = decryptor
            .decrypt(&mut read_buffer, &mut write_buffer, true)
            .map_err(|e| anyhow!("Decryption failed: {:?}", e))?;

        final_result.extend(
            write_buffer
                .take_read_buffer()
                .take_remaining()
                .iter()
                .copied(),
        );
        match result {
            BufferResult::BufferUnderflow => break,
            BufferResult::BufferOverflow => {}
        }
    }

    Ok(final_result.to_hex())
}

#[cfg(test)]
mod tests {
    use rustc_serialize::hex::FromHex;

    use super::{decrypted, encrypted, hashed_name};

    #[test]
    fn test_hashed_name() {
        let s_v = [
            (
                "HelloWorld",
                "872e4e50ce9990d8b041330c47c9ddd11bec6b503ae9386a99da8584e9bb12c4",
            ),
            (
                "HelloWorld!",
                "729e344a01e52c822bdfdec61e28d6eda02658d2e7d2b80a9b9029f41e212dde",
            ),
        ];
        for (k, v) in s_v {
            let rlt = hashed_name(k);
            assert_eq!(rlt, v);
        }
    }

    #[test]
    fn test_enc_dec() {
        let plain = "HelloWorld";
        let k = "key";
        let enc = encrypted(plain, k).unwrap();
        let dec = decrypted(&enc, k).unwrap();
        let dec = dec.from_hex().unwrap();

        assert_eq!(plain, String::from_utf8(dec).unwrap());
    }
}