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
97/// Digest of a SHA-256 hashing operation.
98#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
99#[repr(transparent)]
100pub struct Digest([u8; DIGEST_LENGTH]);
101
102impl Write for Digest {
103    fn write(&self, buf: &mut impl BufMut) {
104        self.0.write(buf);
105    }
106}
107
108impl Read for Digest {
109    type Cfg = ();
110
111    fn read_cfg(buf: &mut impl Buf, _: &()) -> Result<Self, CodecError> {
112        let array = <[u8; DIGEST_LENGTH]>::read(buf)?;
113        Ok(Self(array))
114    }
115}
116
117impl FixedSize for Digest {
118    const SIZE: usize = DIGEST_LENGTH;
119}
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 std::fmt::Formatter<'_>) -> std::fmt::Result {
144        write!(f, "{}", hex(&self.0))
145    }
146}
147
148impl Display for Digest {
149    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
150        write!(f, "{}", hex(&self.0))
151    }
152}
153
154impl crate::Digest for Digest {
155    fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self {
156        let mut array = [0u8; DIGEST_LENGTH];
157        rng.fill_bytes(&mut array);
158        Self(array)
159    }
160}
161
162impl Zeroize for Digest {
163    fn zeroize(&mut self) {
164        self.0.zeroize();
165    }
166}
167
168#[cfg(test)]
169mod tests {
170    use super::*;
171    use commonware_codec::{DecodeExt, Encode};
172    use commonware_utils::hex;
173
174    const HELLO_DIGEST: &str = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
175
176    #[test]
177    fn test_sha256() {
178        let msg = b"hello world";
179
180        // Generate initial hash
181        let mut hasher = Sha256::new();
182        hasher.update(msg);
183        let digest = hasher.finalize();
184        assert!(Digest::decode(digest.as_ref()).is_ok());
185        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
186
187        // Reuse hasher
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        // Test simple hasher
194        let hash = hash(msg);
195        assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
196    }
197
198    #[test]
199    fn test_sha256_len() {
200        assert_eq!(Digest::SIZE, DIGEST_LENGTH);
201    }
202
203    #[test]
204    fn test_codec() {
205        let msg = b"hello world";
206        let mut hasher = Sha256::new();
207        hasher.update(msg);
208        let digest = hasher.finalize();
209
210        let encoded = digest.encode();
211        assert_eq!(encoded.len(), DIGEST_LENGTH);
212        assert_eq!(encoded, digest.as_ref());
213
214        let decoded = Digest::decode(encoded).unwrap();
215        assert_eq!(digest, decoded);
216    }
217}