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, Span};
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(pub [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 Span for Digest {}
126
127impl Array for Digest {}
128
129impl From<[u8; DIGEST_LENGTH]> for Digest {
130    fn from(value: [u8; DIGEST_LENGTH]) -> Self {
131        Self(value)
132    }
133}
134
135impl AsRef<[u8]> for Digest {
136    fn as_ref(&self) -> &[u8] {
137        &self.0
138    }
139}
140
141impl Deref for Digest {
142    type Target = [u8];
143    fn deref(&self) -> &[u8] {
144        &self.0
145    }
146}
147
148impl Debug for Digest {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        write!(f, "{}", hex(&self.0))
151    }
152}
153
154impl Display for Digest {
155    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
156        write!(f, "{}", hex(&self.0))
157    }
158}
159
160impl crate::Digest for Digest {
161    fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
162        let mut array = [0u8; DIGEST_LENGTH];
163        rng.fill_bytes(&mut array);
164        Self(array)
165    }
166}
167
168impl Zeroize for Digest {
169    fn zeroize(&mut self) {
170        self.0.zeroize();
171    }
172}
173
174#[cfg(test)]
175mod tests {
176    use super::*;
177    use commonware_codec::{DecodeExt, Encode};
178    use commonware_utils::hex;
179
180    const HELLO_DIGEST: &str = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
181
182    #[test]
183    fn test_sha256() {
184        let msg = b"hello world";
185
186        // Generate initial hash
187        let mut hasher = Sha256::new();
188        hasher.update(msg);
189        let digest = hasher.finalize();
190        assert!(Digest::decode(digest.as_ref()).is_ok());
191        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
192
193        // Reuse hasher
194        hasher.update(msg);
195        let digest = hasher.finalize();
196        assert!(Digest::decode(digest.as_ref()).is_ok());
197        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
198
199        // Test simple hasher
200        let hash = hash(msg);
201        assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
202    }
203
204    #[test]
205    fn test_sha256_len() {
206        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
207    }
208
209    #[test]
210    fn test_hash_empty() {
211        let digest1 = Sha256::empty();
212        let digest2 = Sha256::empty();
213
214        assert_eq!(digest1, digest2);
215    }
216
217    #[test]
218    fn test_sha256_empty() {
219        let empty_digest = Sha256::empty();
220
221        // SHA-256 hash of empty string:
222        // e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855
223        let expected_bytes = [
224            0xe3, 0xb0, 0xc4, 0x42, 0x98, 0xfc, 0x1c, 0x14, 0x9a, 0xfb, 0xf4, 0xc8, 0x99, 0x6f,
225            0xb9, 0x24, 0x27, 0xae, 0x41, 0xe4, 0x64, 0x9b, 0x93, 0x4c, 0xa4, 0x95, 0x99, 0x1b,
226            0x78, 0x52, 0xb8, 0x55,
227        ];
228        let expected_digest = Digest::from(expected_bytes);
229
230        assert_eq!(empty_digest, expected_digest);
231    }
232
233    #[test]
234    fn test_codec() {
235        let msg = b"hello world";
236        let mut hasher = Sha256::new();
237        hasher.update(msg);
238        let digest = hasher.finalize();
239
240        let encoded = digest.encode();
241        assert_eq!(encoded.len(), DIGEST_LENGTH);
242        assert_eq!(encoded, digest.as_ref());
243
244        let decoded = Digest::decode(encoded).unwrap();
245        assert_eq!(digest, decoded);
246    }
247}