use std::fs::File;
use std::io::{BufReader, Read};
use std::path::Path;
use anyhow::Context;
use mcvm_shared::pkg::PackageAddonOptionalHashes;
use sha2::{Digest, Sha256, Sha512};
pub const HASH_SHA256_RESULT_LENGTH: usize = 32;
pub const HASH_SHA512_RESULT_LENGTH: usize = 64;
pub fn get_hash_str_as_hex(hash: &str) -> anyhow::Result<Vec<u8>> {
Ok(hex::decode(hash)?)
}
#[derive(Copy, Clone)]
pub enum AddonHashType {
SHA256,
SHA512,
}
pub struct BestHashResult(String, AddonHashType);
pub fn get_best_hash(hashes: &PackageAddonOptionalHashes) -> Option<BestHashResult> {
let mut best = None;
if let Some(hash) = &hashes.sha256 {
best = Some(BestHashResult(hash.clone(), AddonHashType::SHA256));
}
if let Some(hash) = &hashes.sha512 {
best = Some(BestHashResult(hash.clone(), AddonHashType::SHA512));
}
best
}
pub fn get_reader_best_hash<R: Read>(
reader: R,
hash_type: AddonHashType,
) -> anyhow::Result<Vec<u8>> {
match hash_type {
AddonHashType::SHA256 => digest_reader::<Sha256, _>(reader),
AddonHashType::SHA512 => digest_reader::<Sha512, _>(reader),
}
}
pub fn hash_file_with_best_hash(path: &Path, result: BestHashResult) -> anyhow::Result<bool> {
let BestHashResult(expected_hash, hash_type) = result;
let file = File::open(path).context("Failed to open file for checksum")?;
let mut file = BufReader::new(file);
let actual_hash =
get_reader_best_hash(&mut file, hash_type).context("Failed to compute file hash")?;
let matches = actual_hash
== get_hash_str_as_hex(&expected_hash).context("Failed to parse provided hash")?;
Ok(matches)
}
pub fn digest_reader<D: Digest, R: Read>(mut reader: R) -> anyhow::Result<Vec<u8>> {
let mut digest = D::new();
let mut buf = [0; 1024];
loop {
let count = reader.read(&mut buf)?;
if count == 0 {
break;
}
digest.update(&buf[..count]);
}
Ok(digest.finalize().to_vec())
}
#[cfg(test)]
mod tests {
use std::io::Cursor;
use sha2::Sha512;
use super::*;
#[test]
fn test_digest_reader() {
let text = "Hello";
let cursor = Cursor::new(text);
let hash = digest_reader::<Sha512, _>(cursor).unwrap();
assert_eq!(
hash,
hex::decode("3615f80c9d293ed7402687f94b22d58e529b8cc7916f8fac7fddf7fbd5af4cf777d3d795a7a00a16bf7e7f3fb9561ee9baae480da9fe7a18769e71886b03f315")
.unwrap()
);
}
}