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