82 lines
1.9 KiB
Rust
82 lines
1.9 KiB
Rust
//! SHA3-256 hashing (FIPS 202).
|
|
|
|
use sha3::{Digest, Sha3_256};
|
|
|
|
/// Compute SHA3-256 hash of `data`.
|
|
#[must_use]
|
|
pub fn hash(data: &[u8]) -> [u8; 32] {
|
|
Sha3_256::digest(data).into()
|
|
}
|
|
|
|
/// Compute the hex-encoded fingerprint (SHA3-256) of a public key.
|
|
#[must_use]
|
|
pub fn fingerprint_hex(pk: &[u8]) -> String {
|
|
let digest = hash(pk);
|
|
hex_encode(&digest)
|
|
}
|
|
|
|
/// Encode bytes as lowercase hex string.
|
|
fn hex_encode(bytes: &[u8]) -> String {
|
|
use std::fmt::Write;
|
|
let mut s = String::with_capacity(bytes.len() * 2);
|
|
for &b in bytes {
|
|
// fmt::Write for String is infallible — the Result is always Ok.
|
|
let _ = write!(s, "{b:02x}");
|
|
}
|
|
s
|
|
}
|
|
|
|
#[cfg(test)]
|
|
mod tests {
|
|
use super::*;
|
|
use hex_literal::hex;
|
|
|
|
#[test]
|
|
fn empty_input() {
|
|
assert_eq!(
|
|
hash(b""),
|
|
hex!("a7ffc6f8bf1ed76651c14756a061d662f580ff4de43b49fa82d80a4b80f8434a")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn abc() {
|
|
assert_eq!(
|
|
hash(b"abc"),
|
|
hex!("3a985da74fe225b2045c172d6bd390bd855f086e3e9d525b46bfe24511431532")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn long_message() {
|
|
assert_eq!(
|
|
hash(b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq"),
|
|
hex!("41c0dba2a9d6240849100376a8235e2c82e1b9998a999e21db32dd97496d3376")
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn fingerprint_hex_format() {
|
|
let fp = fingerprint_hex(b"test");
|
|
assert_eq!(fp.len(), 64);
|
|
assert!(
|
|
fp.chars()
|
|
.all(|c| c.is_ascii_hexdigit() && !c.is_ascii_uppercase())
|
|
);
|
|
}
|
|
|
|
#[test]
|
|
fn fingerprint_hex_deterministic() {
|
|
let a = fingerprint_hex(b"test");
|
|
let b = fingerprint_hex(b"test");
|
|
assert_eq!(a, b);
|
|
}
|
|
|
|
#[test]
|
|
fn fingerprint_hex_matches_hash() {
|
|
let data = b"hello world";
|
|
let fp = fingerprint_hex(data);
|
|
let expected = hex::encode(hash(data));
|
|
assert_eq!(fp, expected);
|
|
}
|
|
}
|