]>
Commit | Line | Data |
---|---|---|
0a29b90c FG |
1 | // MIT License |
2 | ||
49aad941 | 3 | // Copyright (c) 2020-2023 The orion Developers |
0a29b90c FG |
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 | ||
62 | use crate::errors::UnknownCryptoError; | |
63 | ||
64 | #[cfg(feature = "safe_api")] | |
65 | use std::io; | |
66 | ||
67 | /// The blocksize for the hash function SHA256. | |
68 | pub const SHA256_BLOCKSIZE: usize = 64; | |
69 | /// The output size for the hash function SHA256. | |
70 | pub const SHA256_OUTSIZE: usize = 32; | |
71 | /// The number of constants for the hash function SHA256. | |
72 | const N_CONSTS: usize = 64; | |
73 | ||
74 | construct_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 | ||
83 | impl_from_trait!(Digest, SHA256_OUTSIZE); | |
84 | ||
85 | use super::sha2_core::{State, Variant, Word}; | |
86 | use super::w32::WordU32; | |
87 | ||
88 | #[derive(Clone)] | |
89 | /// SHA256 streaming state. | |
90 | pub(crate) struct V256; | |
91 | ||
4b012472 | 92 | impl Variant<WordU32, N_CONSTS> for V256 { |
0a29b90c FG |
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. | |
146 | pub struct Sha256 { | |
4b012472 | 147 | pub(crate) _state: State<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>, |
0a29b90c FG |
148 | } |
149 | ||
150 | impl Default for Sha256 { | |
151 | fn default() -> Self { | |
152 | Self::new() | |
153 | } | |
154 | } | |
155 | ||
156 | impl Sha256 { | |
157 | /// Initialize a `Sha256` struct. | |
158 | pub fn new() -> Self { | |
159 | Self { | |
4b012472 | 160 | _state: State::<WordU32, V256, SHA256_BLOCKSIZE, SHA256_OUTSIZE, N_CONSTS>::_new(), |
0a29b90c FG |
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 | ||
198 | impl 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")] | |
252 | impl 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`](crate::errors::UnknownCryptoError) type. | |
263 | fn write(&mut self, bytes: &[u8]) -> io::Result<usize> { | |
264 | self.update(bytes) | |
265 | .map_err(|e| io::Error::new(io::ErrorKind::Other, e))?; | |
266 | Ok(bytes.len()) | |
267 | } | |
268 | ||
269 | /// This type doesn't buffer writes, so flushing is a no-op. | |
270 | fn flush(&mut self) -> Result<(), std::io::Error> { | |
271 | Ok(()) | |
272 | } | |
273 | } | |
274 | ||
275 | // Testing public functions in the module. | |
276 | #[cfg(test)] | |
277 | mod public { | |
278 | use super::*; | |
279 | ||
280 | #[test] | |
281 | fn test_default_equals_new() { | |
282 | let new = Sha256::new(); | |
283 | let default = Sha256::default(); | |
284 | new._state.compare_state_to_other(&default._state); | |
285 | } | |
286 | ||
287 | #[test] | |
288 | #[cfg(feature = "safe_api")] | |
289 | fn test_debug_impl() { | |
290 | let initial_state = Sha256::new(); | |
291 | let debug = format!("{:?}", initial_state); | |
292 | let expected = "Sha256 { _state: State { working_state: [***OMITTED***], buffer: [***OMITTED***], leftover: 0, message_len: [WordU32(0), WordU32(0)], is_finalized: false } }"; | |
293 | assert_eq!(debug, expected); | |
294 | } | |
295 | ||
296 | mod test_streaming_interface { | |
297 | use super::*; | |
298 | use crate::test_framework::incremental_interface::*; | |
299 | ||
300 | impl TestableStreamingContext<Digest> for Sha256 { | |
301 | fn reset(&mut self) -> Result<(), UnknownCryptoError> { | |
302 | self.reset(); | |
303 | Ok(()) | |
304 | } | |
305 | ||
306 | fn update(&mut self, input: &[u8]) -> Result<(), UnknownCryptoError> { | |
307 | self.update(input) | |
308 | } | |
309 | ||
310 | fn finalize(&mut self) -> Result<Digest, UnknownCryptoError> { | |
311 | self.finalize() | |
312 | } | |
313 | ||
314 | fn one_shot(input: &[u8]) -> Result<Digest, UnknownCryptoError> { | |
315 | Sha256::digest(input) | |
316 | } | |
317 | ||
318 | fn verify_result(expected: &Digest, input: &[u8]) -> Result<(), UnknownCryptoError> { | |
319 | let actual: Digest = Self::one_shot(input)?; | |
320 | ||
321 | if &actual == expected { | |
322 | Ok(()) | |
323 | } else { | |
324 | Err(UnknownCryptoError) | |
325 | } | |
326 | } | |
327 | ||
328 | fn compare_states(state_1: &Sha256, state_2: &Sha256) { | |
329 | state_1._state.compare_state_to_other(&state_2._state); | |
330 | } | |
331 | } | |
332 | ||
333 | #[test] | |
334 | fn default_consistency_tests() { | |
335 | let initial_state: Sha256 = Sha256::new(); | |
336 | ||
337 | let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new( | |
338 | initial_state, | |
339 | SHA256_BLOCKSIZE, | |
340 | ); | |
341 | test_runner.run_all_tests(); | |
342 | } | |
343 | ||
344 | #[quickcheck] | |
345 | #[cfg(feature = "safe_api")] | |
346 | /// Related bug: https://github.com/orion-rs/orion/issues/46 | |
347 | /// Test different streaming state usage patterns. | |
348 | fn prop_input_to_consistency(data: Vec<u8>) -> bool { | |
349 | let initial_state: Sha256 = Sha256::new(); | |
350 | ||
351 | let test_runner = StreamingContextConsistencyTester::<Digest, Sha256>::new( | |
352 | initial_state, | |
353 | SHA256_BLOCKSIZE, | |
354 | ); | |
355 | test_runner.run_all_tests_property(&data); | |
356 | true | |
357 | } | |
358 | } | |
359 | ||
360 | #[cfg(feature = "safe_api")] | |
361 | mod test_io_impls { | |
362 | use crate::hazardous::hash::sha2::sha256::Sha256; | |
363 | use std::io::Write; | |
364 | ||
365 | #[quickcheck] | |
366 | fn prop_hasher_write_same_as_update(data: Vec<u8>) -> bool { | |
367 | let mut hasher_a = Sha256::new(); | |
368 | let mut hasher_b = hasher_a.clone(); | |
369 | ||
370 | hasher_a.update(&data).unwrap(); | |
371 | hasher_b.write_all(&data).unwrap(); | |
372 | ||
373 | let hash_a = hasher_a.finalize().unwrap(); | |
374 | let hash_b = hasher_b.finalize().unwrap(); | |
375 | ||
376 | hash_a == hash_b | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
381 | // Testing private functions in the module. | |
382 | #[cfg(test)] | |
383 | mod private { | |
384 | use super::*; | |
385 | ||
386 | mod test_increment_mlen { | |
387 | use super::*; | |
388 | ||
389 | #[test] | |
390 | fn test_mlen_increase_values() { | |
391 | let mut context = Sha256::default(); | |
392 | ||
393 | context._state.increment_mlen(&WordU32::from(1u32)); | |
394 | assert_eq!(context._state.message_len[0], WordU32::from(0u32)); | |
395 | assert_eq!(context._state.message_len[1], WordU32::from(8u32)); | |
396 | ||
397 | context._state.increment_mlen(&WordU32::from(17u32)); | |
398 | assert_eq!(context._state.message_len[0], WordU32::from(0u32)); | |
399 | assert_eq!(context._state.message_len[1], WordU32::from(144u32)); | |
400 | ||
401 | context._state.increment_mlen(&WordU32::from(12u32)); | |
402 | assert_eq!(context._state.message_len[0], WordU32::from(0u32)); | |
403 | assert_eq!(context._state.message_len[1], WordU32::from(240u32)); | |
404 | ||
405 | // Overflow | |
406 | context._state.increment_mlen(&WordU32::from(u32::MAX / 8)); | |
407 | assert_eq!(context._state.message_len[0], WordU32::from(1u32)); | |
408 | assert_eq!(context._state.message_len[1], WordU32::from(232u32)); | |
409 | } | |
410 | ||
411 | #[test] | |
412 | #[should_panic] | |
413 | fn test_panic_on_second_overflow() { | |
414 | let mut context = Sha256::default(); | |
415 | context._state.message_len = [WordU32::MAX, WordU32::from(u32::MAX - 7)]; | |
416 | // u32::MAX - 7, to leave so that the length represented | |
417 | // in bites should overflow by exactly one. | |
418 | context._state.increment_mlen(&WordU32::from(1u32)); | |
419 | } | |
420 | } | |
421 | } |