highway/lib.rs
1/*!
2
3This crate is a native Rust port of [Google's HighwayHash](https://github.com/google/highwayhash), which is a fast, keyed, and strong hash function, whose output is hardware independent.
4
5## Caution
6
7`HighwayHash` (the algorithm) has not undergone extensive cryptanalysis like SipHash (the default hashing algorithm in Rust), but according to the authors, `HighwayHash` output bits are uniformly distributed and should withstand differential and rotational attacks. Hence `HighwayHash` is referred to as a strong hash function, not a cryptographic hash function. I encourage anyone interested to [peruse the paper](https://arxiv.org/abs/1612.06257) to understand the risks.
8
9## Examples
10
11The quickest way to get started:
12
13```rust
14use highway::{HighwayHasher, HighwayHash};
15let res: u64 = HighwayHasher::default().hash64(&[]);
16let res2: [u64; 2] = HighwayHasher::default().hash128(&[]);
17let res3: [u64; 4] = HighwayHasher::default().hash256(&[]);
18```
19
20A more complete tour of the API follows:
21
22```rust
23use highway::{HighwayHasher, HighwayHash, Key};
24
25// HighwayHash requires a key that should be hidden from attackers
26// to ensure outputs are unpredictable, so attackers can't mount
27// DoS attacks.
28let key = Key([1, 2, 3, 4]);
29
30// A HighwayHasher is the recommended approach to hashing,
31// as it will select the fastest algorithm available
32let mut hasher = HighwayHasher::new(key);
33
34// Append some data
35hasher.append(&[255]);
36
37// After all data has been appended, you ask for
38// 64, 128, or 256bit output. The hasher is consumed
39// after finalization.
40let res: u64 = hasher.finalize64();
41
42assert_eq!(0x07858f24d_2d79b2b2, res);
43```
44
45Creating a 128bit and 256bit hash is just as simple.
46
47```rust
48use highway::{HighwayHasher, HighwayHash, Key};
49
50// Generate 128bit hash
51let key = Key([1, 2, 3, 4]);
52let mut hasher128 = HighwayHasher::new(key);
53hasher128.append(&[255]);
54let res128: [u64; 2] = hasher128.finalize128();
55assert_eq!([0xbb007d2462e77f3c, 0x224508f916b3991f], res128);
56
57// Generate 256bit hash
58let key = Key([1, 2, 3, 4]);
59let mut hasher256 = HighwayHasher::new(key);
60hasher256.append(&[255]);
61let res256: [u64; 4] = hasher256.finalize256();
62let expected: [u64; 4] = [
63 0x7161cadbf7cd70e1,
64 0xaac4905de62b2f5e,
65 0x7b02b936933faa7,
66 0xc8efcfc45b239f8d,
67];
68assert_eq!(expected, res256);
69```
70
71Use highway hash in standard rust collections
72
73```rust
74# #[cfg(feature = "std")]
75# {
76use std::collections::HashMap;
77use highway::{HighwayBuildHasher, Key};
78let mut map =
79 HashMap::with_hasher(HighwayBuildHasher::new(Key([
80 0xcbf29ce484222325,
81 0xc3a5c85c97cb3127,
82 0xb492b66fbe98f273,
83 0x9ae16a3b2f90404f,
84 ])));
85
86map.insert(1, 2);
87assert_eq!(map.get(&1), Some(&2));
88# }
89```
90
91Or if utilizing a key is not important, one can use the default
92
93```rust
94# #[cfg(feature = "std")]
95# {
96use std::collections::HashMap;
97use std::hash::BuildHasherDefault;
98use highway::HighwayHasher;
99let mut map =
100 HashMap::with_hasher(BuildHasherDefault::<HighwayHasher>::default());
101
102map.insert(1, 2);
103assert_eq!(map.get(&1), Some(&2));
104# }
105```
106
107Hashing a file, or anything implementing `Read`
108
109```rust
110# #[cfg(not(feature = "std"))] fn main() { }
111# #[cfg(feature = "std")]
112# fn main() -> std::io::Result<()> {
113use std::hash::Hasher;
114use highway::{PortableHash, HighwayHash};
115
116let mut file = &b"hello world"[..];
117
118// We're using the `PortableHash` to show importing a specific hashing
119// implementation (all hash outputs are already portable / hardware agnostic).
120// The main reason for directly using `PortableHash` would be if avoiding
121// `unsafe` code blocks is a top priority.
122let mut hasher = PortableHash::default();
123std::io::copy(&mut file, &mut hasher)?;
124let hash64 = hasher.finish(); // core Hasher API
125let hash256 = hasher.finalize256(); // HighwayHash API
126# Ok(())
127# }
128```
129
130## Use Cases
131
132`HighwayHash` can be used against untrusted user input where weak hashes can't be used due to exploitation, verified cryptographic hashes are too slow, and a strong hash function meets requirements. Some specific scenarios given by the authors of HighwayHash:
133
134- Use 64bit hashes to for authenticating short lived messages
135- Use 256bit hashes for checksums. Think file storage (S3) or any longer lived data where there is a need for strong guarantees against collisions.
136
137`HighwayHash` may not be a good fit if the payloads trend small (< 100 bytes) and speed is up of the utmost importance, as HighwayHash hits its stride at larger payloads.
138
139## Wasm SIMD
140
141When deploying HighwayHash to a Wasm environment, one can opt into using the Wasm SIMD instructions by adding a Rust flag:
142
143```bash
144RUSTFLAGS="-C target-feature=+simd128" wasm-pack build
145```
146
147Then `HighwayHasher` will automatically defer to the Wasm SIMD implementation via `WasmHash`.
148
149Once opted in, the execution environment must support Wasm SIMD instructions, which Chrome, Firefox, and Node LTS have stabilized since mid-2021. The opt in is required as there is not a way for Wasm to detect SIMD capabilities at runtime. The mere presence of Wasm SIMD instructions will cause incompatible environments to fail to compile, so it is recommended to provide two Wasm payloads to downstream users: one with SIMD enabled and one without.
150
151### `no_std` crates
152
153Be aware that the `no_std` version is unable to detect CPU features and so will always default to the portable implementation. If building for a known SSE 4.1 or AVX 2 machine (and the majority of machines in the last decade will support SSE 4.1), then explicitly enable the target feature:
154
155```bash
156RUSTFLAGS="-C target-feature=+sse4.1" cargo test
157RUSTFLAGS="-C target-feature=+avx2" cargo test
158```
159
160*/
161#![allow(non_snake_case)]
162#![cfg_attr(all(not(feature = "std"), not(test)), no_std)]
163#![warn(missing_docs)]
164#![deny(unsafe_code)]
165
166#[macro_use]
167mod macros;
168mod builder;
169mod hash;
170mod internal;
171mod key;
172mod portable;
173mod traits;
174
175pub use crate::builder::HighwayHasher;
176pub use crate::hash::HighwayBuildHasher;
177pub use crate::key::Key;
178pub use crate::portable::PortableHash;
179pub use crate::traits::HighwayHash;
180
181#[cfg(target_arch = "aarch64")]
182mod aarch64;
183#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
184mod wasm;
185#[cfg(target_arch = "x86_64")]
186mod x86;
187
188#[cfg(target_arch = "aarch64")]
189pub use crate::aarch64::NeonHash;
190#[cfg(target_arch = "x86_64")]
191pub use crate::x86::{AvxHash, SseHash};
192
193#[cfg(all(target_family = "wasm", target_feature = "simd128"))]
194pub use crate::wasm::WasmHash;