Skip to main content

commonware_cryptography/sha256/
mod.rs

1//! SHA-256 implementation of the `Hasher` trait.
2//!
3//! This implementation uses the `sha2` crate to generate SHA-256 digests.
4//!
5//! # Example
6//! ```rust
7//! use commonware_cryptography::{Hasher, Sha256};
8//!
9//! // Create a new SHA-256 hasher
10//! let mut hasher = Sha256::new();
11//!
12//! // Update the hasher with some messages
13//! hasher.update(b"hello,");
14//! hasher.update(b"world!");
15//!
16//! // Finalize the hasher to get the digest
17//! let digest = hasher.finalize();
18//!
19//! // Print the digest
20//! println!("digest: {:?}", digest);
21//! ```
22
23use crate::Hasher;
24#[cfg(not(feature = "std"))]
25use alloc::vec;
26use bytes::{Buf, BufMut};
27use commonware_codec::{
28    DecodeExt, Error as CodecError, FixedArray, FixedSize, Read, ReadExt, Write,
29};
30use commonware_formatting::Hex;
31use commonware_math::algebra::Random;
32use commonware_utils::{Array, Span};
33use core::{
34    fmt::{Debug, Display},
35    ops::Deref,
36};
37use rand_core::CryptoRng;
38use sha2::{Digest as _, Sha256 as ISha256};
39use zeroize::Zeroize;
40
41/// Re-export `sha2::Sha256` as `CoreSha256` for external use if needed.
42pub type CoreSha256 = ISha256;
43
44const DIGEST_LENGTH: usize = 32;
45
46/// SHA-256 hasher.
47#[derive(Debug, Default)]
48pub struct Sha256 {
49    hasher: ISha256,
50}
51
52impl Clone for Sha256 {
53    fn clone(&self) -> Self {
54        // We manually implement `Clone` to avoid cloning the hasher state.
55        Self::default()
56    }
57}
58
59impl Sha256 {
60    /// Convenience function for testing that creates an easily recognizable digest by repeating a
61    /// single byte.
62    pub fn fill(b: u8) -> <Self as Hasher>::Digest {
63        <Self as Hasher>::Digest::decode(vec![b; DIGEST_LENGTH].as_ref()).unwrap()
64    }
65}
66
67impl Hasher for Sha256 {
68    type Digest = Digest;
69
70    fn update(&mut self, message: &[u8]) -> &mut Self {
71        self.hasher.update(message);
72        self
73    }
74
75    fn finalize(&mut self) -> Self::Digest {
76        let finalized = self.hasher.finalize_reset();
77        let array: [u8; DIGEST_LENGTH] = finalized.into();
78        Self::Digest::from(array)
79    }
80
81    fn reset(&mut self) -> &mut Self {
82        self.hasher = ISha256::new();
83        self
84    }
85}
86
87/// Digest of a SHA-256 hashing operation.
88#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash, FixedArray)]
89#[fixed_array(infallible)]
90#[repr(transparent)]
91pub struct Digest(pub [u8; DIGEST_LENGTH]);
92
93#[cfg(feature = "arbitrary")]
94impl<'a> arbitrary::Arbitrary<'a> for Digest {
95    fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
96        // Generate random bytes and compute their Sha256 hash
97        let len = u.int_in_range(0..=256)?;
98        let data = u.bytes(len)?;
99        Ok(Sha256::hash(data))
100    }
101}
102
103impl Write for Digest {
104    fn write(&self, buf: &mut impl BufMut) {
105        self.0.write(buf);
106    }
107}
108
109impl Read for Digest {
110    type Cfg = ();
111
112    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
113        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
114        Ok(Self(array))
115    }
116}
117
118impl FixedSize for Digest {
119    const SIZE: usize = DIGEST_LENGTH;
120}
121
122impl Span for Digest {}
123
124impl Array for Digest {}
125
126impl AsRef<[u8]> for Digest {
127    fn as_ref(&self) -> &[u8] {
128        &self.0
129    }
130}
131
132impl Deref for Digest {
133    type Target = [u8];
134    fn deref(&self) -> &[u8] {
135        &self.0
136    }
137}
138
139impl Debug for Digest {
140    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
141        write!(f, "{}", Hex(&self.0))
142    }
143}
144
145impl Display for Digest {
146    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
147        write!(f, "{}", Hex(&self.0))
148    }
149}
150
151impl crate::Digest for Digest {
152    const EMPTY: Self = Self([0u8; DIGEST_LENGTH]);
153}
154
155impl Random for Digest {
156    fn random(mut rng: impl CryptoRng) -> Self {
157        let mut array = [0u8; DIGEST_LENGTH];
158        rng.fill_bytes(&mut array);
159        Self(array)
160    }
161}
162
163impl Zeroize for Digest {
164    fn zeroize(&mut self) {
165        self.0.zeroize();
166    }
167}
168
169#[cfg(test)]
170mod tests {
171    use super::*;
172    use commonware_codec::{DecodeExt, Encode};
173
174    const HELLO_DIGEST: [u8; DIGEST_LENGTH] = commonware_formatting::hex!(
175        "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9"
176    );
177
178    #[test]
179    fn test_sha256() {
180        let msg = b"hello world";
181
182        // Generate initial hash
183        let mut hasher = Sha256::new();
184        hasher.update(msg);
185        let digest = hasher.finalize();
186        assert!(Digest::decode(digest.as_ref()).is_ok());
187        assert_eq!(digest.as_ref(), HELLO_DIGEST);
188
189        // Reuse hasher
190        hasher.update(msg);
191        let digest = hasher.finalize();
192        assert!(Digest::decode(digest.as_ref()).is_ok());
193        assert_eq!(digest.as_ref(), HELLO_DIGEST);
194
195        // Test simple hasher
196        let hash = Sha256::hash(msg);
197        assert_eq!(hash.as_ref(), HELLO_DIGEST);
198    }
199
200    #[test]
201    fn test_sha256_len() {
202        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
203    }
204
205    #[test]
206    fn test_codec() {
207        let msg = b"hello world";
208        let mut hasher = Sha256::new();
209        hasher.update(msg);
210        let digest = hasher.finalize();
211
212        let encoded = digest.encode();
213        assert_eq!(encoded.len(), DIGEST_LENGTH);
214        assert_eq!(encoded, digest.as_ref());
215
216        let decoded = Digest::decode(encoded).unwrap();
217        assert_eq!(digest, decoded);
218    }
219
220    #[cfg(feature = "arbitrary")]
221    mod conformance {
222        use super::*;
223        use commonware_codec::conformance::CodecConformance;
224
225        commonware_conformance::conformance_tests! {
226            CodecConformance<Digest>,
227        }
228    }
229}