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::{Array, Error, Hasher};
24use bytes::{Buf, BufMut};
25use commonware_codec::{Codec, Error as CodecError, SizedCodec};
26use commonware_utils::hex;
27use rand::{CryptoRng, Rng};
28use sha2::{Digest as _, Sha256 as ISha256};
29use std::{
30    fmt::{Debug, Display},
31    ops::Deref,
32};
33
34const DIGEST_LENGTH: usize = 32;
35
36/// Generate a SHA-256 digest from a message.
37pub fn hash(message: &[u8]) -> Digest {
38    let array: [u8; DIGEST_LENGTH] = ISha256::digest(message).into();
39    Digest::from(array)
40}
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 Hasher for Sha256 {
62    type Digest = Digest;
63
64    fn new() -> Self {
65        Self {
66            hasher: ISha256::new(),
67        }
68    }
69
70    fn update(&mut self, message: &[u8]) {
71        self.hasher.update(message);
72    }
73
74    fn finalize(&mut self) -> Self::Digest {
75        let finalized = self.hasher.finalize_reset();
76        let array: [u8; DIGEST_LENGTH] = finalized.into();
77        Self::Digest::from(array)
78    }
79
80    fn reset(&mut self) {
81        self.hasher = ISha256::new();
82    }
83
84    fn random<R: Rng + CryptoRng>(rng: &mut R) -> Self::Digest {
85        let mut digest = [0u8; DIGEST_LENGTH];
86        rng.fill_bytes(&mut digest);
87        Self::Digest::from(digest)
88    }
89}
90
91/// Digest of a SHA-256 hashing operation.
92#[derive(Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Hash)]
93#[repr(transparent)]
94pub struct Digest([u8; DIGEST_LENGTH]);
95
96impl Codec for Digest {
97    fn write(&self, buf: &mut impl BufMut) {
98        self.0.write(buf);
99    }
100
101    fn read(buf: &mut impl Buf) -> Result<Self, CodecError> {
102        Self::read_from(buf).map_err(|err| CodecError::Wrapped("Digest", err.into()))
103    }
104
105    fn len_encoded(&self) -> usize {
106        DIGEST_LENGTH
107    }
108}
109
110impl SizedCodec for Digest {
111    const LEN_ENCODED: usize = DIGEST_LENGTH;
112}
113
114impl Array for Digest {
115    type Error = Error;
116}
117
118impl From<[u8; DIGEST_LENGTH]> for Digest {
119    fn from(value: [u8; DIGEST_LENGTH]) -> Self {
120        Self(value)
121    }
122}
123
124impl TryFrom<&[u8]> for Digest {
125    type Error = Error;
126    fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
127        if value.len() != DIGEST_LENGTH {
128            return Err(Error::InvalidDigestLength);
129        }
130        let array: [u8; DIGEST_LENGTH] =
131            value.try_into().map_err(|_| Error::InvalidDigestLength)?;
132        Ok(Self(array))
133    }
134}
135
136impl TryFrom<&Vec<u8>> for Digest {
137    type Error = Error;
138    fn try_from(value: &Vec<u8>) -> Result<Self, Self::Error> {
139        Self::try_from(value.as_slice())
140    }
141}
142
143impl TryFrom<Vec<u8>> for Digest {
144    type Error = Error;
145    fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
146        if value.len() != DIGEST_LENGTH {
147            return Err(Error::InvalidDigestLength);
148        }
149
150        // If the length is correct, we can safely convert the vector into a boxed slice without
151        // any copies.
152        let boxed_slice = value.into_boxed_slice();
153        let boxed_array: Box<[u8; DIGEST_LENGTH]> = boxed_slice
154            .try_into()
155            .map_err(|_| Error::InvalidDigestLength)?;
156        Ok(Self(*boxed_array))
157    }
158}
159
160impl AsRef<[u8]> for Digest {
161    fn as_ref(&self) -> &[u8] {
162        &self.0
163    }
164}
165
166impl Deref for Digest {
167    type Target = [u8];
168    fn deref(&self) -> &[u8] {
169        &self.0
170    }
171}
172
173impl Debug for Digest {
174    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
175        write!(f, "{}", hex(&self.0))
176    }
177}
178
179impl Display for Digest {
180    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
181        write!(f, "{}", hex(&self.0))
182    }
183}
184
185impl crate::Digest for Digest {}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190    use commonware_utils::hex;
191
192    const HELLO_DIGEST: &str = "b94d27b9934d3e08a52e52d7da7dabfac484efe37a5380ee9088f7ace2efcde9";
193
194    #[test]
195    fn test_sha256() {
196        let msg = b"hello world";
197
198        // Generate initial hash
199        let mut hasher = Sha256::new();
200        hasher.update(msg);
201        let digest = hasher.finalize();
202        assert!(Digest::try_from(digest.as_ref()).is_ok());
203        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
204
205        // Reuse hasher
206        hasher.update(msg);
207        let digest = hasher.finalize();
208        assert!(Digest::try_from(digest.as_ref()).is_ok());
209        assert_eq!(hex(digest.as_ref()), HELLO_DIGEST);
210
211        // Test simple hasher
212        let hash = hash(msg);
213        assert_eq!(hex(hash.as_ref()), HELLO_DIGEST);
214    }
215
216    #[test]
217    fn test_sha256_len() {
218        assert_eq!(Digest::LEN_ENCODED, DIGEST_LENGTH);
219    }
220
221    #[test]
222    fn test_codec() {
223        let msg = b"hello world";
224        let mut hasher = Sha256::new();
225        hasher.update(msg);
226        let digest = hasher.finalize();
227
228        let encoded = digest.encode();
229        assert_eq!(encoded.len(), DIGEST_LENGTH);
230        assert_eq!(encoded, digest.as_ref());
231
232        let decoded = Digest::decode(encoded).unwrap();
233        assert_eq!(digest, decoded);
234    }
235}