blkar 7.2.7

Multithreaded archiver offering bit rot protection and sector level recoverability
Documentation
#![allow(dead_code)]

#[allow(non_camel_case_types)]
#[derive(Clone, Copy, Debug, PartialEq)]
pub enum HashType {
    SHA1,
    SHA2_256,
    SHA256,
    SHA2_512_256,
    SHA2_512_512,
    SHA512,
    BLAKE2B_256,
    BLAKE2B_512,
    BLAKE2S_128,
    BLAKE2S_256,
}

pub type HashBytes = (HashType, Box<[u8]>);

pub fn hash_type_to_string(hash_type: HashType) -> String {
    use self::HashType::*;

    match hash_type {
        SHA1 => String::from("SHA1"),
        SHA2_256 | SHA256 => String::from("SHA256"),
        SHA2_512_256 => String::from("SHA2-512-256"),
        SHA2_512_512 | SHA512 => String::from("SHA512"),
        BLAKE2B_256 => String::from("BLAKE2b-256"),
        BLAKE2B_512 => String::from("BLAKE2b-512"),
        BLAKE2S_128 => String::from("BLAKE2s-128"),
        BLAKE2S_256 => String::from("BLAKE2s-256"),
    }
}

pub fn string_to_hash_type(string: &str) -> Result<HashType, ()> {
    let string = string.to_lowercase();

    use self::HashType::*;

    let hash_type = match string.as_str() {
        "sha1" => Ok(SHA1),
        "sha2-256" | "sha256" => Ok(SHA256),
        "sha2-512-256" => Ok(SHA2_512_256),
        "sha2-512-512" | "sha512" => Ok(SHA512),
        "blake2b-256" => Ok(BLAKE2B_256),
        "blake2b-512" => Ok(BLAKE2B_512),
        "blake2s-128" => Ok(BLAKE2S_128),
        "blake2s-256" => Ok(BLAKE2S_256),
        _ => Err(()),
    };

    match hash_type {
        Ok(hs) => {
            if hash::hash_type_is_supported(hs) {
                Ok(hs)
            } else {
                Err(())
            }
        }
        Err(()) => Err(()),
    }
}

pub fn hash_bytes_to_bytes(hash_bytes: &HashBytes, buffer: &mut [u8]) {
    let param = specs::Param::new(hash_bytes.0);
    let digest_bytes = &hash_bytes.1;
    for i in 0..param.hash_func_type.len() {
        buffer[i] = param.hash_func_type[i];
    }

    buffer[param.hash_func_type.len()] = param.digest_length;

    let offset = param.hash_func_type.len() + 1;

    for i in 0..param.digest_length as usize {
        buffer[i + offset] = digest_bytes[i];
    }
}

pub fn hash_bytes_into_bytes(hash_bytes: &HashBytes) -> Box<[u8]> {
    let param = specs::Param::new(hash_bytes.0);
    let mut buffer = vec![0; param.total_length()].into_boxed_slice();

    hash_bytes_to_bytes(hash_bytes, &mut buffer);
    buffer
}

pub mod specs {
    use super::*;

    #[derive(Copy, Clone, Debug)]
    pub struct Param {
        pub hash_func_type: &'static [u8],
        pub digest_length: u8,
    }

    macro_rules! param {
        (
            $func_type:ident; $len:expr
        ) => {
            Param {
                hash_func_type: &$func_type,
                digest_length: $len,
            }
        };
    }

    static SHA1_HFT: [u8; 1] = [0x11];
    static SHA256_HFT: [u8; 1] = [0x12];
    static SHA512_HFT: [u8; 1] = [0x13];
    static BLAKE2B_256_HFT: [u8; 2] = [0xb2, 0x20];
    static BLAKE2B_512_HFT: [u8; 2] = [0xb2, 0x40];
    static BLAKE2S_128_HFT: [u8; 2] = [0xb2, 0x50];
    static BLAKE2S_256_HFT: [u8; 2] = [0xb2, 0x60];

    pub static SHA1_PARAM: Param = param!(SHA1_HFT; 0x14);
    pub static SHA256_PARAM: Param = param!(SHA256_HFT; 0x20);
    pub static SHA2_512_256_PARAM: Param = param!(SHA512_HFT; 0x20);
    pub static SHA512_PARAM: Param = param!(SHA512_HFT; 0x40);
    pub static BLAKE2B_256_PARAM: Param = param!(BLAKE2B_256_HFT; 0x20);
    pub static BLAKE2B_512_PARAM: Param = param!(BLAKE2B_512_HFT; 0x40);
    pub static BLAKE2S_128_PARAM: Param = param!(BLAKE2S_128_HFT; 0x10);
    pub static BLAKE2S_256_PARAM: Param = param!(BLAKE2S_256_HFT; 0x20);

