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;
24use bytes::{Buf, BufMut};
25use commonware_codec::{DecodeExt, Error as CodecError, FixedSize, Read, ReadExt, Write};
26use commonware_utils::{hex, Array};
27use rand::{CryptoRng, Rng};
28use sha2::{Digest as _, Sha256 as ISha256};
29use std::{
30    fmt::{Debug, Display},
31    ops::Deref,
32};
33use zeroize::Zeroize;
34
35/// Re-export `sha2::Sha256` as `CoreSha256` for external use if needed.
36pub type CoreSha256 = ISha256;
37
38const DIGEST_LENGTH: usize = 32;
39
40/// Generate a SHA-256 digest from a message.
41pub fn hash(message: &[u8]) -> Digest {
42    let array: [u8; DIGEST_LENGTH] = ISha256::digest(message).into();
43    Digest::from(array)
44}
45
46/// SHA-256 hasher.
47#[derive(Debug)]
48pub struct Sha256 {
49    hasher: ISha256,
50}
51
52impl Default for Sha256 {
53    fn default() -> Self {
54        Self::new()
55    }
56}
57
58impl Clone for Sha256 {
59    fn clone(&self) -> Self {
60        // We manually implement `Clone` to avoid cloning the hasher state.
61        Self::default()
62    }
63}
64
65impl Sha256 {
66    /// Convenience function for testing that creates an easily recognizable digest by repeating a
67    /// single byte.
68    pub fn fill(b: u8) -> <Self as Hasher>::Digest {
69        <Self as Hasher>::Digest::decode(vec![b; DIGEST_LENGTH].as_ref()).unwrap()
70    }
71}
72
73impl Hasher for Sha256 {
74    type Digest = Digest;
75
76    fn new() -> Self {
77        Self {
78            hasher: ISha256::new(),
79        }
80    }
81
82    fn update(&mut self, message: &[u8]) {
83        self.hasher.update(message);
84    }
85
86    fn finalize(&mut self) -> Self::Digest {
87        let finalized = self.hasher.finalize_reset();
88        let array: [u8; DIGEST_LENGTH] = finalized.into();
89        Self::Digest::from(array)
90    }
91
92    fn reset(&mut self) {
93        self.hasher = ISha256::new();
94    }
95
96    fn empty() -> Self::Digest {
97        Self::new().finalize()
98    }
99}
100
101/// Digest of a SHA-256 hashing operation.
102#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
103#[repr(transparent)]
104pub struct Digest([u8; DIGEST_LENGTH]);
105
106impl Write for Digest {
107    fn write(&self, buf: &mut impl BufMut) {
108        self.0.write(buf);
109    }
110}
111
112impl Read for Digest {
113    type Cfg = ();
114
115    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
116        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
117        Ok(Self(array))
118    }
119}
120
121impl FixedSize for Digest {
122    const SIZE: usize = DIGEST_LENGTH;
123}
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 std::fmt::Formatter<'_>) -> std::fmt::Result {
148        write!(f, "{}", hex(&self.0))
149    }
150}
151
152impl Display for Digest {
153    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
154        write!(f, "{}", hex(&self.0))
155    }
156}
157
158impl crate::Digest for Digest {
159    fn random<R: Rng + CryptoRng>(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 = 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}