orion/high_level/hash.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//! Hashing.
24//!
25//! # Use case:
26//! `orion::hash` can be used to hash some given data.
27//!
28//! An example of this could be using hashes of files to ensure integrity.
29//! Meaning, checking if a file has been modified since the time the hash was
30//! recorded.
31//!
32//! If you are looking for a keyed hash, please see the [`orion::auth`](super::auth) module.
33//!
34//! # About:
35//! - Uses BLAKE2b with an output size of 32 bytes (i.e BLAKE2b-256).
36//!
37//! # Parameters:
38//! - `data`: The data to be hashed.
39//!
40//! # Panics:
41//! A panic will occur if:
42//! - More than 2*(2^64-1) bytes of data are hashed.
43//!
44//! # Security:
45//! - This interface does not support supplying BLAKE2b with a secret key, and
46//! the hashes retrieved
47//! from using `orion::hash` are therefore not suitable as MACs.
48//! - BLAKE2b is not suitable for password hashing. See [`orion::pwhash`](super::pwhash)
49//! instead.
50//!
51//! # Examples
52//!
53//! ## Hashing in-memory data
54//! ```rust
55//! use orion::hash::{digest, Digest};
56//!
57//! let hash: Digest = digest(b"Some data")?;
58//! # Ok::<(), orion::errors::UnknownCryptoError>(())
59//! ```
60//!
61//! ## Hashing data from an arbitrary reader
62//! ```rust
63//! use orion::hash::{digest_from_reader, Digest};
64//!
65//! // `reader` could instead be `File::open("file.txt")?`
66//! let reader = std::io::Cursor::new(b"some data");
67//! let hash: Digest = digest_from_reader(reader)?;
68//! # Ok::<(), orion::errors::UnknownCryptoError>(())
69//! ```
70
71#![cfg_attr(docsrs, doc(cfg(feature = "safe_api")))]
72
73pub use crate::hazardous::hash::blake2::blake2b::Digest;
74use crate::{errors::UnknownCryptoError, hazardous::hash::blake2::blake2b};
75
76#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
77/// Hashing using BLAKE2b-256.
78pub fn digest(data: &[u8]) -> Result<Digest, UnknownCryptoError> {
79 blake2b::Hasher::Blake2b256.digest(data)
80}
81
82/// Hash data from a [`Read`](std::io::Read)` type using BLAKE2b-256.
83///
84/// See the [module-level docs](crate::hash) for an example of how to use this function.
85/// Internally calls [`std::io::copy`]() to move data from the reader into the Blake2b writer.
86/// Note that the [`std::io::copy`]() function buffers reads, so passing in a
87/// [`BufReader`](std::io::BufReader) may be unnecessary.
88///
89/// For lower-level control over reads, writes, buffer sizes, *etc.*, consider using the
90/// [`Blake2b`](crate::hazardous::hash::blake2::blake2b::Blake2b) type and its
91/// [`Write`](std::io::Write) implementation directly. See `Blake2b`'s `Write` implementation
92/// and/or its `Write` documentation for an example.
93///
94/// ## Errors:
95/// This function will only ever return the [`std::io::ErrorKind::Other`]()
96/// variant when it returns an error. Additionally, this will always contain Orion's
97/// [`UnknownCryptoError`](crate::errors::UnknownCryptoError) type.
98///
99/// Note that if an error is returned, data may still have been consumed from the given reader.
100#[cfg(feature = "safe_api")]
101#[must_use = "SECURITY WARNING: Ignoring a Result can have real security implications."]
102pub fn digest_from_reader(mut reader: impl std::io::Read) -> Result<Digest, UnknownCryptoError> {
103 let mut hasher = blake2b::Blake2b::new(32)?;
104 std::io::copy(&mut reader, &mut hasher).map_err(|_| UnknownCryptoError)?;
105 hasher.finalize()
106}
107
108// Testing public functions in the module.
109#[cfg(feature = "safe_api")]
110#[cfg(test)]
111mod public {
112 use super::*;
113
114 #[quickcheck]
115 /// Hashing twice with same input should always produce same output.
116 fn prop_digest_same_result(input: Vec<u8>) -> bool {
117 digest(&input[..]).unwrap() == digest(&input[..]).unwrap()
118 }
119
120 #[quickcheck]
121 /// Hashing all input should be the same as wrapping it in a
122 /// cursor and using digest_from_reader.
123 fn prop_digest_same_as_digest_from_reader(input: Vec<u8>) -> bool {
124 let digest_a = digest_from_reader(std::io::Cursor::new(&input)).unwrap();
125 let digest_b = digest(&input).unwrap();
126 digest_a == digest_b
127 }
128
129 #[quickcheck]
130 /// Hashing twice with different input should never produce same output.
131 fn prop_digest_diff_result(input: Vec<u8>) -> bool {
132 digest(&input[..]).unwrap() != digest(b"Completely wrong input").unwrap()
133 }
134}