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
//! An implementation of the [CryptoNight][1] digest algorithm.
//!
//! # Usage
//!
//! ```rust
//! # #[macro_use] extern crate hex_literal;
//! # fn main() {
//! use cryptonight_hash::{CryptoNight, Digest};
//!
//! // Create the CryptoNight hasher
//! let mut hasher = CryptoNight::new();
//!
//! // Input some data into the hasher
//! hasher.input(b"This is ");
//!
//! // Insert more data as needed.
//! hasher.input("a test");
//!
//! // Finalize the result. This will temporary allocate a 2MB buffer.
//! let result = hasher.result();
//!
//! assert_eq!(result[..], hex!("a084f01d1437a09c6985401b60d43554ae105802c5f5d8a9b3253649c0be6605")[..]);
//!
//! # }
//! ```
//!
//! Be sure to refer to the [RustCrypto/hashes][2] readme for more more
//! information about the Digest traits.
//!
//! [1]: https://cryptonote.org/cns/cns008.txt
//! [2]: https://github.com/RustCrypto/hashes
use std::alloc::{alloc, Layout};

use blake_hash::Blake256;
pub use digest::{BlockInput, Digest, FixedOutput, Input, Reset};
use digest::generic_array::GenericArray;
use digest::generic_array::typenum::{U200, U32};
use groestl::Groestl256;
use jh_x86_64::Jh256;
use skein_hash::Skein512;

mod aes;
#[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "aesni"))]
mod aesni;

const SCRATCH_PAD_SIZE: usize = 1 << 21;
const ROUNDS: usize = 524_288;

#[repr(align(16))]
/// Helper to enforce 16 byte alignment
struct A16<T>(pub T);

/// CryptoNight version 0 implementation.
#[derive(Debug, Default, Clone)]
pub struct CryptoNight {
    internal_hasher: sha3::Keccak256Full,
}


impl CryptoNight {
    fn digest_main(keccac: &mut [u8], scratchpad: &mut [u8]) {
        #[cfg(all(any(target_arch = "x86", target_arch = "x86_64"), feature = "aesni"))]
            {
                if is_x86_feature_detected!("aes") && is_x86_feature_detected!("sse4.1") {
                    return unsafe { aesni::digest_main(keccac, scratchpad) };
                }
            }

        aes::digest_main(keccac, scratchpad);
    }

    fn hash_final_state(state: &[u8]) -> GenericArray<u8, <Self as FixedOutput>::OutputSize> {
        match state[0] & 3 {
            0 => Blake256::digest(&state),
            1 => Groestl256::digest(&state),
            2 => Jh256::digest(&state),
            3 => Skein512::digest(&state),
            x => unreachable!("Invalid output option {}", x)
        }
    }
}

impl Input for CryptoNight {
    fn input<B: AsRef<[u8]>>(&mut self, data: B) {
        Input::input(&mut self.internal_hasher, data);
    }
}

impl Reset for CryptoNight {
    fn reset(&mut self) {
        Reset::reset(&mut self.internal_hasher);
    }
}

impl BlockInput for CryptoNight {
    type BlockSize = <sha3::Keccak256Full as BlockInput>::BlockSize;
}

impl FixedOutput for CryptoNight {
    type OutputSize = U32;

    fn fixed_result(self) -> GenericArray<u8, Self::OutputSize> {
        let mut keccac = A16(self.internal_hasher.fixed_result());
        let keccac = &mut keccac.0;

        let mut scratch_pad = unsafe {
            let buffer = alloc(Layout::from_size_align_unchecked(SCRATCH_PAD_SIZE, 16));
            Vec::from_raw_parts(buffer, SCRATCH_PAD_SIZE, SCRATCH_PAD_SIZE)
        };

        Self::digest_main(keccac, &mut scratch_pad);

        #[allow(clippy::cast_ptr_alignment)]
            tiny_keccak::keccakf(unsafe { &mut *(keccac as *mut GenericArray<u8, U200> as *mut [u64; 25]) });

        Self::hash_final_state(&keccac)
    }
}