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