orion/hazardous/hash/sha2/
sha256.rs

1// MIT License
2
3// Copyright (c) 2020-2025 The orion Developers
4
5// Permission is hereby granted, free of charge, to any person obtaining a copy
6// of this software and associated documentation files (the "Software"), to deal
7// in the Software without restriction, including without limitation the rights
8// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9// copies of the Software, and to permit persons to whom the Software is
10// furnished to do so, subject to the following conditions:
11
12// The above copyright notice and this permission notice shall be included in
13// all copies or substantial portions of the Software.
14
15// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21// SOFTWARE.
22
23//! # Parameters:
24//! - `data`: The data to be hashed.
25//!
26//! # Errors:
27//! An error will be returned if:
28//! - [`finalize()`] is called twice without a [`reset()`] in between.
29//! - [`update()`] is called after [`finalize()`] without a [`reset()`] in
30//!   between.
31//!
32//! # Panics:
33//! A panic will occur if:
34//! - More than 2*(2^32-1) __bits__ of data are hashed.
35//!
36//! # Security:
37//! - SHA256 is vulnerable to length extension attacks.
38//!
39//! # Recommendation:
40//! - It is recommended to use [BLAKE2b] when possible.
41//!
42//! # Example:
43//! ```rust
44//! use orion::hazardous::hash::sha2::sha256::Sha256;
45//!
46//! // Using the streaming interface
47//! let mut state = Sha256::new();
48//! state.update(b"Hello world")?;
49//! let hash = state.finalize()?;
50//!
51//! // Using the one-shot function
52//! let hash_one_shot = Sha256::digest(b"Hello world")?;
53//!
54//! assert_eq!(hash, hash_one_shot);
55//! # Ok::<(), orion::errors::UnknownCryptoError>(())
56//! ```
57//! [`update()`]: sha256::Sha256::update
58//! [`reset()`]: sha256::Sha256::reset
59//! [`finalize()`]: sha256::Sha256::finalize
60//! [BLAKE2b]: super::blake2::blake2b
61
62use crate::errors::UnknownCryptoError;
63
64#[cfg(feature = "safe_api")]
65use std::io;
66
67/// The blocksize for the hash function SHA256.
68pub const SHA256_BLOCKSIZE: usize = 64;
69/// The output size for the hash function SHA256.
70pub const SHA256_OUTSIZE: usize = 32;
71/// The number of constants for the hash function SHA256.
72const N_CONSTS: usize = 64;
73
74construct_public! {
75    /// A type to represent the `Digest` that SHA256 returns.
76    ///
77    /// # Errors:
78    /// An error will be returned if:
79    /// - `slice` is not 32 bytes.
80    (Digest, test_digest, SHA256_OUTSIZE, SHA256_OUTSIZE)
81}
82
83impl_from_trait!(Digest, SHA256_OUTSIZE);
84
85use super::sha2_core::{State, Variant, Word};
86use super::w32::WordU32;
87
88#[derive(Clone)]
89/// SHA256 streaming state.
90pub(crate) struct V256;
91
92impl Variant<WordU32, N_CONSTS> for V256 {
93    #[rustfmt::skip]
94    #[allow(clippy::unreadable_literal)]
95    /// The SHA256 constants as defined in FIPS 180-4.
96    const K: [WordU32; N_CONSTS] = [
97            WordU32(0x428a2f98), WordU32(0x71374491), WordU32(0xb5c0fbcf), WordU32(0xe9b5dba5),
98            WordU32(0x3956c25b), WordU32(0x59f111f1), WordU32(0x923f82a4), WordU32(0xab1c5ed5),
99            WordU32(0xd807aa98), WordU32(0x12835b01), WordU32(0x243185be), WordU32(0x550c7dc3),
100            WordU32(0x72be5d74), WordU32(0x80deb1fe), WordU32(0x9bdc06a7), WordU32(0xc19bf174),
101            WordU32(0xe49b69c1), WordU32(0xefbe4786), WordU32(0x0fc19dc6), WordU32(0x240ca1cc),
102            WordU32(0x2de92c6f), WordU32(0x4a7484aa), WordU32(0x5cb0a9dc), WordU32(0x76f988da),
103            WordU32(0x983e5152), WordU32(0xa831c66d), WordU32(0xb00327c8), WordU32(0xbf597fc7),
104            WordU32(0xc6e00bf3), WordU32(0xd5a79147), WordU32(0x06ca6351), WordU32(0x14292967),
105            WordU32(0x27b70a85), WordU32(0x2e1b2138), WordU32(0x4d2c6dfc), WordU32(0x53380d13),
106            WordU32(0x650a7354), WordU32(0x766a0abb), WordU32(0x81c2c92e), WordU32(0x92722c85),
107            WordU32(0xa2bfe8a1), WordU32(0xa81a664b), WordU32(0xc24b8b70), WordU32(0xc76c51a3),
108            WordU32(0xd192e819), WordU32(0xd6990624), WordU32(0xf40e3585), WordU32(0x106aa070),
109            WordU32(0x19a4c116), WordU32(0x1e376c08), WordU32(0x2748774c), WordU32(0x34b0bcb5),
110            WordU32(0x391c0cb3), WordU32(0x4ed8aa4a), WordU32(0x5b9cca4f), WordU32(0x682e6ff3),
111            WordU32(0x748f82ee), WordU32(0x78a5636f), WordU32(0x84c87814), WordU32(0x8cc70208),
112            WordU32(0x90befffa), WordU32(0xa4506ceb), WordU32(0xbef9a3f7), WordU32(0xc67178f2),
113        ];
114
115    #[rustfmt::skip]
116    #[allow(clippy::unreadable_literal)]
117    /// The SHA256 initial hash value H(0) as defined in FIPS 180-4.
118    const H0: [WordU32; 8] = [
119            WordU32(0x6a09e667), WordU32(0xbb67ae85), WordU32(0x3c6ef372), WordU32(0xa54ff53a),
120            WordU32(0x510e527f), WordU32(0x9b05688c), WordU32(0x1f83d9ab), WordU32(0x5be0cd19),
121        ];
122
123    /// The Big Sigma 0 function as specified in FIPS 180-4 section 4.1.2.
124    fn big_sigma_0(x: WordU32) -> WordU32 {
125        (x.rotate_right(2)) ^ x.rotate_right(13) ^ x.rotate_right(22)
126    }
127
128    /// The Big Sigma 1 function as specified in FIPS 180-4 section 4.1.2.
129    fn big_sigma_1(x: WordU32) -> WordU32 {
130        (x.rotate_right(6)) ^ x.rotate_right(11) ^ x.rotate_right(25)
131    }
132
133    /// The Small Sigma 0 function as specified in FIPS 180-4 section 4.1.2.
134    fn small_sigma_0(x: WordU32) -> WordU32 {
135        (x.rotate_right(7)) ^ x.rotate_right(18) ^ (x >> WordU32(3))
136    }
137
138    /// The Small Sigma 1 function as specified in FIPS 180-4 section 4.1.2.
139    fn small_sigma_1(x: WordU32) -> WordU32 {
140        (x.rotate_right(17)) ^ x.rotate_right(19) ^ (x >> WordU32(10))
141    }
142}
143
144#[derive(Clone, Debug)]
145/// SHA256 streaming state.
146pub struct Sha256 {
147    pub(crate) _state: State<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>,
148}
149
150impl Default for Sha256 {
151    fn default() -> Self {
152        Self::new()
153    }
154}
155
156impl Sha256 {
157    /// Initialize a `Sha256` struct.
158    pub fn new() -> Self {
159        Self {
160            _state: State::<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>::_new(),
161        }
162    }
163
164    /// Reset to `new()` state.
165    pub fn reset(&mut self) {
166        self._state._reset();
167    }
168
169    #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
170    /// Update state with `data`. This can be called multiple times.
171    pub fn update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
172        self._state._update(data)
173    }
174
175    /// Finalize the hash and put the final digest into `dest`.
176    pub(crate) fn _finalize_internal(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
177        self._state._finalize(dest)
178    }
179
180    #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
181    /// Return a SHA256 digest.
182    pub fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> {
183        let mut digest = [0u8; SHA256_OUTSIZE];
184        self._finalize_internal(&mut digest)?;
185
186        Ok(Digest::from(digest))
187    }
188
189    #[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
190    /// Calculate a SHA256 digest of some `data`.
191    pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> {
192        let mut ctx = Self::new();
193        ctx.update(data)?;
194        ctx.finalize()
195    }
196}
197
198impl crate::hazardous::mac::hmac::HmacHashFunction for Sha256 {
199    /// The blocksize of the hash function.
200    const _BLOCKSIZE: usize = SHA256_BLOCKSIZE;
201
202    /// The output size of the hash function.
203    const _OUTSIZE: usize = SHA256_OUTSIZE;
204
205    /// Create a new instance of the hash function.
206    fn _new() -> Self {
207        Self::new()
208    }
209
210    /// Update the internal state with `data`.
211    fn _update(&mut self, data: &[u8]) -> Result<(), UnknownCryptoError> {
212        self.update(data)
213    }
214
215    /// Finalize the hash and put the final digest into `dest`.
216    fn _finalize(&mut self, dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
217        self._finalize_internal(dest)
218    }
219
220    /// Compute a digest of `data` and copy it into `dest`.
221    fn _digest(data: &[u8], dest: &mut [u8]) -> Result<(), UnknownCryptoError> {
222        let mut ctx = Self::new();
223        ctx.update(data)?;
224        ctx._finalize_internal(dest)
225    }
226
227    #[cfg(test)]
228    fn compare_state_to_other(&self, other: &Self) {
229        self._state.compare_state_to_other(&other._state);
230    }
231}
232
233#[cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
234/// Example: hashing from a [`Read`](std::io::Read)er with SHA256.
235/// ```rust
236/// use orion::{
237///     hazardous::hash::sha2::sha256::{Sha256, Digest},
238///     errors::UnknownCryptoError,
239/// };
240/// use std::io::{self, Read, Write};
241///
242/// // `reader` could also be a `File::open(...)?`.
243/// let mut reader = io::Cursor::new(b"some data");
244/// let mut hasher = Sha256::new();
245/// std::io::copy(&mut reader, &mut hasher)?;
246///
247/// let digest: Digest = hasher.finalize()?;
248///
249/// # Ok::<(), Box<dyn std::error::Error>>(())
250/// ```
251#[cfg(feature = "safe_api")]
252impl io::Write for Sha256 {
253    /// Update the hasher's internal state with *all* of the bytes given.
254    /// If this function returns the `Ok` variant, it's guaranteed that it
255    /// will contain the length of the buffer passed to [`Write`](std::io::Write).
256    /// Note that this function is just a small wrapper over
257    /// [`Sha256::update`](crate::hazardous::hash::sha2::sha256::Sha256::update).
258    ///
259    /// ## Errors:
260    /// This function will only ever return the [`std::io::ErrorKind::Other`]()
261    /// variant when it returns an error. Additionally, this will always contain Orion's
262    /// [`UnknownCryptoError`] type.
263    fn write(&mut self, bytes: &[u8]) -> io::Result<usize> {
264        self.update(bytes).map_err(io::Error::other)?;
265        Ok(bytes.len())
266    }
267
268    /// This type doesn't buffer writes, so flushing is a no-op.
269    fn flush(&mut self) -> Result<(), io::Error> {
270        Ok(())
271    }
272}
273
274// Testing public functions in the module.
275#[cfg(test)]
276mod public {
277    use super::*;
278
279    #[test]
280    fn test_default_equals_new() {
281        let new = Sha256::new();
282        let default = Sha256::default();
283        new._state.compare_state_to_other(&default._state);
284    }
285
286    #[test]
287    #[cfg(feature = "safe_api")]
288    fn test_debug_impl() {
289        let initial_state = Sha256::new();
290        let debug = format!("{:?}", initial_state);
291        let expected = "Sha256 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU32(0), WordU32(0)], is_finalized: false } }";
292        assert_eq!(debug, expected);
293    }
294
295    mod test_streaming_interface {
296        use super::*;
297        use crate::test_framework::incremental_interface::*;
298
299        impl TestableStreamingContext<Digest> for Sha256 {
300            fn reset(&mut self) -> Result<(), UnknownCryptoError> {
301                self.reset();
302                Ok(())
303            }
304
305            fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> {
306                self.update(input)
307            }
308
309            fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> {
310                self.finalize()
311            }
312
313            fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> {
314                Sha256::digest(input)
315            }
316
317            fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> {
318                let actual: Digest = Self::one_shot(input)?;
319
320                if &actual == expected {
321                    Ok(())
322                } else {
323                    Err(UnknownCryptoError)
324                }
325            }
326
327            fn compare_states(state_1: &Sha256, state_2: &Sha256) {
328                state_1._state.compare_state_to_other(&state_2._state);
329            }
330        }
331
332        #[test]
333        fn default_consistency_tests() {
334            let initial_state: Sha256 = Sha256::new();
335
336            let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new(
337                initial_state,
338                SHA256_BLOCKSIZE,
339            );
340            test_runner.run_all_tests();
341        }
342
343        #[quickcheck]
344        #[cfg(feature = "safe_api")]
345        /// Related bug: https://github.com/orion-rs/orion/issues/46
346        /// Test different streaming state usage patterns.
347        fn prop_input_to_consistency(data: Vec<u8>) -> bool {
348            let initial_state: Sha256 = Sha256::new();
349
350            let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new(
351                initial_state,
352                SHA256_BLOCKSIZE,
353            );
354            test_runner.run_all_tests_property(&data);
355            true
356        }
357    }
358
359    #[cfg(feature = "safe_api")]
360    mod test_io_impls {
361        use crate::hazardous::hash::sha2::sha256::Sha256;
362        use std::io::Write;
363
364        #[quickcheck]
365        fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool {
366            let mut hasher_a = Sha256::new();
367            let mut hasher_b = hasher_a.clone();
368
369            hasher_a.update(&data).unwrap();
370            hasher_b.write_all(&data).unwrap();
371
372            // Additionally make sure flush() is a no-op, which we expect.
373            hasher_b.flush().unwrap();
374            hasher_a._state.compare_state_to_other(&hasher_b._state);
375
376            let hash_a = hasher_a.finalize().unwrap();
377            let hash_b = hasher_b.finalize().unwrap();
378
379            // Additionally make sure flush() is a no-op, which we expect.
380            hasher_b.flush().unwrap();
381            hasher_a._state.compare_state_to_other(&hasher_b._state);
382
383            hash_a == hash_b
384        }
385    }
386}
387
388// Testing private functions in the module.
389#[cfg(test)]
390mod private {
391    use super::*;
392
393    mod test_increment_mlen {
394        use super::*;
395
396        #[test]
397        fn test_mlen_increase_values() {
398            let mut context = Sha256::default();
399
400            context._state.increment_mlen(&WordU32::from(1u32));
401            assert_eq!(context._state.message_len[0], WordU32::from(0u32));
402            assert_eq!(context._state.message_len[1], WordU32::from(8u32));
403
404            context._state.increment_mlen(&WordU32::from(17u32));
405            assert_eq!(context._state.message_len[0], WordU32::from(0u32));
406            assert_eq!(context._state.message_len[1], WordU32::from(144u32));
407
408            context._state.increment_mlen(&WordU32::from(12u32));
409            assert_eq!(context._state.message_len[0], WordU32::from(0u32));
410            assert_eq!(context._state.message_len[1], WordU32::from(240u32));
411
412            // Overflow
413            context._state.increment_mlen(&WordU32::from(u32::MAX / 8));
414            assert_eq!(context._state.message_len[0], WordU32::from(1u32));
415            assert_eq!(context._state.message_len[1], WordU32::from(232u32));
416        }
417
418        #[test]
419        #[should_panic]
420        fn test_panic_on_second_overflow() {
421            let mut context = Sha256::default();
422            context._state.message_len = [WordU32::MAX, WordU32::from(u32::MAX - 7)];
423            // u32::MAX - 7, to leave so that the length represented
424            // in bites should overflow by exactly one.
425            context._state.increment_mlen(&WordU32::from(1u32));
426        }
427    }
428}