    impl Param {
        pub fn new(hash_type: HashType) -> Param {
            use super::HashType::*;
            match hash_type {
                SHA1 => SHA1_PARAM,
                SHA2_256 | SHA256 => SHA256_PARAM,
                SHA2_512_256 => SHA2_512_256_PARAM,
                SHA2_512_512 | SHA512 => SHA512_PARAM,
                BLAKE2B_256 => BLAKE2B_256_PARAM,
                BLAKE2B_512 => BLAKE2B_512_PARAM,
                BLAKE2S_128 => BLAKE2S_128_PARAM,
                BLAKE2S_256 => BLAKE2S_256_PARAM,
            }
        }

        pub fn total_length(&self) -> usize {
            self.hash_func_type.len() + 1 + self.digest_length as usize
        }
    }
}

pub mod hash {
    use super::*;

    use blake2::{VarBlake2b, VarBlake2s};

    #[derive(Clone, Debug)]
    pub struct Ctx {
        ctx: _Ctx,
    }

    #[allow(non_camel_case_types)]
    #[derive(Clone, Debug)]
    enum _Ctx {
        SHA1(sha1::Sha1),
        SHA256(sha2::Sha256),
        SHA512(sha2::Sha512),
        BLAKE2B_256(VarBlake2b),
        BLAKE2B_512(VarBlake2b),
        BLAKE2S_128(VarBlake2s),
        BLAKE2S_256(VarBlake2s),
    }

    pub fn hash_type_is_supported(hash_type: HashType) -> bool {
        match Ctx::new(hash_type) {
            Ok(_) => true,
            Err(_) => false,
        }
    }

    impl Ctx {
        pub fn new(hash_type: HashType) -> Result<Ctx, ()> {
            let ctx = match hash_type {
                HashType::SHA1 => {
                    use sha1::Digest;
                    Some(_Ctx::SHA1(sha1::Sha1::new()))
                }
                HashType::SHA2_256 | HashType::SHA256 => {
                    use sha2::Digest;
                    Some(_Ctx::SHA256(sha2::Sha256::new()))
                }
                HashType::SHA2_512_256 => None,
                HashType::SHA2_512_512 | HashType::SHA512 => {
                    use sha2::Digest;
                    Some(_Ctx::SHA512(sha2::Sha512::new()))
                }
                HashType::BLAKE2B_256 => {
                    use blake2::digest::VariableOutput;
                    Some(_Ctx::BLAKE2B_256(
                        VarBlake2b::new(specs::Param::new(hash_type).digest_length as usize)
                            .unwrap(),
                    ))
                }
                HashType::BLAKE2B_512 => {
                    use blake2::digest::VariableOutput;
                    Some(_Ctx::BLAKE2B_512(
                        VarBlake2b::new(specs::Param::new(hash_type).digest_length as usize)
                            .unwrap(),
                    ))
                }
                HashType::BLAKE2S_128 => {
                    use blake2::digest::VariableOutput;
                    Some(_Ctx::BLAKE2S_128(
                        VarBlake2s::new(specs::Param::new(hash_type).digest_length as usize)
                            .unwrap(),
                    ))
                }
                HashType::BLAKE2S_256 => {
                    use blake2::digest::VariableOutput;
                    Some(_Ctx::BLAKE2S_256(
                        VarBlake2s::new(specs::Param::new(hash_type).digest_length as usize)
                            .unwrap(),
                    ))
                }
            };
            match ctx {
                Some(ctx) => Ok(Ctx { ctx }),
                None => Err(()),
            }
        }

        pub fn hash_type(&self) -> HashType {
            match self.ctx {
                _Ctx::SHA1(_) => HashType::SHA1,
                _Ctx::SHA256(_) => HashType::SHA256,
                _Ctx::SHA512(_) => HashType::SHA512,
                _Ctx::BLAKE2B_256(_) => HashType::BLAKE2B_256,
                _Ctx::BLAKE2B_512(_) => HashType::BLAKE2B_512,
                _Ctx::BLAKE2S_128(_) => HashType::BLAKE2S_128,
                _Ctx::BLAKE2S_256(_) => HashType::BLAKE2S_256,
            }
        }

        pub fn update(&mut self, data: &[u8]) {
            match self.ctx {
                _Ctx::SHA1(ref mut ctx) => {
                    use sha1::Digest;
                    ctx.input(data)
                }
                _Ctx::SHA256(ref mut ctx) => {
                    use sha2::Digest;
                    ctx.input(data)
                }
                _Ctx::SHA512(ref mut ctx) => {
                    use sha2::Digest;
                    ctx.input(data)
                }
                _Ctx::BLAKE2B_256(ref mut ctx) => {
                    use blake2::digest::Input;
                    ctx.input(data);
                }
                _Ctx::BLAKE2B_512(ref mut ctx) => {
                    use blake2::digest::Input;
                    ctx.input(data);
                }
                _Ctx::BLAKE2S_128(ref mut ctx) => {
                    use blake2::digest::Input;
                    ctx.input(data);
                }
                _Ctx::BLAKE2S_256(ref mut ctx) => {
                    use blake2::digest::Input;
                    ctx.input(data);
                }
            }
        }

