#![forbid(unsafe_code)]
#![no_std]
extern crate alloc;
#[cfg(feature = "std")]
extern crate std;
mod hash_builder;
mod parallelhash;
mod xof;
pub use hash_builder::{DynStreamingHash, HashAlgorithm, HashBuilder, StreamingHashBuilder};
pub use parallelhash::{
parallel_hash128, parallel_hash128_xof, parallel_hash256, parallel_hash256_xof,
ParallelHash128, ParallelHash256,
};
pub use xof::{
blake2b_keyed, cshake128, cshake256, shake128, shake128_start, shake256, shake256_start,
tuple_hash128, tuple_hash256, Blake2bKeyed, Shake128Reader, Shake256Reader,
};
#[cfg(feature = "std")]
pub use xof::{hash_file_blake3, hash_file_sha256, hash_file_sha512};
use alloc::vec::Vec;
use digest::Digest;
use oxicrypto_core::{CryptoError, Hash, StreamingHash};
pub struct DigestStreamingAdapter<D: Digest + Default> {
inner: D,
}
impl<D: Digest + Default> DigestStreamingAdapter<D> {
pub fn new() -> Self {
Self {
inner: D::default(),
}
}
}
impl<D: Digest + Default> Default for DigestStreamingAdapter<D> {
fn default() -> Self {
Self::new()
}
}
impl<D: Digest + Default + Clone> Clone for DigestStreamingAdapter<D> {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl<D: Digest + Default + Send> StreamingHash for DigestStreamingAdapter<D> {
fn update(&mut self, data: &[u8]) {
Digest::update(&mut self.inner, data);
}
fn finalize(self, out: &mut [u8]) -> Result<(), CryptoError> {
let result = Digest::finalize(self.inner);
if out.len() < result.len() {
return Err(CryptoError::BufferTooSmall);
}
out[..result.len()].copy_from_slice(&result);
Ok(())
}
fn reset(&mut self) {
self.inner = D::default();
}
}
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha256;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha384;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha512;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha512_256;
impl Hash for Sha256 {
fn name(&self) -> &'static str {
"SHA-256"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha2::Sha256::digest(msg);
out[..32].copy_from_slice(&digest);
Ok(())
}
}
impl Hash for Sha384 {
fn name(&self) -> &'static str {
"SHA-384"
}
fn output_len(&self) -> usize {
48
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 48 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha2::Sha384::digest(msg);
out[..48].copy_from_slice(&digest);
Ok(())
}
}
impl Hash for Sha512 {
fn name(&self) -> &'static str {
"SHA-512"
}
fn output_len(&self) -> usize {
64
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 64 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha2::Sha512::digest(msg);
out[..64].copy_from_slice(&digest);
Ok(())
}
}
impl Hash for Sha512_256 {
fn name(&self) -> &'static str {
"SHA-512/256"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha2::Sha512_256::digest(msg);
out[..32].copy_from_slice(&digest);
Ok(())
}
}
impl Sha256 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 64;
}
impl Sha384 {
pub const DIGEST_LEN: usize = 48;
pub const BLOCK_SIZE: usize = 128;
}
impl Sha512 {
pub const DIGEST_LEN: usize = 64;
pub const BLOCK_SIZE: usize = 128;
}
impl Sha512_256 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 128;
}
pub type Sha256Streaming = DigestStreamingAdapter<sha2::Sha256>;
pub type Sha384Streaming = DigestStreamingAdapter<sha2::Sha384>;
pub type Sha512Streaming = DigestStreamingAdapter<sha2::Sha512>;
pub type Sha512_256Streaming = DigestStreamingAdapter<sha2::Sha512_256>;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha3_256;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha3_384;
#[derive(Debug, Default, Clone, Copy)]
pub struct Sha3_512;
impl Hash for Sha3_256 {
fn name(&self) -> &'static str {
"SHA3-256"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha3::Sha3_256::digest(msg);
out[..32].copy_from_slice(&digest);
Ok(())
}
}
impl Hash for Sha3_384 {
fn name(&self) -> &'static str {
"SHA3-384"
}
fn output_len(&self) -> usize {
48
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 48 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha3::Sha3_384::digest(msg);
out[..48].copy_from_slice(&digest);
Ok(())
}
}
impl Hash for Sha3_512 {
fn name(&self) -> &'static str {
"SHA3-512"
}
fn output_len(&self) -> usize {
64
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 64 {
return Err(CryptoError::BufferTooSmall);
}
let digest = sha3::Sha3_512::digest(msg);
out[..64].copy_from_slice(&digest);
Ok(())
}
}
impl Sha3_256 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 136;
}
impl Sha3_384 {
pub const DIGEST_LEN: usize = 48;
pub const BLOCK_SIZE: usize = 104;
}
impl Sha3_512 {
pub const DIGEST_LEN: usize = 64;
pub const BLOCK_SIZE: usize = 72;
}
pub type Sha3_256Streaming = DigestStreamingAdapter<sha3::Sha3_256>;
pub type Sha3_384Streaming = DigestStreamingAdapter<sha3::Sha3_384>;
pub type Sha3_512Streaming = DigestStreamingAdapter<sha3::Sha3_512>;
#[derive(Debug, Default, Clone, Copy)]
pub struct Blake2b256;
#[derive(Debug, Default, Clone, Copy)]
pub struct Blake2b512;
#[derive(Debug, Default, Clone, Copy)]
pub struct Blake2s256;
impl Hash for Blake2b256 {
fn name(&self) -> &'static str {
"BLAKE2b-256"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let result = blake2::Blake2b256::digest(msg);
out[..32].copy_from_slice(&result);
Ok(())
}
}
impl Hash for Blake2b512 {
fn name(&self) -> &'static str {
"BLAKE2b-512"
}
fn output_len(&self) -> usize {
64
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 64 {
return Err(CryptoError::BufferTooSmall);
}
let result = blake2::Blake2b512::digest(msg);
out[..64].copy_from_slice(&result);
Ok(())
}
}
impl Hash for Blake2s256 {
fn name(&self) -> &'static str {
"BLAKE2s-256"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let result = blake2::Blake2s256::digest(msg);
out[..32].copy_from_slice(&result);
Ok(())
}
}
impl Blake2b256 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 128;
}
impl Blake2b512 {
pub const DIGEST_LEN: usize = 64;
pub const BLOCK_SIZE: usize = 128;
}
impl Blake2s256 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 64;
}
pub type Blake2b256Streaming = DigestStreamingAdapter<blake2::Blake2b256>;
pub type Blake2b512Streaming = DigestStreamingAdapter<blake2::Blake2b512>;
pub type Blake2s256Streaming = DigestStreamingAdapter<blake2::Blake2s256>;
#[derive(Debug, Default, Clone, Copy)]
pub struct Blake3;
impl Hash for Blake3 {
fn name(&self) -> &'static str {
"BLAKE3"
}
fn output_len(&self) -> usize {
32
}
fn hash(&self, msg: &[u8], out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let digest = blake3::hash(msg);
out[..32].copy_from_slice(digest.as_bytes());
Ok(())
}
}
impl Blake3 {
pub const DIGEST_LEN: usize = 32;
pub const BLOCK_SIZE: usize = 64;
}
pub struct Blake3Streaming {
inner: blake3::Hasher,
}
impl Blake3Streaming {
pub fn new() -> Self {
Self {
inner: blake3::Hasher::new(),
}
}
}
impl Default for Blake3Streaming {
fn default() -> Self {
Self::new()
}
}
impl Clone for Blake3Streaming {
fn clone(&self) -> Self {
Self {
inner: self.inner.clone(),
}
}
}
impl StreamingHash for Blake3Streaming {
fn update(&mut self, data: &[u8]) {
self.inner.update(data);
}
fn finalize(self, out: &mut [u8]) -> Result<(), CryptoError> {
if out.len() < 32 {
return Err(CryptoError::BufferTooSmall);
}
let result = self.inner.finalize();
out[..32].copy_from_slice(result.as_bytes());
Ok(())
}
fn reset(&mut self) {
self.inner.reset();
}
}
#[cfg(feature = "std")]
impl<D: Digest + Default + Send> std::io::Write for DigestStreamingAdapter<D> {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(feature = "std")]
impl std::io::Write for Blake3Streaming {
fn write(&mut self, buf: &[u8]) -> std::io::Result<usize> {
self.update(buf);
Ok(buf.len())
}
fn flush(&mut self) -> std::io::Result<()> {
Ok(())
}
}
#[cfg(feature = "std")]
pub fn sha256_hex(msg: &[u8]) -> std::string::String {
let digest = sha2::Sha256::digest(msg);
bytes_to_hex(digest.as_ref())
}
#[cfg(feature = "std")]
pub fn sha384_hex(msg: &[u8]) -> std::string::String {
let digest = sha2::Sha384::digest(msg);
bytes_to_hex(digest.as_ref())
}
#[cfg(feature = "std")]
pub fn sha512_hex(msg: &[u8]) -> std::string::String {
let digest = sha2::Sha512::digest(msg);
bytes_to_hex(digest.as_ref())
}
#[cfg(feature = "std")]
pub fn sha3_256_hex(msg: &[u8]) -> std::string::String {
let digest = sha3::Sha3_256::digest(msg);
bytes_to_hex(digest.as_ref())
}
#[cfg(feature = "std")]
pub fn blake3_hex(msg: &[u8]) -> std::string::String {
let digest = blake3::hash(msg);
bytes_to_hex(digest.as_bytes())
}
#[cfg(feature = "std")]
fn bytes_to_hex(bytes: &[u8]) -> std::string::String {
bytes.iter().fold(
std::string::String::with_capacity(bytes.len() * 2),
|mut s, b| {
let _ = std::fmt::write(&mut s, format_args!("{b:02x}"));
s
},
)
}
pub struct Blake3Keyed {
key: [u8; 32],
}
impl Blake3Keyed {
pub fn new(key: [u8; 32]) -> Self {
Self { key }
}
pub fn hash(&self, msg: &[u8]) -> [u8; 32] {
*blake3::keyed_hash(&self.key, msg).as_bytes()
}
}
pub fn blake3_keyed_hash(key: &[u8; 32], msg: &[u8]) -> [u8; 32] {
*blake3::keyed_hash(key, msg).as_bytes()
}
pub fn blake3_derive_key(context: &str, key_material: &[u8]) -> [u8; 32] {
blake3::derive_key(context, key_material)
}
pub fn blake3_xof(msg: &[u8], output_len: usize) -> Vec<u8> {
let mut out = alloc::vec![0u8; output_len];
let mut reader = blake3::Hasher::new().update(msg).finalize_xof();
reader.fill(&mut out);
out
}
#[cfg(test)]
mod tests {
use super::*;
fn hex_decode(s: &str) -> Vec<u8> {
(0..s.len())
.step_by(2)
.map(|i| u8::from_str_radix(&s[i..i + 2], 16).expect("valid hex"))
.collect()
}
#[test]
fn sha256_empty() {
let hasher = Sha256;
let result = hasher.hash_to_vec(b"").unwrap();
let expected =
hex_decode("e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855");
assert_eq!(result, expected, "SHA-256 of empty string mismatch");
}
#[test]
fn sha256_abc() {
let hasher = Sha256;
let result = hasher.hash_to_vec(b"abc").unwrap();
let expected =
hex_decode("ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad");
assert_eq!(result, expected, "SHA-256 of 'abc' mismatch");
}
#[test]
fn sha384_abc() {
let hasher = Sha384;
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 48);
}
#[test]
fn sha512_abc() {
let hasher = Sha512;
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 64);
}
#[test]
fn sha3_256_output_len() {
let hasher = Sha3_256;
assert_eq!(hasher.output_len(), 32);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 32);
}
#[test]
fn sha3_384_output_len() {
let hasher = Sha3_384;
assert_eq!(hasher.output_len(), 48);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 48);
}
#[test]
fn sha3_512_output_len() {
let hasher = Sha3_512;
assert_eq!(hasher.output_len(), 64);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 64);
}
#[test]
fn blake3_abc() {
let hasher = Blake3;
let result = hasher.hash_to_vec(b"abc").unwrap();
let expected =
hex_decode("6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85");
assert_eq!(result, expected, "BLAKE3 of 'abc' mismatch");
}
#[test]
fn buffer_too_small_error() {
let hasher = Sha256;
let mut out = [0u8; 16];
let err = hasher.hash(b"test", &mut out).unwrap_err();
assert_eq!(err, CryptoError::BufferTooSmall);
}
#[test]
fn sha256_streaming_hello_world() {
let one_shot = Sha256.hash_to_vec(b"hello world").unwrap();
let mut streamer = Sha256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn sha256_streaming_one_byte_chunks() {
let one_shot = Sha256.hash_to_vec(b"abc").unwrap();
let mut streamer = Sha256Streaming::new();
for byte in b"abc" {
StreamingHash::update(&mut streamer, core::slice::from_ref(byte));
}
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn sha256_streaming_reset() {
let expected = Sha256.hash_to_vec(b"world").unwrap();
let mut streamer = Sha256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::reset(&mut streamer);
StreamingHash::update(&mut streamer, b"world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), expected.as_slice());
}
#[test]
fn sha256_streaming_buffer_too_small() {
let mut streamer = Sha256Streaming::new();
StreamingHash::update(&mut streamer, b"test");
let mut buf = [0u8; 16];
let err = StreamingHash::finalize(streamer, &mut buf).unwrap_err();
assert_eq!(err, CryptoError::BufferTooSmall);
}
#[test]
fn blake3_streaming_equivalence() {
let one_shot = Blake3.hash_to_vec(b"hello world").unwrap();
let mut streamer = Blake3Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn blake3_streaming_reset() {
let expected = Blake3.hash_to_vec(b"world").unwrap();
let mut streamer = Blake3Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::reset(&mut streamer);
StreamingHash::update(&mut streamer, b"world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), expected.as_slice());
}
#[test]
fn blake2b256_output_len() {
let hasher = Blake2b256;
assert_eq!(hasher.output_len(), 32);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 32);
}
#[test]
fn blake2b256_empty_nonzero() {
let result = Blake2b256.hash_to_vec(b"").unwrap();
assert_eq!(result.len(), 32);
assert!(
result.iter().any(|&b| b != 0),
"BLAKE2b-256 of empty should be non-zero"
);
}
#[test]
fn blake2b256_streaming_equivalence() {
let one_shot = Blake2b256.hash_to_vec(b"hello world").unwrap();
let mut streamer = Blake2b256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn blake2b512_output_len() {
let hasher = Blake2b512;
assert_eq!(hasher.output_len(), 64);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 64);
}
#[test]
fn blake2b512_streaming_equivalence() {
let one_shot = Blake2b512.hash_to_vec(b"hello world").unwrap();
let mut streamer = Blake2b512Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 64];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn blake2s256_output_len() {
let hasher = Blake2s256;
assert_eq!(hasher.output_len(), 32);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 32);
}
#[test]
fn blake2s256_streaming_equivalence() {
let one_shot = Blake2s256.hash_to_vec(b"hello world").unwrap();
let mut streamer = Blake2s256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn sha512_256_output_len() {
let hasher = Sha512_256;
assert_eq!(hasher.output_len(), 32);
let result = hasher.hash_to_vec(b"abc").unwrap();
assert_eq!(result.len(), 32);
}
#[test]
fn sha512_256_known_vector() {
let result = Sha512_256.hash_to_vec(b"abc").unwrap();
let expected =
hex_decode("53048e2681941ef99b2e29b76b4c7dabe4c2d0c634fc6d46e0e2f13107e7af23");
assert_eq!(result, expected, "SHA-512/256 of 'abc' mismatch");
}
#[test]
fn sha512_256_streaming_equivalence() {
let one_shot = Sha512_256.hash_to_vec(b"hello world").unwrap();
let mut streamer = Sha512_256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
StreamingHash::update(&mut streamer, b" world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).unwrap();
assert_eq!(buf.as_ref(), one_shot.as_slice());
}
#[test]
fn blake3_keyed_different_keys() {
let key1 = [1u8; 32];
let key2 = [2u8; 32];
let msg = b"same message";
let out1 = Blake3Keyed::new(key1).hash(msg);
let out2 = Blake3Keyed::new(key2).hash(msg);
assert_ne!(out1, out2, "Different keys must produce different outputs");
}
#[test]
fn blake3_keyed_different_messages() {
let key = [42u8; 32];
let out1 = Blake3Keyed::new(key).hash(b"message1");
let out2 = Blake3Keyed::new(key).hash(b"message2");
assert_ne!(
out1, out2,
"Different messages must produce different outputs"
);
}
#[test]
fn blake3_keyed_deterministic() {
let key = [7u8; 32];
let msg = b"deterministic";
let out1 = Blake3Keyed::new(key).hash(msg);
let out2 = blake3_keyed_hash(&key, msg);
assert_eq!(out1, out2, "Method and free function must agree");
}
#[test]
fn blake3_derive_key_different_contexts() {
let material = b"shared key material";
let out1 = blake3_derive_key("context A", material);
let out2 = blake3_derive_key("context B", material);
assert_ne!(
out1, out2,
"Different contexts must produce different derived keys"
);
}
#[test]
fn blake3_derive_key_deterministic() {
let material = b"deterministic material";
let out1 = blake3_derive_key("test context", material);
let out2 = blake3_derive_key("test context", material);
assert_eq!(out1, out2, "derive_key must be deterministic");
}
#[test]
fn blake3_derive_key_output_len() {
let out = blake3_derive_key("oxicrypto test", b"material");
assert_eq!(out.len(), 32);
}
#[test]
fn blake3_xof_64_bytes() {
let out = blake3_xof(b"hello", 64);
assert_eq!(out.len(), 64);
}
#[test]
fn blake3_xof_first_32_match_standard_hash() {
let msg = b"xof test";
let standard = Blake3.hash_to_vec(msg).unwrap();
let extended = blake3_xof(msg, 64);
assert_eq!(
&extended[..32],
standard.as_slice(),
"First 32 bytes of XOF must match standard BLAKE3 hash"
);
}
#[test]
fn blake3_xof_prefix_consistency() {
let msg = b"prefix test";
let out64 = blake3_xof(msg, 64);
let out128 = blake3_xof(msg, 128);
assert_eq!(
&out128[..64],
out64.as_slice(),
"128-byte XOF must be prefixed by 64-byte XOF"
);
}
#[test]
fn blake3_xof_zero_len() {
let out = blake3_xof(b"anything", 0);
assert!(out.is_empty());
}
#[test]
fn sha256_digest_len_constant() {
assert_eq!(Sha256::DIGEST_LEN, 32);
assert_eq!(Sha256::BLOCK_SIZE, 64);
}
#[test]
fn sha384_digest_len_constant() {
assert_eq!(Sha384::DIGEST_LEN, 48);
assert_eq!(Sha384::BLOCK_SIZE, 128);
}
#[test]
fn sha512_digest_len_constant() {
assert_eq!(Sha512::DIGEST_LEN, 64);
assert_eq!(Sha512::BLOCK_SIZE, 128);
}
#[test]
fn sha3_256_digest_len_constant() {
assert_eq!(Sha3_256::DIGEST_LEN, 32);
}
#[test]
fn blake3_digest_len_constant() {
assert_eq!(Blake3::DIGEST_LEN, 32);
assert_eq!(Blake3::BLOCK_SIZE, 64);
}
#[test]
fn blake2b256_digest_len_constant() {
assert_eq!(Blake2b256::DIGEST_LEN, 32);
assert_eq!(Blake2b256::BLOCK_SIZE, 128);
}
#[test]
fn blake2b512_digest_len_constant() {
assert_eq!(Blake2b512::DIGEST_LEN, 64);
assert_eq!(Blake2b512::BLOCK_SIZE, 128);
}
#[test]
fn blake2s256_digest_len_constant() {
assert_eq!(Blake2s256::DIGEST_LEN, 32);
assert_eq!(Blake2s256::BLOCK_SIZE, 64);
}
#[test]
fn constants_match_runtime_output_len() {
assert_eq!(Sha256::DIGEST_LEN, Sha256.output_len());
assert_eq!(Sha384::DIGEST_LEN, Sha384.output_len());
assert_eq!(Sha512::DIGEST_LEN, Sha512.output_len());
assert_eq!(Blake3::DIGEST_LEN, Blake3.output_len());
assert_eq!(Blake2b256::DIGEST_LEN, Blake2b256.output_len());
assert_eq!(Blake2b512::DIGEST_LEN, Blake2b512.output_len());
assert_eq!(Blake2s256::DIGEST_LEN, Blake2s256.output_len());
}
#[test]
fn sha256_streaming_clone_independent() {
let mut streamer = Sha256Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
let mut cloned = streamer.clone();
StreamingHash::update(&mut streamer, b" world");
StreamingHash::update(&mut cloned, b" clone");
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf1).expect("finalize original");
StreamingHash::finalize(cloned, &mut buf2).expect("finalize clone");
assert_ne!(
buf1, buf2,
"cloned streamer with different data must differ"
);
}
#[test]
fn blake3_streaming_clone_independent() {
let mut streamer = Blake3Streaming::new();
StreamingHash::update(&mut streamer, b"hello");
let mut cloned = streamer.clone();
StreamingHash::update(&mut streamer, b" a");
StreamingHash::update(&mut cloned, b" b");
let mut buf1 = [0u8; 32];
let mut buf2 = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf1).expect("finalize original");
StreamingHash::finalize(cloned, &mut buf2).expect("finalize clone");
assert_ne!(
buf1, buf2,
"cloned Blake3Streaming with different data must differ"
);
}
#[cfg(feature = "std")]
#[test]
fn sha256_hex_known_vector() {
let hex = sha256_hex(b"");
assert_eq!(
hex,
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
}
#[cfg(feature = "std")]
#[test]
fn sha256_hex_length() {
let hex = sha256_hex(b"abc");
assert_eq!(hex.len(), 64, "SHA-256 hex string must be 64 characters");
assert!(hex
.chars()
.all(|c| c.is_ascii_digit() || ('a'..='f').contains(&c)));
}
#[cfg(feature = "std")]
#[test]
fn sha512_hex_length() {
let hex = sha512_hex(b"abc");
assert_eq!(hex.len(), 128, "SHA-512 hex string must be 128 characters");
}
#[cfg(feature = "std")]
#[test]
fn blake3_hex_known_vector() {
let hex = blake3_hex(b"abc");
assert_eq!(
hex,
"6437b3ac38465133ffb63b75273a8db548c558465d79db03fd359c6cd5bd9d85"
);
}
#[cfg(feature = "std")]
#[test]
fn sha256_streaming_io_write() {
use std::io::Write;
let mut streamer = Sha256Streaming::new();
streamer.write_all(b"hello").expect("write_all hello");
streamer.write_all(b" world").expect("write_all world");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).expect("finalize");
let expected = Sha256.hash_to_vec(b"hello world").expect("one-shot");
assert_eq!(
&buf[..],
expected.as_slice(),
"io::Write result must match one-shot"
);
}
#[cfg(feature = "std")]
#[test]
fn blake3_streaming_io_write() {
use std::io::Write;
let mut streamer = Blake3Streaming::new();
streamer.write_all(b"test data").expect("write_all");
let mut buf = [0u8; 32];
StreamingHash::finalize(streamer, &mut buf).expect("finalize");
let expected = Blake3.hash_to_vec(b"test data").expect("one-shot");
assert_eq!(
&buf[..],
expected.as_slice(),
"io::Write result must match one-shot"
);
}
}