dynhash 0.1.0

Dynamic trait-object hashing library
Documentation
//! Simple dynamic trait-object based hashing library
//!
//! This library allows you to define a thin non-generic interface, over which
//! you can pass various hashes and hash functions. This is done using Trait
//! Objects.
//!
//! While every crate can define and implement custom Hash / HashType structs,
//! this library has built-in support for the
//! - sha3
//! - sha2
//!
//! crates.
//!
//! These can be enabled by the "sha2" or "sha3" features respectively.
//!
//! ## Example
//!
//! Let's suppose you're writing a web-server library that accepts a hash from
//! a client. The hash and hash type are passed as a byte slice and &str
//! respectively. The web-server shouldn't have any knowledge about the
//! allowed hash types, these can be passed in at runtime:
//!
//! ```
//! use dynhash::{Hash, HashType};
//!
//! fn new_hash(
//!     available_types: &[&'static HashType],
//!     digest: &[u8],
//!     hash_type: &str,
//! ) -> Option<Box<Hash>> {
//!     for h in available_types.iter() {
//!         if h.as_string() == hash_type {
//!             return h.new_with_content(digest).ok();
//!         }
//!     }
//!
//!     return None;
//! }
//! ```
//!
//! This allows the web-server library to support all kinds of hashes, that the
//! higher-level crate chooses to support.

#[macro_use]
extern crate mopa;

#[cfg(any(feature = "sha3", feature = "doc"))]
extern crate sha3 as lib_sha3;
#[cfg(any(feature = "sha3", feature = "doc"))]
pub mod sha3;

#[cfg(any(feature = "sha2", feature = "doc"))]
extern crate sha2 as lib_sha2;
#[cfg(any(feature = "sha2", feature = "doc"))]
pub mod sha2;

use std::fmt;
use std::io::Error as IoError;

#[derive(Debug)]
pub enum HashError {
    IncorrectSize,
}

pub trait HashType {
    fn as_string(&self) -> &'static str;
    fn new_with_content(&self, f: &[u8]) -> Result<Box<Hash>, HashError>;
    fn len(&self) -> usize;
}

impl fmt::Debug for HashType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "HashType<{}>", self.as_string())
    }
}

pub trait Hash: mopa::Any + std::fmt::Debug + std::fmt::LowerHex + std::fmt::UpperHex {
    fn hash_type(&self) -> &'static HashType
    where
        Self: Sized;
    fn as_slice(&self) -> &[u8];
}
mopafy!(Hash);

pub trait Hasher {
    type HasherOutput: Hash;
    fn output_type() -> &'static HashType;

    fn new() -> Self;
    fn input(&mut self, buf: &[u8]) -> Result<(), IoError>;
    fn result(self) -> Box<Self::HasherOutput>;
}

#[macro_export]
macro_rules! new_hash {
    ( $hname:ident, $tname:ident, $len:expr, $str:expr ) => {
        #[allow(non_camel_case_types)]
        pub struct $tname;
        impl HashType for $tname {
            fn as_string(&self) -> &'static str {
                $str
            }

            fn len(&self) -> usize {
                $len
            }

            fn new_with_content(&self, c: &[u8]) -> Result<Box<Hash>, HashError> {
                if c.len() != self.len() {
                    Err(self::HashError::IncorrectSize)
                } else {
                    let mut arr: Box<$hname> = Box::new($hname([0; $len]));
                    arr.0.copy_from_slice(c);
                    Ok(arr as Box<Hash>)
                }
            }
        }

        #[allow(non_camel_case_types)]
        pub struct $hname(pub [u8; $len]);
        impl Hash for $hname {
            fn hash_type(&self) -> &'static HashType {
                &$tname as &HashType
            }

            fn as_slice(&self) -> &[u8] {
                &self.0
            }
        }

        impl std::fmt::LowerHex for $hname {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                for &b in self.as_slice().iter() {
                    write!(f, "{:02x}", b)?;
                }

                Ok(())
            }
        }
        impl std::fmt::UpperHex for $hname {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                for &b in self.as_slice().iter() {
                    write!(f, "{:02X}", b)?;
                }

                Ok(())
            }
        }
        impl std::fmt::Debug for $hname {
            fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
                write!(f, "Hash<{}, {:#02x}>", self.hash_type().as_string(), self)
            }
        }
    };
}

#[test]
fn test_struct_sizes() {
    assert!(std::mem::size_of::<sha2::HashType_Sha2_512>() == 0);
    assert!(std::mem::size_of::<sha3::HashType_Sha3_512>() == 0);

    assert!(std::mem::size_of::<sha2::Hash_Sha2_256>() == 32);
    assert!(std::mem::size_of::<sha3::Hash_Sha3_512>() == 64);
}

#[test]
fn test_downcast() {
    let sha3_hash: Box<dyn Hash> = Box::new(sha3::Hash_Sha3_512([0xAF as u8; 64]));
    let _sha3_hash: Box<sha3::Hash_Sha3_512> = sha3_hash.downcast().unwrap();

    let sha2_hash: Box<dyn Hash> = Box::new(sha2::Hash_Sha2_256([0xFA as u8; 32]));
    let maybe_sha3_hash: Result<Box<sha3::Hash_Sha3_256>, Box<dyn Hash>> = sha2_hash.downcast();
    maybe_sha3_hash.unwrap_err();
}