Skip to main content

r2smt_patch/
digest.rs

1//! SHA-256 wrapper used by [`crate::manifest`] to record before / after
2//! integrity hashes of the binary being patched.
3
4use std::fs::File;
5use std::io::{Read, Result as IoResult};
6use std::path::Path;
7
8use sha2::{Digest, Sha256};
9
10const READ_CHUNK: usize = 64 * 1024;
11
12/// Compute the SHA-256 of a file, returning its lower-case hex digest.
13///
14/// Streams the file in 64 KiB chunks so the host memory footprint is
15/// bounded regardless of binary size.
16///
17/// # Errors
18///
19/// Propagates [`std::io::Error`] if the file cannot be opened or read.
20pub fn sha256_hex(path: impl AsRef<Path>) -> IoResult<String> {
21    let mut file = File::open(path)?;
22    let mut hasher = Sha256::new();
23    let mut buf = vec![0u8; READ_CHUNK];
24    loop {
25        let n = file.read(&mut buf)?;
26        if n == 0 {
27            break;
28        }
29        hasher.update(&buf[..n]);
30    }
31    Ok(hex::encode(hasher.finalize()))
32}
33
34#[cfg(test)]
35mod tests {
36    #![allow(clippy::unwrap_used)]
37
38    use std::io::Write;
39
40    use tempfile::NamedTempFile;
41
42    use super::*;
43
44    #[test]
45    fn sha256_empty_file_matches_known_constant() {
46        let tmp = NamedTempFile::new().unwrap();
47        let digest = sha256_hex(tmp.path()).unwrap();
48        assert_eq!(
49            digest,
50            "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
51        );
52    }
53
54    #[test]
55    fn sha256_known_vector_for_abc() {
56        let mut tmp = NamedTempFile::new().unwrap();
57        tmp.write_all(b"abc").unwrap();
58        tmp.flush().unwrap();
59        let digest = sha256_hex(tmp.path()).unwrap();
60        assert_eq!(
61            digest,
62            "ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad"
63        );
64    }
65}