use aws_lc_rs::digest::Algorithm;
use aws_lc_rs::digest::Context;
use aws_lc_rs::digest::SHA256;
use aws_lc_rs::digest::SHA256_OUTPUT_LEN;
use aws_lc_rs::digest::SHA512;
use aws_lc_rs::digest::SHA512_OUTPUT_LEN;
pub trait HashFn<const N: usize> {
fn hash(data: &[&[u8]]) -> [u8; N];
#[must_use]
#[allow(clippy::too_many_arguments)]
fn exchange_hash(
banner_client: &[u8],
banner_server: &[u8],
kexinit_payload_client: &[u8],
kexinit_payload_server: &[u8],
public_host_key_server: &[u8],
ephemeral_public_key_client: &[u8],
ephemeral_public_key_server: &[u8],
shared_secret: &[u8],
) -> [u8; N] {
Self::hash(&[
banner_client,
banner_server,
kexinit_payload_client,
kexinit_payload_server,
public_host_key_server,
ephemeral_public_key_client,
ephemeral_public_key_server,
shared_secret,
])
}
}
pub struct Sha256;
impl HashFn<SHA256_OUTPUT_LEN> for Sha256 {
fn hash(data: &[&[u8]]) -> [u8; SHA256_OUTPUT_LEN] {
hash_inner(&SHA256, data)
}
}
pub struct Sha512;
impl HashFn<SHA512_OUTPUT_LEN> for Sha512 {
fn hash(data: &[&[u8]]) -> [u8; SHA512_OUTPUT_LEN] {
hash_inner(&SHA512, data)
}
}
fn hash_inner<const N: usize>(algorithm: &'static Algorithm, data: &[&[u8]]) -> [u8; N] {
let mut ctx = Context::new(algorithm);
for d in data {
ctx.update(d);
}
*ctx.finish()
.as_ref()
.as_array()
.expect("Output will always be hash length")
}
#[cfg(test)]
mod tests {
use rstest::rstest;
use super::*;
#[rstest]
#[case(
b"abc",
"ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
)]
#[case(
b"",
"e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
)]
#[case(
b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
"cf5b16a778af8380036ce59e7b0492370b249b11e8f07a51afac45037afee9d1"
)]
fn sha256_works(#[case] input: &[u8], #[case] hash_should: &str) {
let hash_should = hex::decode(hash_should).unwrap();
let hash_got = Sha256::hash(&[input]);
assert_eq!(hash_got.as_slice(), &hash_should);
}
#[rstest]
#[case(
b"abc",
"ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f"
)]
#[case(
b"",
"cf83e1357eefb8bdf1542850d66d8007d620e4050b5715dc83f4a921d36ce9ce47d0d13c5d85f2b0ff8318d2877eec2f63b931bd47417a81a538327af927da3e"
)]
#[case(
b"abcdefghbcdefghicdefghijdefghijkefghijklfghijklmghijklmnhijklmnoijklmnopjklmnopqklmnopqrlmnopqrsmnopqrstnopqrstu",
"8e959b75dae313da8cf4f72814fc143f8f7779c6eb9f7fa17299aeadb6889018501d289e4900f7e4331b99dec4b5433ac7d329eeb6dd26545e96e55b874be909"
)]
fn sha512_works(#[case] input: &[u8], #[case] hash_should: &str) {
let hash_should = hex::decode(hash_should).unwrap();
let hash_got = Sha512::hash(&[input]);
assert_eq!(hash_got.as_slice(), &hash_should);
}
}