diff --git a/Cargo.lock b/Cargo.lock index 55dfba9..6316c30 100755 --- a/Cargo.lock +++ b/Cargo.lock @@ -16,6 +16,7 @@ name = "algorithms" version = "0.1.0" dependencies = [ "criterion", + "generic-array", "iai", ] @@ -307,6 +308,15 @@ dependencies = [ "num", ] +[[package]] +name = "generic-array" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e8c8444bc9d71b935156cc0ccab7f622180808af7867b1daae6547d773591703" +dependencies = [ + "typenum", +] + [[package]] name = "half" version = "1.8.3" @@ -788,6 +798,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "typenum" +version = "1.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1dccffe3ce07af9386bfd29e80c0ab1a8205a2fc34e4bcd40364df902cfa8f3f" + [[package]] name = "unicode-ident" version = "1.0.18" diff --git a/crates/algorithms/Cargo.toml b/crates/algorithms/Cargo.toml index d677abe..7e4c4b1 100755 --- a/crates/algorithms/Cargo.toml +++ b/crates/algorithms/Cargo.toml @@ -4,6 +4,7 @@ version = "0.1.0" edition = "2024" [dependencies] +generic-array = "1.2.0" [dev-dependencies] criterion = "0.3" diff --git a/crates/algorithms/src/hash/ffi/sha2.rs b/crates/algorithms/src/hash/ffi/sha2.rs index 6bde16d..d0f0a56 100644 --- a/crates/algorithms/src/hash/ffi/sha2.rs +++ b/crates/algorithms/src/hash/ffi/sha2.rs @@ -1,6 +1,6 @@ use crate::ffi::{ref_to_ptr, ref_to_ptr_mut}; -use super::super::sha2::*; +use super::super::sha2::{DIGEST256_EMPTY, Digest256, SHA2_256_BLOCK_SIZE}; #[repr(C)] #[allow(non_camel_case_types)] @@ -17,8 +17,8 @@ pub enum SHA2Result { #[repr(C)] #[derive(Debug, PartialEq, Eq, Clone, Copy, Hash)] -pub struct SHA2Context { - pub intermediate_hash: Digest, +pub struct Sha2_256Context { + pub intermediate_hash: Digest256, pub length_low: u32, pub length_high: u32, pub message_block_index: i16, @@ -27,10 +27,10 @@ pub struct SHA2Context { pub corrupted: bool, } -impl SHA2Context { +impl Sha2_256Context { pub fn new() -> Self { Self { - intermediate_hash: DIGEST_EMPTY, + intermediate_hash: DIGEST256_EMPTY, length_low: 0, length_high: 0, message_block_index: 0, @@ -41,7 +41,7 @@ impl SHA2Context { } } -impl Default for SHA2Context { +impl Default for Sha2_256Context { fn default() -> Self { Self::new() } @@ -50,40 +50,44 @@ impl Default for SHA2Context { mod ffi { use super::*; unsafe extern "C" { - pub fn sha2_256_oneshot(data: *const u8, len: usize, digest: *mut Digest) -> SHA2Result; - pub fn sha2_256_reset(context: *mut SHA2Context) -> SHA2Result; - pub fn sha2_256_input(context: *mut SHA2Context, data: *const u8, len: usize) + pub fn sha2_256_oneshot(data: *const u8, len: usize, digest: *mut Digest256) -> SHA2Result; + pub fn sha2_256_reset(context: *mut Sha2_256Context) -> SHA2Result; + pub fn sha2_256_input( + context: *mut Sha2_256Context, + data: *const u8, + len: usize, + ) -> SHA2Result; + pub fn sha2_256_result(context: *mut Sha2_256Context, digest: *mut Digest256) -> SHA2Result; - pub fn sha2_256_result(context: *mut SHA2Context, digest: *mut Digest) -> SHA2Result; } } #[inline] -pub fn sha2_256_oneshot(data: &[u8]) -> Result { - let mut digest: Digest = DIGEST_EMPTY; +pub fn sha2_256_oneshot(data: &[u8]) -> Result { + let mut digest: Digest256 = DIGEST256_EMPTY; let res = unsafe { ffi::sha2_256_oneshot(ref_to_ptr(data), data.len(), ref_to_ptr_mut(&mut digest)) }; to_res(digest, res) } -pub fn sha2_256_reset(context: &mut SHA2Context) -> Result<(), SHA2Result> { +pub fn sha2_256_reset(context: &mut Sha2_256Context) -> Result<(), SHA2Result> { let res = unsafe { ffi::sha2_256_reset(ref_to_ptr_mut(context)) }; to_res((), res) } -pub fn sha2_256_input(context: &mut SHA2Context, data: &[u8]) -> Result<(), SHA2Result> { +pub fn sha2_256_input(context: &mut Sha2_256Context, data: &[u8]) -> Result<(), SHA2Result> { let res = unsafe { ffi::sha2_256_input(ref_to_ptr_mut(context), ref_to_ptr(data), data.len()) }; to_res((), res) } -pub fn sha2_256_result(context: &mut SHA2Context) -> Result { - let mut digest: Digest = DIGEST_EMPTY; +pub fn sha2_256_result(context: &mut Sha2_256Context) -> Result { + let mut digest: Digest256 = DIGEST256_EMPTY; let res = unsafe { ffi::sha2_256_result(ref_to_ptr_mut(context), ref_to_ptr_mut(&mut digest)) }; to_res(digest, res) } #[inline] -fn to_res(if_ok: T, res: SHA2Result) -> Result { +pub(crate) fn to_res(if_ok: T, res: SHA2Result) -> Result { if res == SHA2Result::shaSuccess { Ok(if_ok) } else { @@ -91,26 +95,28 @@ fn to_res(if_ok: T, res: SHA2Result) -> Result { } } +// NOTE: this SHA2_256 implementation does not suit core::hash::Hasher in Rust, since that expects +// a finish method with &self and that the hasher can be used after finish. + #[cfg(test)] mod test { - use crate::digest; - use crate::hash::Digest; + use crate::hash::Digest256; use crate::hash::ffi::sha2_256_oneshot; - const TEST_VALUES: &[(&str, Digest)] = &[ + const TEST_VALUES: &[(&str, Digest256)] = &[ ( "AAAA", - digest!( + [ 0x63c1dd95, 0x1ffedf6f, 0x7fd968ad, 0x4efa39b8, 0xed584f16, 0x2f46e715, 0x114ee184, - 0xf8de9201 - ), + 0xf8de9201, + ], ), ( "BAAA", - digest!( + [ 0x49e3cd45, 0x27c96cdc, 0x010160ff, 0x08520e0c, 0xb63c6ef8, 0xc4e7d486, 0x08995343, - 0x7f83a159 - ), + 0x7f83a159, + ], ), ]; diff --git a/crates/algorithms/src/hash/sha2.rs b/crates/algorithms/src/hash/sha2.rs index f0bbb9d..75c8cf7 100644 --- a/crates/algorithms/src/hash/sha2.rs +++ b/crates/algorithms/src/hash/sha2.rs @@ -1,15 +1,107 @@ +use generic_array::GenericArray; + +use crate::hash::{ffi::SHA2Result, sha2::sealed::SHA2Mode}; + pub const SHA2_256_HASH_BITS: usize = 256; pub const SHA2_256_HASH_BYTES: usize = SHA2_256_HASH_BITS / 8; pub const SHA2_256_HASH_PARTS: usize = SHA2_256_HASH_BYTES / 4 /* storing with u32 */; pub const SHA2_256_BLOCK_SIZE: usize = 2 * SHA2_256_HASH_BYTES; -pub type Digest = [u32; SHA2_256_HASH_PARTS]; +pub type Digest256 = [u32; SHA2_256_HASH_PARTS]; +pub(crate) const DIGEST256_EMPTY: Digest256 = [0; SHA2_256_HASH_PARTS]; -#[macro_export] -macro_rules! digest { - ($($block:literal$(,)?)+) => { - [$($block,)+ ] - }; +pub struct Mode256; +mod sealed { + use generic_array::{ArrayLength, GenericArray}; + + use super::Mode256; + pub trait SHA2Mode { + type HashBits: ArrayLength; + type HashBytes: ArrayLength; + type HashParts: ArrayLength; + type BlockSize: ArrayLength; + type Digest: AsRef<[u32]> + AsMut<[u32]>; + type Block: AsRef<[u8]> + AsMut<[u8]>; + } + impl SHA2Mode for Mode256 { + type HashBits = generic_array::typenum::U256; + type HashBytes = generic_array::typenum::U32; + type HashParts = generic_array::typenum::U8; + type BlockSize = generic_array::typenum::U512; + type Digest = GenericArray; + type Block = GenericArray; + } + impl Mode256 {} } -pub(crate) const DIGEST_EMPTY: Digest = [0; SHA2_256_HASH_PARTS]; +#[derive(Debug, PartialEq, Eq, Clone, Hash)] +pub struct SHA2Context { + intermediate_hash: GenericArray, + block: GenericArray, + block_idx: usize, +} + +impl SHA2Context { + pub fn new() -> Self { + Self { + intermediate_hash: Default::default(), + block: Default::default(), + block_idx: Default::default(), + } + } + + pub fn clear(&mut self) { + *self = Self::new(); + } + + pub fn input(&mut self, data: &[u8]) -> Result<(), SHA2Result> { + todo!() + } + + pub fn finish(self) -> Result { + todo!() + } +} + +impl Default for SHA2Context { + fn default() -> Self { + Self::new() + } +} + +pub fn sha2_oneshot(data: &[u8]) -> Result { + todo!() +} + +pub fn sha2_256_oneshot(data: &[u8]) -> Result { + sha2_oneshot::(data).map(|dig| dig.into_array()) +} + +#[cfg(test)] +mod test { + use super::*; + + const TEST_VALUES: &[(&str, Digest256)] = &[ + ( + "AAAA", + [ + 0x63c1dd95, 0x1ffedf6f, 0x7fd968ad, 0x4efa39b8, 0xed584f16, 0x2f46e715, 0x114ee184, + 0xf8de9201, + ], + ), + ( + "BAAA", + [ + 0x49e3cd45, 0x27c96cdc, 0x010160ff, 0x08520e0c, 0xb63c6ef8, 0xc4e7d486, 0x08995343, + 0x7f83a159, + ], + ), + ]; + + #[test] + fn test_check() { + for (input, expected_output) in TEST_VALUES.iter().copied() { + assert_eq!(sha2_256_oneshot(input.as_bytes()), Ok(expected_output)) + } + } +}