1use base64::Engine;
9use sha2::{Digest, Sha512};
10
11pub fn verify(name: &str, bytes: &[u8], integrity: &str) -> Result<(), Box<dyn std::error::Error>> {
14 let expected = integrity
15 .split_whitespace()
16 .find_map(|token| token.strip_prefix("sha512-"))
17 .ok_or_else(|| format!("package `{name}`: no sha512 integrity to verify against"))?;
18 let actual = base64::engine::general_purpose::STANDARD.encode(Sha512::digest(bytes));
19 if actual != expected {
20 return Err(format!(
21 "package `{name}`: integrity mismatch — the downloaded tarball does not match \
22 the expected sha512"
23 )
24 .into());
25 }
26 Ok(())
27}
28
29#[cfg(test)]
30mod tests {
31 use super::*;
32
33 #[test]
34 fn verify_checks_sha512_and_rejects_tampering() {
35 let bytes = b"a downloaded tarball's bytes";
36 let good = format!(
37 "sha512-{}",
38 base64::engine::general_purpose::STANDARD.encode(Sha512::digest(bytes))
39 );
40 verify("p", bytes, &good).expect("matching sha512 passes");
41
42 let mut tampered = bytes.to_vec();
43 tampered[0] ^= 0xff;
44 assert!(verify("p", &tampered, &good).is_err(), "flipped byte fails");
45
46 assert!(verify("p", bytes, "sha1-deadbeef").is_err());
48 }
49}