chksum_hash_sha2_256/
lib.rs

1//! This crate provides an implementation of the SHA-2 256 hash function based on [FIPS PUB 180-4: Secure Hash Standard](https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.180-4.pdf).
2//!
3//! # Setup
4//!
5//! To use this crate, add the following entry to your `Cargo.toml` file in the `dependencies` section:
6//!
7//! ```toml
8//! [dependencies]
9//! chksum-hash-sha2-256 = "0.0.1"
10//! ```
11//!
12//! Alternatively, you can use the [`cargo add`](https://doc.rust-lang.org/cargo/commands/cargo-add.html) subcommand:
13//!
14//! ```sh
15//! cargo add chksum-hash-sha2-256
16//! ```     
17//!
18//! # Batch Processing
19//!
20//! The digest of known-size data can be calculated with the [`hash`] function.
21//!
22//! ```rust
23//! use chksum_hash_sha2_256 as sha2_256;
24//!
25//! let digest = sha2_256::hash("example data");
26//! assert_eq!(
27//!     digest.to_hex_lowercase(),
28//!     "44752f37272e944fd2c913a35342eaccdd1aaf189bae50676b301ab213fc5061"
29//! );
30//! ```
31//!
32//! # Stream Processing
33//!
34//! The digest of data streams can be calculated chunk-by-chunk with a consumer created by calling the [`default`] function.
35//!
36//! ```rust
37//! // Import all necessary items
38//! # use std::io;
39//! # use std::path::PathBuf;
40//! use std::fs::File;
41//! use std::io::Read;
42//!
43//! use chksum_hash_sha2_256 as sha2_256;
44//!
45//! # fn wrapper(path: PathBuf) -> io::Result<()> {
46//! // Create a hash instance
47//! let mut hash = sha2_256::default();
48//!
49//! // Open a file and create a buffer for incoming data
50//! let mut file = File::open(path)?;
51//! let mut buffer = vec![0; 64];
52//!
53//! // Iterate chunk by chunk
54//! while let Ok(count) = file.read(&mut buffer) {
55//!     // EOF reached, exit loop
56//!     if count == 0 {
57//!         break;
58//!     }
59//!
60//!     // Update the hash with data
61//!     hash.update(&buffer[..count]);
62//! }
63//!
64//! // Calculate the digest
65//! let digest = hash.digest();
66//! // Cast the digest to hex and compare
67//! assert_eq!(
68//!     digest.to_hex_lowercase(),
69//!     "44752f37272e944fd2c913a35342eaccdd1aaf189bae50676b301ab213fc5061"
70//! );
71//! # Ok(())
72//! # }
73//! ```
74//!
75//! # Internal Buffering
76//!
77//! An internal buffer is utilized due to the unknown size of data chunks.
78//!
79//! The size of this buffer is at least as large as one hash block of data processed at a time.
80//!
81//! To mitigate buffering-related performance issues, ensure the length of processed chunks is a multiple of the block size.
82//!
83//! # Input Type
84//!
85//! Anything that implements `AsRef<[u8]>` can be passed as input.
86//!
87//! ```rust
88//! use chksum_hash_sha2_256 as sha2_256;
89//!
90//! let digest = sha2_256::default()
91//!     .update("str")
92//!     .update(b"bytes")
93//!     .update([0x75, 0x38])
94//!     .digest();
95//! assert_eq!(
96//!     digest.to_hex_lowercase(),
97//!     "61466aaea66ab788a5f507ecd6061292c95e18fb9e144eab023a899aa96b59cb"
98//! );
99//! ```
100//!
101//! Since [`Digest`] implements `AsRef<[u8]>`, digests can be chained to calculate hash of a hash digest.
102//!
103//! ```rust
104//! use chksum_hash_sha2_256 as sha2_256;
105//!
106//! let digest = sha2_256::hash(b"example data");
107//! let digest = sha2_256::hash(digest);
108//! assert_eq!(
109//!     digest.to_hex_lowercase(),
110//!     "3290ecdd193eddf06676521ba0cf802dff85d3be5b2cf4725b79800af6626102"
111//! );
112//! ```
113//!
114//! # License
115//!
116//! This crate is licensed under the MIT License.
117
118#![cfg_attr(docsrs, feature(doc_auto_cfg))]
119#![forbid(unsafe_code)]
120
121pub mod block;
122pub mod digest;
123pub mod state;
124
125use chksum_hash_core as core;
126
127use crate::block::Block;
128#[doc(inline)]
129pub use crate::block::LENGTH_BYTES as BLOCK_LENGTH_BYTES;
130#[doc(inline)]
131pub use crate::digest::{Digest, LENGTH_BYTES as DIGEST_LENGTH_BYTES};
132#[doc(inline)]
133pub use crate::state::State;
134
135/// Creates a new hash.
136///
137/// # Example
138///
139/// ```rust
140/// use chksum_hash_sha2_256 as sha2_256;
141///
142/// let digest = sha2_256::new().digest();
143/// assert_eq!(
144///     digest.to_hex_lowercase(),
145///     "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
146/// );
147///
148/// let digest = sha2_256::new().update("data").digest();
149/// assert_eq!(
150///     digest.to_hex_lowercase(),
151///     "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"
152/// );
153/// ```
154#[must_use]
155pub fn new() -> Update {
156    Update::new()
157}
158
159/// Creates a default hash.
160///
161/// # Example
162///
163/// ```rust
164/// use chksum_hash_sha2_256 as sha2_256;
165///
166/// let digest = sha2_256::default().digest();
167/// assert_eq!(
168///     digest.to_hex_lowercase(),
169///     "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
170/// );
171///
172/// let digest = sha2_256::default().update("data").digest();
173/// assert_eq!(
174///     digest.to_hex_lowercase(),
175///     "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"
176/// );
177/// ```
178#[must_use]
179pub fn default() -> Update {
180    core::default()
181}
182
183/// Computes the hash of the given input.
184///
185/// # Example
186///
187/// ```rust
188/// use chksum_hash_sha2_256 as sha2_256;
189///
190/// let digest = sha2_256::hash("data");
191/// assert_eq!(
192///     digest.to_hex_lowercase(),
193///     "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"
194/// );
195/// ```
196pub fn hash(data: impl AsRef<[u8]>) -> Digest {
197    core::hash::<Update>(data)
198}
199
200/// A hash state containing an internal buffer that can handle an unknown amount of input data.
201///
202/// # Example
203///
204/// ```rust
205/// use chksum_hash_sha2_256 as sha2_256;
206///
207/// // Create a new hash instance
208/// let mut hash = sha2_256::Update::new();
209///
210/// // Fill with data
211/// hash.update("data");
212///
213/// // Finalize and create a digest
214/// let digest = hash.finalize().digest();
215/// assert_eq!(
216///     digest.to_hex_lowercase(),
217///     "3a6eb0790f39ac87c94f3856b2dd2c5d110e6811602261a9a923d3bb23adc8b7"
218/// );
219///
220/// // Reset to default values
221/// hash.reset();
222///
223/// // Produce a hash digest using internal finalization
224/// let digest = hash.digest();
225/// assert_eq!(
226///     digest.to_hex_lowercase(),
227///     "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
228/// );
229/// ```
230#[derive(Clone, Debug, Eq, PartialEq)]
231#[cfg_attr(feature = "fuzzing", derive(arbitrary::Arbitrary))]
232pub struct Update {
233    state: State,
234    unprocessed: Vec<u8>,
235    processed: usize,
236}
237
238impl Update {
239    /// Creates a new hash.
240    #[must_use]
241    pub fn new() -> Self {
242        let state = state::new();
243        let unprocessed = Vec::with_capacity(BLOCK_LENGTH_BYTES);
244        let processed = 0;
245        Self {
246            state,
247            unprocessed,
248            processed,
249        }
250    }
251
252    /// Updates the internal state with an input data.
253    ///
254    /// # Performance issues
255    ///
256    /// To achieve maximum performance, the length of incoming data parts should be a multiple of the block length.
257    ///
258    /// In any other case, an internal buffer is used, which can cause a speed decrease in performance.
259    pub fn update(&mut self, data: impl AsRef<[u8]>) -> &mut Self {
260        let data = data.as_ref();
261
262        // The `chunks_exact` method doesn't drain original vector so it needs to be handled manually
263        for _ in 0..(self.unprocessed.len() / BLOCK_LENGTH_BYTES) {
264            let block = {
265                let chunk = self.unprocessed.drain(..BLOCK_LENGTH_BYTES);
266                let chunk = chunk.as_slice();
267                Block::try_from(chunk)
268                    .expect("chunk length must be exact size as block")
269                    .into()
270            };
271            self.state = self.state.update(block);
272            self.processed = self.processed.wrapping_add(BLOCK_LENGTH_BYTES);
273        }
274
275        if self.unprocessed.is_empty() {
276            // Internal buffer is empty, incoming data can be processed without buffering.
277            let mut chunks = data.chunks_exact(BLOCK_LENGTH_BYTES);
278            for chunk in chunks.by_ref() {
279                let block = Block::try_from(chunk)
280                    .expect("chunk length must be exact size as block")
281                    .into();
282                self.state = self.state.update(block);
283                self.processed = self.processed.wrapping_add(BLOCK_LENGTH_BYTES);
284            }
285            let remainder = chunks.remainder();
286            if !remainder.is_empty() {
287                self.unprocessed.extend(remainder);
288            }
289        } else if (self.unprocessed.len() + data.len()) < BLOCK_LENGTH_BYTES {
290            // Not enough data even for one block.
291            self.unprocessed.extend(data);
292        } else {
293            // Create the first block from the buffer, create the second (and every other) block from incoming data.
294            let unprocessed = self.unprocessed.len() % BLOCK_LENGTH_BYTES;
295            let missing = BLOCK_LENGTH_BYTES - unprocessed;
296            let (fillment, data) = data.split_at(missing);
297            let block = {
298                let mut block = [0u8; BLOCK_LENGTH_BYTES];
299                let (first_part, second_part) = block.split_at_mut(self.unprocessed.len());
300                first_part.copy_from_slice(self.unprocessed.drain(..self.unprocessed.len()).as_slice());
301                second_part[..missing].copy_from_slice(fillment);
302                block
303            };
304            let mut chunks = block.chunks_exact(BLOCK_LENGTH_BYTES);
305            for chunk in chunks.by_ref() {
306                let block = Block::try_from(chunk)
307                    .expect("chunk length must be exact size as block")
308                    .into();
309                self.state = self.state.update(block);
310                self.processed = self.processed.wrapping_add(BLOCK_LENGTH_BYTES);
311            }
312            let remainder = chunks.remainder();
313            assert!(remainder.is_empty(), "chunks remainder must be empty");
314
315            let mut chunks = data.chunks_exact(BLOCK_LENGTH_BYTES);
316            for chunk in chunks.by_ref() {
317                let block = Block::try_from(chunk)
318                    .expect("chunk length must be exact size as block")
319                    .into();
320                self.state = self.state.update(block);
321                self.processed = self.processed.wrapping_add(BLOCK_LENGTH_BYTES);
322            }
323            let remainder = chunks.remainder();
324            self.unprocessed.extend(remainder);
325        }
326
327        self
328    }
329
330    /// Applies padding and produces the finalized state.
331    #[must_use]
332    pub fn finalize(&self) -> Finalize {
333        let mut state = self.state;
334        let mut processed = self.processed;
335        let unprocessed = {
336            let mut chunks = self.unprocessed.chunks_exact(BLOCK_LENGTH_BYTES);
337            for chunk in chunks.by_ref() {
338                let block = Block::try_from(chunk)
339                    .expect("chunk length must be exact size as block")
340                    .into();
341                state = state.update(block);
342                processed = processed.wrapping_add(BLOCK_LENGTH_BYTES);
343            }
344            chunks.remainder()
345        };
346
347        let length = {
348            let length = unprocessed.len().wrapping_add(processed) as u64;
349            let length = length.wrapping_mul(8); // convert byte-length into bits-length
350            length.to_be_bytes()
351        };
352
353        if (unprocessed.len() + 1 + length.len()) <= BLOCK_LENGTH_BYTES {
354            let padding = {
355                let mut padding = [0u8; BLOCK_LENGTH_BYTES];
356                padding[..unprocessed.len()].copy_from_slice(&unprocessed[..unprocessed.len()]);
357                padding[unprocessed.len()] = 0x80;
358                padding[(BLOCK_LENGTH_BYTES - length.len())..].copy_from_slice(&length);
359                padding
360            };
361
362            let block = {
363                let block = &padding[..];
364                Block::try_from(block)
365                    .expect("padding length must exact size as block")
366                    .into()
367            };
368            state = state.update(block);
369        } else {
370            let padding = {
371                let mut padding = [0u8; BLOCK_LENGTH_BYTES * 2];
372                padding[..unprocessed.len()].copy_from_slice(&unprocessed[..unprocessed.len()]);
373                padding[unprocessed.len()] = 0x80;
374                padding[(BLOCK_LENGTH_BYTES * 2 - length.len())..].copy_from_slice(&length);
375                padding
376            };
377
378            let block = {
379                let block = &padding[..BLOCK_LENGTH_BYTES];
380                Block::try_from(block)
381                    .expect("padding length must exact size as block")
382                    .into()
383            };
384            state = state.update(block);
385
386            let block = {
387                let block = &padding[BLOCK_LENGTH_BYTES..];
388                Block::try_from(block)
389                    .expect("padding length must exact size as block")
390                    .into()
391            };
392            state = state.update(block);
393        }
394
395        Finalize { state }
396    }
397
398    /// Resets the internal state to default values.
399    pub fn reset(&mut self) -> &mut Self {
400        self.state = self.state.reset();
401        self.unprocessed.clear();
402        self.processed = 0;
403        self
404    }
405
406    /// Produces the hash digest using internal finalization.
407    #[must_use]
408    pub fn digest(&self) -> Digest {
409        self.finalize().digest()
410    }
411}
412
413impl core::Update for Update {
414    type Digest = Digest;
415    type Finalize = Finalize;
416
417    fn update(&mut self, data: impl AsRef<[u8]>) {
418        self.update(data);
419    }
420
421    fn finalize(&self) -> Self::Finalize {
422        self.finalize()
423    }
424
425    fn reset(&mut self) {
426        self.reset();
427    }
428}
429
430impl Default for Update {
431    fn default() -> Self {
432        Self::new()
433    }
434}
435
436/// A finalized hash state.
437#[derive(Clone, Copy, Debug, Eq, PartialEq)]
438pub struct Finalize {
439    state: State,
440}
441
442impl Finalize {
443    /// Creates and returns the hash digest.
444    #[must_use]
445    #[rustfmt::skip]
446    pub fn digest(&self) -> Digest {
447        let State { a, b, c, d, e, f, g, h } = self.state;
448        let [a, b, c, d, e, f, g, h] = [
449            a.to_be_bytes(),
450            b.to_be_bytes(),
451            c.to_be_bytes(),
452            d.to_be_bytes(),
453            e.to_be_bytes(),
454            f.to_be_bytes(),
455            g.to_be_bytes(),
456            h.to_be_bytes(),
457        ];
458        Digest::new([
459            a[0], a[1], a[2], a[3],
460            b[0], b[1], b[2], b[3],
461            c[0], c[1], c[2], c[3],
462            d[0], d[1], d[2], d[3],
463            e[0], e[1], e[2], e[3],
464            f[0], f[1], f[2], f[3],
465            g[0], g[1], g[2], g[3],
466            h[0], h[1], h[2], h[3],
467        ])
468    }
469
470    /// Resets the hash state to the in-progress state.
471    #[must_use]
472    pub fn reset(&self) -> Update {
473        Update::new()
474    }
475}
476
477impl core::Finalize for Finalize {
478    type Digest = Digest;
479    type Update = Update;
480
481    fn digest(&self) -> Self::Digest {
482        self.digest()
483    }
484
485    fn reset(&self) -> Self::Update {
486        self.reset()
487    }
488}
489
490#[cfg(test)]
491mod tests {
492    use super::*;
493
494    #[test]
495    fn empty() {
496        let digest = default().digest().to_hex_lowercase();
497        assert_eq!(
498            digest,
499            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
500        );
501
502        let digest = new().digest().to_hex_lowercase();
503        assert_eq!(
504            digest,
505            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
506        );
507    }
508
509    #[test]
510    fn reset() {
511        let digest = new().update("data").reset().digest().to_hex_lowercase();
512        assert_eq!(
513            digest,
514            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
515        );
516
517        let digest = new().update("data").finalize().reset().digest().to_hex_lowercase();
518        assert_eq!(
519            digest,
520            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
521        );
522    }
523
524    #[test]
525    fn hello_world() {
526        let digest = new().update("Hello World").digest().to_hex_lowercase();
527        assert_eq!(
528            digest,
529            "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
530        );
531
532        let digest = new()
533            .update("Hello")
534            .update(" ")
535            .update("World")
536            .digest()
537            .to_hex_lowercase();
538        assert_eq!(
539            digest,
540            "a591a6d40bf420404a011733cfb7b190d62c65bf0bcda32b57b277d9ad9f146e"
541        );
542    }
543
544    #[test]
545    fn rust_book() {
546        let phrase = "Welcome to The Rust Programming Language, an introductory book about Rust. The Rust programming \
547                      language helps you write faster, more reliable software. High-level ergonomics and low-level \
548                      control are often at odds in programming language design; Rust challenges that conflict. \
549                      Through balancing powerful technical capacity and a great developer experience, Rust gives you \
550                      the option to control low-level details (such as memory usage) without all the hassle \
551                      traditionally associated with such control.";
552
553        let digest = hash(phrase).to_hex_lowercase();
554        assert_eq!(
555            digest,
556            "b2de5395f39bf32376693a9cdccc13da1d705d0eb9e9ec8c566a91f604fcc942"
557        );
558    }
559
560    #[test]
561    fn zeroes() {
562        let data = vec![0u8; 64];
563
564        let digest = new().update(&data[..60]).digest().to_hex_lowercase();
565        assert_eq!(
566            digest,
567            "5dcc1b5872dd9ff1c234501f1fefda01f664164e1583c3e1bb3dbea47588ab31"
568        );
569
570        let digest = new()
571            .update(&data[..60])
572            .update(&data[60..])
573            .digest()
574            .to_hex_lowercase();
575        assert_eq!(
576            digest,
577            "f5a5fd42d16a20302798ef6ed309979b43003d2320d9f0e8ea9831a92759fb4b"
578        );
579    }
580}