use sha2::{Digest, Sha256};
pub const SHA256_DIGEST_LEN: usize = 32;
pub const SHA256_HEX_LEN: usize = SHA256_DIGEST_LEN * 2;
#[must_use]
pub fn sha256(input: &[u8]) -> [u8; SHA256_DIGEST_LEN] {
Sha256::digest(input).into()
}
#[must_use]
pub fn sha256_hex(input: &[u8]) -> String {
let bytes = sha256(input);
let mut hex = String::with_capacity(SHA256_HEX_LEN);
for byte in bytes {
hex.push(nibble_to_hex(byte >> 4));
hex.push(nibble_to_hex(byte & 0x0F));
}
hex
}
#[inline]
fn nibble_to_hex(n: u8) -> char {
match n {
0..=9 => (b'0' + n) as char,
10..=15 => (b'a' + n - 10) as char,
_ => unreachable!("nibble is 4 bits"),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn empty_input_matches_published_vector() {
assert_eq!(
sha256_hex(b""),
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
);
}
#[test]
fn abc_matches_published_vector() {
assert_eq!(
sha256_hex(b"abc"),
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
);
}
#[test]
fn fifty_six_byte_vector_matches() {
let input = b"abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq";
assert_eq!(
sha256_hex(input),
"248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1"
);
}
#[test]
fn longer_vector_crosses_block_boundary() {
let input =
b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu";
assert_eq!(
sha256_hex(input),
"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
);
}
#[test]
fn million_a_characters_matches() {
let input = vec![b'a'; 1_000_000];
assert_eq!(
sha256_hex(&input),
"cdc76e5c9914fb9281a1c7e284d73e67f1809a48a497200e046d39ccc7112cd0"
);
}
}