1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
//! Blake2b hash function
//!
//! [Official documentation](https://monocypher.org/manual/hash)

use ffi;
use libc::size_t;
use std::mem;

/// Simple function to hash the input data.
///
/// # Example
///
/// ```
/// use monocypher::hashing::blake2b::easy;
///
/// let hash = easy("tohash".as_bytes());
/// ```
pub fn easy(data: &[u8]) -> [u8; 64] {
    general(data, b"")
}


/// Function to hash the input data with an additional key.
///
/// # Example
///
/// ```
/// use monocypher::hashing::blake2b::general;
///
/// let hash = general("tohash".as_bytes(), "key".as_bytes());
/// ```
pub fn general(data: &[u8], key: &[u8]) -> [u8; 64] {
    unsafe {
        let mut hash: [u8; 64] = mem::uninitialized();
        ffi::crypto_blake2b_general(
            hash.as_mut_ptr(),
            64 as size_t,
            key.as_ptr(),
            key.len() as size_t,
            data.as_ptr(),
            data.len() as size_t,
        );
        hash
    }
}

pub struct Context(ffi::crypto_blake2b_ctx);

/// Context based hashing for e.g. large inputs.
///
/// # Example
///
/// ```
/// use monocypher::hashing::blake2b::Context;
///
/// let mut ctx = Context::new("tohash".as_bytes());
/// ctx.update("moretohash".as_bytes());
/// let hash = ctx.finalize();
/// ```
impl Context {
    #[inline]

    /// Initializes a new context with the given key.
    pub fn new(key: &[u8]) -> Context {
        unsafe {
            let mut ctx = mem::uninitialized();
            ffi::crypto_blake2b_general_init(&mut ctx, 64, key.as_ptr(), key.len());
            Context(ctx)
        }
    }

    /// Updates the context with the given data.
    #[inline]
    pub fn update(&mut self, data: &[u8]) {
        unsafe {
            ffi::crypto_blake2b_update(&mut self.0, data.as_ptr(), data.len());
        }
    }

    /// Finalizes the hash and returns it.
    #[inline]
    pub fn finalize(&mut self) -> [u8; 64] {
        unsafe {
            let mut hash: [u8; 64] = mem::uninitialized();
            ffi::crypto_blake2b_final(&mut self.0, hash.as_mut_ptr());
            hash
        }
    }
}

#[cfg(test)]
mod test {
    use hex;
    use super::*;

    #[test]
    fn blake2b_incremental() {
        let mut ctx = Context::new("test".as_bytes());
        ctx.update("TEST".as_bytes());
        let hash = ctx.finalize();
        assert_eq!(hex::encode(hash.to_vec()), "e33ee689585ebe3fc169a845482a47432c21a4134134d2f6c57d06dda4622500e73c79f3ab9d8a3728a7575ebb0f5a78bc6608db427e18cbba1ff6847e3fb6bb");
    }

    #[test]
    fn blake2b_len() {
        let vec = easy("TEST".as_bytes());
        assert_eq!(vec.len(), 64);
    }

    #[test]
    fn blake2b_sum() {
        let ret = easy("TEST".as_bytes()).to_vec();
        assert_eq!(hex::encode(ret), "5322bc39e200a6d2ef54ac6716376d5000f98a9715cb5293edd6e1e0f8865d3b22cb0fa92e09d52abef0cf58a2b067d4bc64fbee1e4bce0e9e642ce803dc6f99");
    }

    #[test]
    fn blake2b_general_len() {
        let vec = general("TEST".as_bytes(), "test".as_bytes());
        assert_eq!(vec.len(), 64);
    }

    #[test]
    fn blake2b_general_sum() {
        let ret = general("TEST".as_bytes(), "test".as_bytes()).to_vec();
        assert_eq!(hex::encode(ret), "e33ee689585ebe3fc169a845482a47432c21a4134134d2f6c57d06dda4622500e73c79f3ab9d8a3728a7575ebb0f5a78bc6608db427e18cbba1ff6847e3fb6bb");
    }
}