        pub fn finish_to_bytes(self, hashval: &mut [u8]) {
            match self.ctx {
                _Ctx::SHA1(ctx) => {
                    use sha1::Digest;
                    hashval.copy_from_slice(&ctx.result())
                }
                _Ctx::SHA256(ctx) => {
                    use sha2::Digest;
                    hashval.copy_from_slice(&ctx.result())
                }
                _Ctx::SHA512(ctx) => {
                    use sha2::Digest;
                    hashval.copy_from_slice(&ctx.result())
                }
                _Ctx::BLAKE2B_256(ctx) => {
                    use blake2::digest::VariableOutput;
                    hashval.copy_from_slice(&ctx.vec_result())
                }
                _Ctx::BLAKE2B_512(ctx) => {
                    use blake2::digest::VariableOutput;
                    hashval.copy_from_slice(&ctx.vec_result())
                }
                _Ctx::BLAKE2S_128(ctx) => {
                    use blake2::digest::VariableOutput;
                    hashval.copy_from_slice(&ctx.vec_result())
                }
                _Ctx::BLAKE2S_256(ctx) => {
                    use blake2::digest::VariableOutput;
                    hashval.copy_from_slice(&ctx.vec_result())
                }
            }
        }

        pub fn finish_into_bytes(self) -> Box<[u8]> {
            let hash_type = self.hash_type();
            let param = specs::Param::new(hash_type);
            let digest_len = param.digest_length;
            let mut hashval = vec![0; digest_len as usize].into_boxed_slice();
            self.finish_to_bytes(&mut hashval);
            hashval
        }

        pub fn finish_to_hash_bytes(self, hash_bytes: &mut HashBytes) {
            hash_bytes.0 = self.hash_type();
            self.finish_to_bytes(&mut hash_bytes.1);
        }

        pub fn finish_into_hash_bytes(self) -> HashBytes {
            (self.hash_type(), self.finish_into_bytes())
        }
    }
}

pub mod parsers {
    use super::super::misc_utils;
    use super::specs;
    use super::{HashBytes, HashType};

    macro_rules! make_hash_parser_w_len {
        (
            $name:ident, $ht:path, $param:path
        ) => {
            named!(
                $name<HashBytes>,
                do_parse!(
                    _total_len: tag!(&[$param.total_length() as u8])
                        >> _id: tag!($param.hash_func_type)
                        >> _n: tag!(&[$param.digest_length])
                        >> res: take!($param.digest_length)
                        >> (($ht, misc_utils::slice_to_boxed(res)))
                )
            );
        };
    }

    make_hash_parser_w_len!(sha1_w_len_p, HashType::SHA1, specs::SHA1_PARAM);
    make_hash_parser_w_len!(sha256_w_len_p, HashType::SHA256, specs::SHA256_PARAM);
    make_hash_parser_w_len!(
        sha2_512_256_w_len_p,
        HashType::SHA2_512_256,
        specs::SHA2_512_256_PARAM
    );
    make_hash_parser_w_len!(sha512_w_len_p, HashType::SHA512, specs::SHA512_PARAM);
    make_hash_parser_w_len!(
        blake2b_256_w_len_p,
        HashType::BLAKE2B_256,
        specs::BLAKE2B_256_PARAM
    );
    make_hash_parser_w_len!(
        blake2b_512_w_len_p,
        HashType::BLAKE2B_512,
        specs::BLAKE2B_512_PARAM
    );
    make_hash_parser_w_len!(
        blake2s_128_w_len_p,
        HashType::BLAKE2S_128,
        specs::BLAKE2S_128_PARAM
    );
    make_hash_parser_w_len!(
        blake2s_256_w_len_p,
        HashType::BLAKE2S_256,
        specs::BLAKE2S_256_PARAM
    );

    named!(pub multihash_w_len_p <HashBytes>,
           alt!(
               complete!(sha1_w_len_p)
                   | complete!(sha256_w_len_p)
                   | complete!(sha2_512_256_w_len_p)
                   | complete!(sha512_w_len_p)
                   | complete!(blake2b_256_w_len_p)
                   | complete!(blake2b_512_w_len_p)
                   | complete!(blake2s_128_w_len_p)
                   | complete!(blake2s_256_w_len_p)
           )
    );
}