serpent-cipher 0.1.2

A naive pure Rust implementation of the Serpent block cipher.
Documentation
// #[macro_use]
// use serpent::generic_array;
// use block_cipher_trait::BlockCipher;

fn nibble_value(b: u8) -> u8 {
    match b {
        b'0'..=b'9' => b - b'0',
        b'a'..=b'f' => b - b'a' + 10,
        b'A'..=b'F' => b - b'A' + 10,
        _ => unreachable!(),
    }
}

fn bytes_from_hex_str(s: &str) -> Option<Vec<u8>> {
    let bytes = s.as_bytes();
    if bytes.is_empty() || bytes.len() % 2 == 1 || bytes.iter().any(|b| !b.is_ascii_hexdigit()) {
        return None;
    }

    Some(
        bytes
            .chunks_exact(2)
            .rev()
            .map(|chunk| nibble_value(chunk[0]) << 4 | nibble_value(chunk[1]))
            .collect(),
    )
}

fn block_from_bytes(bytes: &[u8]) -> u128 {
    use std::convert::TryInto;
    u128::from_le_bytes(bytes.try_into().unwrap())
}

#[test]
fn variable_key() {
    let vk_txt = include_str!("ecb_vk.txt");
    let mut pt = None;
    let mut key = None;
    for line in vk_txt.lines() {
        let mut parts = line.splitn(2, '=');
        match (parts.next(), parts.next()) {
            (Some("PT"), Some(v)) => pt = bytes_from_hex_str(v),
            (Some("KEY"), Some(v)) => key = Some(v),
            (Some("CT"), Some(v)) => {
                let ct = bytes_from_hex_str(v).unwrap();
                let cipher = serpent::Serpent::with_text_key(key.unwrap()).unwrap();
                let plain = block_from_bytes(pt.as_ref().unwrap());
                let truth = block_from_bytes(&ct);
                let encrypted = cipher.encrypt_block(plain);
                let decrypted = cipher.decrypt_block(encrypted);
                assert_eq!(encrypted, truth);
                assert_eq!(decrypted, plain);
            }
            _ => {}
        }
    }
}

#[test]
fn variable_text() {
    let vt_txt = include_str!("ecb_vt.txt");
    let mut pt = None;
    let mut key = None;
    for line in vt_txt.lines() {
        let mut parts = line.splitn(2, '=');
        match (parts.next(), parts.next()) {
            (Some("PT"), Some(v)) => pt = bytes_from_hex_str(v),
            (Some("KEY"), Some(v)) => key = Some(v),
            (Some("CT"), Some(v)) => {
                let ct = bytes_from_hex_str(v).unwrap();
                let cipher = serpent::Serpent::with_text_key(key.unwrap()).unwrap();
                let plain = block_from_bytes(pt.as_ref().unwrap());
                let truth = block_from_bytes(&ct);
                let encrypted = cipher.encrypt_block(plain);
                let decrypted = cipher.decrypt_block(encrypted);
                assert_eq!(encrypted, truth);
                assert_eq!(decrypted, plain);
            }
            _ => {}
        }
    }
}

#[test]
fn tables() {
    let vt_txt = include_str!("ecb_tbl.txt");
    let mut pt = None;
    let mut key = None;
    for line in vt_txt.lines() {
        let mut parts = line.splitn(2, '=');
        match (parts.next(), parts.next()) {
            (Some("PT"), Some(v)) => pt = bytes_from_hex_str(v),
            (Some("KEY"), Some(v)) => key = Some(v),
            (Some("CT"), Some(v)) => {
                let ct = bytes_from_hex_str(v).unwrap();
                let cipher = serpent::Serpent::with_text_key(key.unwrap()).unwrap();
                let plain = block_from_bytes(pt.as_ref().unwrap());
                let truth = block_from_bytes(&ct);
                let encrypted = cipher.encrypt_block(plain);
                let decrypted = cipher.decrypt_block(encrypted);
                assert_eq!(encrypted, truth);
                assert_eq!(decrypted, plain);
            }
            _ => {}
        }
    }
}