Hashing
Recipe | Crates | Categories |
---|---|---|
Calculate the SHA-256 digest of a file | ||
sha2 | ||
sha1 | ||
md-5 | ||
blake3 |
Hashing is a process that converts input data of any size into a fixed-size string of characters, typically a sequence of numbers and letters. The output, known as a hash value or hash code, is generated by a hash function. Hash functions are designed to be fast, deterministic (the same input will always produce the same output), and produce a unique hash for distinct inputs. One key feature of hashing is that it's a one-way function; you can't reverse-engineer the original input from the hash value.
Hashes are widely used in various applications such as:
- Data Integrity: Ensuring that data has not been altered by comparing the hash value of the original data with the hash value of the received data.
- Password Storage: Storing hashed versions of passwords rather than plain text to enhance security.
- Digital Signatures: Verifying the authenticity and integrity of messages or documents.
Calculate the SHA-256 digest of a file
SHA-256 (Secure Hash Algorithm 256-bit) is part of the SHA-2 family of cryptographic hash functions. It produces a fixed-size 256-bit hash value (64 characters) from input data of any size. SHA-256 is widely used in applications such as digital signatures, certificate generation, and data integrity verification.
Writes some data to a file, then calculates the SHA-256 digest::Digest
⮳ of the file's contents using digest::Context
⮳
use std::fs; use std::fs::File; use std::io::BufReader; use std::io::Read; use std::io::Write; use anyhow::Result; use data_encoding::HEXUPPER; use ring::digest::Context; use ring::digest::Digest; use ring::digest::SHA256; fn sha256_digest<R: Read>(mut reader: R) -> Result<Digest> { let mut context = Context::new(&SHA256); let mut buffer = [0; 1024]; loop { let count = reader.read(&mut buffer)?; if count == 0 { break; } context.update(&buffer[..count]); } Ok(context.finish()) } fn main() -> Result<()> { if !fs::exists("temp")? { fs::create_dir("temp")?; } let path = "temp/file.txt"; let mut output = File::create(path)?; write!(output, "We will generate a digest of this text")?; let input = File::open(path)?; let reader = BufReader::new(input); let digest = sha256_digest(reader)?; println!("SHA-256 digest is {}", HEXUPPER.encode(digest.as_ref())); Ok(()) }
Use general-purpose hashing algorithms
For more algorithms, see Rust Crypto Hashes: sha2, sha1, md-5
blake3
blake3
⮳ implements the BLAKE3 hash function. BLAKE3 is a cryptographic hash function that is faster than MD5, SHA-1, SHA-2, and SHA-3, yet is at least as secure as the latest standard SHA-3. It's designed to take advantage of parallel processing capabilities. BLAKE3 can produce hashes of arbitrary length, from short digests to longer ones. This is useful for various applications, including key derivation and password hashing. BLAKE3 allows for incremental hashing, where you can update the hash state with new data without recomputing the entire hash. This is useful for streaming data or situations where the input is received in chunks.
use blake3::Hasher; fn main() { // Example 1: Hashing a simple string let input_string = "Hello, world!"; let hash = blake3::hash(input_string.as_bytes()); println!("Hash of '{}': {}", input_string, hash); // Example 2: Incremental hashing let mut hasher = Hasher::new(); hasher.update(b"The quick brown "); hasher.update(b"fox jumps over "); hasher.update(b"the lazy dog."); let hash2 = hasher.finalize(); println!("Incremental hash: {}", hash2); // Example 3: Hashing a larger byte array let large_data: Vec<u8> = (0..1024).map(|i| (i % 256) as u8).collect(); // Example 1KB data let hash3 = blake3::hash(&large_data); println!("Hash of 1KB data: {}", hash3); // Example 4: Using a key for keyed hashing (KMAC) let key: &[u8; 32] = b"mysecretkeymysecretkey__________"; let mut hasher_keyed = blake3::Hasher::new_keyed(key); hasher_keyed.update(b"Message to be keyed hashed"); let keyed_hash = hasher_keyed.finalize(); // OR let mac = blake3::keyed_hash(key, b"foo"); println!("Keyed hash: {}", keyed_hash); // Example 5: Deriving a key using a context string // Given cryptographic key material of any length and a context string of // any length, `derive_key` outputs a 32-byte derived subkey. // The context string should be hardcoded, globally unique, and // application-specific. A good default format for such strings is // "[application] [commit timestamp] [purpose]", e.g., "example.com // 2019-12-25 16:18:03 session tokens v1". let context = "My application context"; let derived_key = blake3::derive_key(context, b"Input key material"); println!("Derived Key: {:?}", derived_key); // Example 6: Extended output. let mut output = [0u8; 1000]; let hasher = blake3::Hasher::new(); // Finalize the hash state and return an OutputReader, which can supply any // number of output bytes. let mut output_reader = hasher.finalize_xof(); output_reader.fill(&mut output); // OutputReader also implements Read and Seek. println!("Output: {:x?}", output); }
sha2
SHA-2 (Secure Hash Algorithm 2) is a family of cryptographic hash functions designed by the National Security Agency (NSA) and standardized by NIST.
sha2
⮳ is a pure Rust implementation of the SHA-2 hash function family, including SHA-224, SHA-256, SHA-384, and SHA-512. SHA-256 is the most commonly used variant.
// Constant-Time Base64 encoding use base64ct::Base64; use base64ct::Encoding; // Convenience wrapper trait covering functionality of // cryptographic hash functions with fixed output size. use sha2::Digest; // SHA-256 hasher use sha2::Sha256; // SHA-512 hasher. use sha2::Sha512; fn main() -> anyhow::Result<()> { // If a complete message is available, then you can use the convenience // `Digest::digest` method: let hash1 = Sha256::digest(b"my message"); // Print the hash as a hexadecimal string println!("SHA-256 hash #1: {:x}", hash1); // Otherwise, create a Sha256 hasher let mut hasher = Sha256::new(); // Add input data let data = b"hello world"; hasher.update(data); // `update` can be called repeatedly and is generic over `AsRef<[u8]>` hasher.update("String data"); // Read hash digest and consume hasher let hash2 = hasher.finalize(); println!("SHA-256 hash #2: {:x}", hash2); // Same exercise, but using `Sha512` and `chain_update`: let hash3 = Sha512::new() .chain_update(b"Hello world!") .chain_update("String data") .finalize(); let base64_hash = Base64::encode_string(&hash3); println!("Base64-encoded hash #3: {}", base64_hash); // Hash the contents of a file: // First, we will create a file inside of `env::temp_dir()`. let mut file = tempfile::tempfile()?; use std::io::Write; writeln!(file, "Some data")?; // or: let mut file = fs::File::open(&some_path)?; // Copies the entire contents of a reader into a writer, // in this case the hasher let mut hasher = Sha256::new(); std::io::copy(&mut file, &mut hasher)?; let hash4 = hasher.finalize(); // Constant-time conversion to hexadecimal let hex_hash = base16ct::lower::encode_string(&hash4); println!("Hex-encoded hash #4: {}", hex_hash); Ok(()) }
sha1
sha1
⮳ implements the SHA-1 hash function.
use hex_literal::hex; use sha1::Digest; use sha1::Sha1; // SHA-1 is considered cryptographically broken and should NOT be used for new // security-critical applications. It is primarily used for legacy compatibility // or non-security-sensitive purposes. fn main() { // Create a SHA-1 hasher let mut hasher = Sha1::new(); // Process input message hasher.update(b"hello world"); // Compute the hash digest let result = hasher.finalize(); // Assert the expected hash value assert_eq!(result[..], hex!("2aae6c35c94fcfb415dbe95f408b9ce91ee846ed")); println!("SHA-1 hash of 'hello world': {:x}", result); } // Example adapted from https://docs.rs/sha1/0.10.6/sha1/index.html
md-5
md-5
⮳ implements the MD5 hash function.
// WARNING: MD5 should be considered cryptographically broken and unsuitable for // further use. Collision attacks against MD5 are both practical and trivial. // This crate does not implement the `digest` traits, so it is not interoperable // with the RustCrypto ecosystem. fn main() { // Input data let data = "hello world"; // Compute MD5 hash let digest = md5::compute(data); // Print the hash as a hexadecimal string println!("MD5 hash of '{}': {:x}", data, digest); }