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