use crate::error::Result;
use blake2::{Blake2b512 as Blake2b512Impl, Blake2s256 as Blake2s256Impl, Digest as Blake2Digest};
use multihash_codetable::Code;
#[allow(unused_imports)]
use sha2::{Digest, Sha256 as Sha256Impl, Sha512 as Sha512Impl};
use sha3::{Sha3_256 as Sha3_256Impl, Sha3_512 as Sha3_512Impl};
use std::sync::Arc;
pub trait HashEngine: Send + Sync {
fn digest(&self, data: &[u8]) -> Vec<u8>;
fn code(&self) -> Code;
fn name(&self) -> &'static str;
#[inline]
fn is_simd_enabled(&self) -> bool {
false
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct CpuFeatures {
pub avx2: bool,
pub neon: bool,
}
impl CpuFeatures {
pub fn detect() -> Self {
#[cfg(target_arch = "x86_64")]
{
Self {
avx2: is_x86_feature_detected!("avx2"),
neon: false,
}
}
#[cfg(target_arch = "aarch64")]
{
Self {
avx2: false,
neon: true,
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
Self {
avx2: false,
neon: false,
}
}
}
}
pub struct Sha256Engine {
features: CpuFeatures,
}
impl Sha256Engine {
pub fn new() -> Self {
Self {
features: CpuFeatures::detect(),
}
}
#[cfg(target_arch = "x86_64")]
#[target_feature(enable = "avx2")]
unsafe fn digest_avx2(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
#[cfg(target_arch = "aarch64")]
fn digest_neon(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
fn digest_scalar(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha256Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
}
impl Default for Sha256Engine {
fn default() -> Self {
Self::new()
}
}
impl HashEngine for Sha256Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
#[cfg(target_arch = "x86_64")]
{
if self.features.avx2 {
unsafe { self.digest_avx2(data) }
} else {
self.digest_scalar(data)
}
}
#[cfg(target_arch = "aarch64")]
{
if self.features.neon {
self.digest_neon(data)
} else {
self.digest_scalar(data)
}
}
#[cfg(not(any(target_arch = "x86_64", target_arch = "aarch64")))]
{
self.digest_scalar(data)
}
}
#[inline]
fn code(&self) -> Code {
Code::Sha2_256
}
#[inline]
fn name(&self) -> &'static str {
"sha2-256"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
self.features.avx2 || self.features.neon
}
}
pub struct Sha512Engine {
features: CpuFeatures,
}
impl Sha512Engine {
pub fn new() -> Self {
Self {
features: CpuFeatures::detect(),
}
}
fn digest_scalar(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha512Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
}
impl Default for Sha512Engine {
fn default() -> Self {
Self::new()
}
}
impl HashEngine for Sha512Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
self.digest_scalar(data)
}
#[inline]
fn code(&self) -> Code {
Code::Sha2_512
}
#[inline]
fn name(&self) -> &'static str {
"sha2-512"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
self.features.avx2 || self.features.neon
}
}
pub struct Sha3_256Engine;
impl Sha3_256Engine {
pub fn new() -> Self {
Self
}
fn digest_impl(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha3_256Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
}
impl Default for Sha3_256Engine {
fn default() -> Self {
Self::new()
}
}
impl HashEngine for Sha3_256Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
self.digest_impl(data)
}
#[inline]
fn code(&self) -> Code {
Code::Sha3_256
}
#[inline]
fn name(&self) -> &'static str {
"sha3-256"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
false
}
}
pub struct Sha3_512Engine;
impl Sha3_512Engine {
pub fn new() -> Self {
Self
}
fn digest_impl(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Sha3_512Impl::new();
hasher.update(data);
hasher.finalize().to_vec()
}
}
impl Default for Sha3_512Engine {
fn default() -> Self {
Self::new()
}
}
impl HashEngine for Sha3_512Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
self.digest_impl(data)
}
#[inline]
fn code(&self) -> Code {
Code::Sha3_512
}
#[inline]
fn name(&self) -> &'static str {
"sha3-512"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
false
}
}
pub struct Blake3Engine;
impl Blake3Engine {
pub fn new() -> Self {
Self
}
}
impl Default for Blake3Engine {
fn default() -> Self {
Self::new()
}
}
impl HashEngine for Blake3Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = blake3::Hasher::new();
hasher.update(data);
hasher.finalize().as_bytes().to_vec()
}
#[inline]
fn code(&self) -> Code {
Code::Blake3_256
}
#[inline]
fn name(&self) -> &'static str {
"blake3"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Blake2b256Engine;
impl Blake2b256Engine {
pub fn new() -> Self {
Self
}
}
impl HashEngine for Blake2b256Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
use blake2::digest::FixedOutput;
let mut hasher = Blake2b512Impl::new();
Blake2Digest::update(&mut hasher, data);
let result = hasher.finalize_fixed();
result[..32].to_vec()
}
#[inline]
fn code(&self) -> Code {
Code::Blake2b256
}
#[inline]
fn name(&self) -> &'static str {
"blake2b-256"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Blake2b512Engine;
impl Blake2b512Engine {
pub fn new() -> Self {
Self
}
}
impl HashEngine for Blake2b512Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Blake2b512Impl::new();
Blake2Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[inline]
fn code(&self) -> Code {
Code::Blake2b512
}
#[inline]
fn name(&self) -> &'static str {
"blake2b-512"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
true
}
}
#[derive(Debug, Clone, Copy, Default)]
pub struct Blake2s256Engine;
impl Blake2s256Engine {
pub fn new() -> Self {
Self
}
}
impl HashEngine for Blake2s256Engine {
fn digest(&self, data: &[u8]) -> Vec<u8> {
let mut hasher = Blake2s256Impl::new();
Blake2Digest::update(&mut hasher, data);
hasher.finalize().to_vec()
}
#[inline]
fn code(&self) -> Code {
Code::Blake2s256
}
#[inline]
fn name(&self) -> &'static str {
"blake2s-256"
}
#[inline]
fn is_simd_enabled(&self) -> bool {
true
}
}
pub struct HashRegistry {
algorithms: std::collections::HashMap<u64, Arc<dyn HashEngine>>,
}
impl HashRegistry {
pub fn new() -> Self {
let mut registry = Self {
algorithms: std::collections::HashMap::new(),
};
registry.register(Arc::new(Sha256Engine::new()));
registry.register(Arc::new(Sha512Engine::new()));
registry.register(Arc::new(Sha3_256Engine::new()));
registry.register(Arc::new(Sha3_512Engine::new()));
registry.register(Arc::new(Blake2b256Engine::new()));
registry.register(Arc::new(Blake2b512Engine::new()));
registry.register(Arc::new(Blake2s256Engine::new()));
registry.register(Arc::new(Blake3Engine::new()));
registry
}
pub fn register(&mut self, engine: Arc<dyn HashEngine>) {
let code_u64 = engine.code() as u64;
self.algorithms.insert(code_u64, engine);
}
pub fn get(&self, code: Code) -> Option<Arc<dyn HashEngine>> {
let code_u64 = code as u64;
self.algorithms.get(&code_u64).cloned()
}
pub fn digest(&self, code: Code, data: &[u8]) -> Result<Vec<u8>> {
let engine = self.get(code).ok_or_else(|| {
crate::error::Error::InvalidInput(format!("Unsupported hash algorithm: {:?}", code))
})?;
Ok(engine.digest(data))
}
}
impl Default for HashRegistry {
fn default() -> Self {
Self::new()
}
}
static HASH_REGISTRY: once_cell::sync::Lazy<HashRegistry> =
once_cell::sync::Lazy::new(HashRegistry::new);
pub fn global_hash_registry() -> &'static HashRegistry {
&HASH_REGISTRY
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_cpu_feature_detection() {
let features = CpuFeatures::detect();
#[cfg(target_arch = "x86_64")]
{
assert!(!features.neon);
}
#[cfg(target_arch = "aarch64")]
{
assert!(features.neon);
assert!(!features.avx2);
}
}
#[test]
fn test_sha256_engine() {
let engine = Sha256Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_sha3_256_engine() {
let engine = Sha3_256Engine::new();
let hash = engine.digest(b"test data");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"test data");
assert_eq!(hash, hash2);
}
#[test]
fn test_hash_registry() {
let registry = HashRegistry::new();
let sha256 = registry.get(Code::Sha2_256);
assert!(sha256.is_some());
let sha512 = registry.get(Code::Sha2_512);
assert!(sha512.is_some());
let sha3_256 = registry.get(Code::Sha3_256);
assert!(sha3_256.is_some());
let sha3_512 = registry.get(Code::Sha3_512);
assert!(sha3_512.is_some());
}
#[test]
fn test_registry_digest() {
let registry = HashRegistry::new();
let hash = registry.digest(Code::Sha2_256, b"test").unwrap();
assert_eq!(hash.len(), 32);
}
#[test]
fn test_global_registry() {
let registry = global_hash_registry();
let hash = registry.digest(Code::Sha2_256, b"global test").unwrap();
assert_eq!(hash.len(), 32);
}
#[test]
fn test_sha256_deterministic() {
let engine = Sha256Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_sha512_engine() {
let engine = Sha512Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 64);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_sha3_512_engine() {
let engine = Sha3_512Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 64);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_sha512_deterministic() {
let engine = Sha512Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_sha3_512_deterministic() {
let engine = Sha3_512Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_sha512_vs_sha256() {
let sha512 = Sha512Engine::new();
let sha256 = Sha256Engine::new();
let data = b"test data";
let hash512 = sha512.digest(data);
let hash256 = sha256.digest(data);
assert_eq!(hash512.len(), 64);
assert_eq!(hash256.len(), 32);
assert_ne!(&hash512[..32], &hash256[..]);
}
#[test]
fn test_sha3_512_vs_sha3_256() {
let sha3_512 = Sha3_512Engine::new();
let sha3_256 = Sha3_256Engine::new();
let data = b"test data";
let hash512 = sha3_512.digest(data);
let hash256 = sha3_256.digest(data);
assert_eq!(hash512.len(), 64);
assert_eq!(hash256.len(), 32);
assert_ne!(&hash512[..32], &hash256[..]);
}
#[test]
fn test_blake3_engine() {
let engine = Blake3Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_blake3_deterministic() {
let engine = Blake3Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_blake3_simd_enabled() {
let engine = Blake3Engine::new();
assert!(engine.is_simd_enabled());
}
#[test]
fn test_blake3_vs_sha256() {
let blake3 = Blake3Engine::new();
let sha256 = Sha256Engine::new();
let data = b"test data for comparison";
let blake3_hash = blake3.digest(data);
let sha256_hash = sha256.digest(data);
assert_eq!(blake3_hash.len(), 32);
assert_eq!(sha256_hash.len(), 32);
assert_ne!(blake3_hash, sha256_hash);
}
#[test]
fn test_blake3_empty_input() {
let engine = Blake3Engine::new();
let hash = engine.digest(b"");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"");
assert_eq!(hash, hash2);
}
#[test]
fn test_blake2b256_engine() {
let engine = Blake2b256Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_blake2b512_engine() {
let engine = Blake2b512Engine::new();
let hash = engine.digest(b"hello world");
assert_eq!(hash.len(), 64);
let hash2 = engine.digest(b"hello world");
assert_eq!(hash, hash2);
let hash3 = engine.digest(b"hello mars");
assert_ne!(hash, hash3);
}
#[test]
fn test_blake2s256_engine() {
let engine = Blake2s256Engine::new();
let hash = engine.digest(b"test data");
assert_eq!(hash.len(), 32);
let hash2 = engine.digest(b"test data");
assert_eq!(hash, hash2);
}
#[test]
fn test_blake2b256_deterministic() {
let engine = Blake2b256Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_blake2b512_deterministic() {
let engine = Blake2b512Engine::new();
for size in [0, 1, 64, 256, 1024, 4096] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
assert_eq!(hash1.len(), 64);
}
}
#[test]
fn test_blake2s256_deterministic() {
let engine = Blake2s256Engine::new();
for size in [0, 1, 64, 256, 1024] {
let data = vec![42u8; size];
let hash1 = engine.digest(&data);
let hash2 = engine.digest(&data);
assert_eq!(hash1, hash2, "Failed for size {}", size);
}
}
#[test]
fn test_blake2b_vs_blake2s() {
let blake2b = Blake2b256Engine::new();
let blake2s = Blake2s256Engine::new();
let data = b"test data for comparison";
let blake2b_hash = blake2b.digest(data);
let blake2s_hash = blake2s.digest(data);
assert_eq!(blake2b_hash.len(), 32);
assert_eq!(blake2s_hash.len(), 32);
assert_ne!(blake2b_hash, blake2s_hash);
}
#[test]
fn test_blake2_empty_input() {
let blake2b256 = Blake2b256Engine::new();
let blake2b512 = Blake2b512Engine::new();
let blake2s = Blake2s256Engine::new();
let hash256 = blake2b256.digest(b"");
let hash512 = blake2b512.digest(b"");
let hashs = blake2s.digest(b"");
assert_eq!(hash256.len(), 32);
assert_eq!(hash512.len(), 64);
assert_eq!(hashs.len(), 32);
assert_eq!(hash256, blake2b256.digest(b""));
assert_eq!(hash512, blake2b512.digest(b""));
assert_eq!(hashs, blake2s.digest(b""));
}
#[test]
fn test_blake2_simd_enabled() {
let blake2b256 = Blake2b256Engine::new();
let blake2b512 = Blake2b512Engine::new();
let blake2s = Blake2s256Engine::new();
assert!(blake2b256.is_simd_enabled());
assert!(blake2b512.is_simd_enabled());
assert!(blake2s.is_simd_enabled());
}
#[test]
fn test_hash_registry_blake2() {
let registry = HashRegistry::new();
let blake2b256 = registry.get(Code::Blake2b256);
assert!(blake2b256.is_some());
let blake2b512 = registry.get(Code::Blake2b512);
assert!(blake2b512.is_some());
let blake2s256 = registry.get(Code::Blake2s256);
assert!(blake2s256.is_some());
}
#[test]
fn test_registry_digest_blake2() {
let registry = HashRegistry::new();
let hash256 = registry.digest(Code::Blake2b256, b"test").unwrap();
assert_eq!(hash256.len(), 32);
let hash512 = registry.digest(Code::Blake2b512, b"test").unwrap();
assert_eq!(hash512.len(), 64);
let hashs = registry.digest(Code::Blake2s256, b"test").unwrap();
assert_eq!(hashs.len(), 32);
}
#[test]
fn test_blake2_names() {
assert_eq!(Blake2b256Engine::new().name(), "blake2b-256");
assert_eq!(Blake2b512Engine::new().name(), "blake2b-512");
assert_eq!(Blake2s256Engine::new().name(), "blake2s-256");
}
#[test]
fn test_blake2_codes() {
assert_eq!(Blake2b256Engine::new().code(), Code::Blake2b256);
assert_eq!(Blake2b512Engine::new().code(), Code::Blake2b512);
assert_eq!(Blake2s256Engine::new().code(), Code::Blake2s256);
}
}