如何在 Rust 中对二进制文件进行哈希

8
在Rust中,使用类似于sha256 = "1.0.2"的方式时,如何对二进制文件(例如tar.gz归档文件)进行哈希操作?
我想要获取该二进制文件的sha256值。
以下方法无效:
fn hash() {
    let file = "file.tar.gz";
    let computed_hash = sha256::digest_file(std::path::Path::new(file)).unwrap();
    computed_hash
}

输出结果为:

...
Error { kind: InvalidData, message: "stream did not contain valid UTF-8" }

1
经过 https://github.com/baoyachi/sha256-rs/pull/2 ,您发布的代码应该可以工作。在我看来,这种行为是一个 Bug,所以我提交了 PR。 - cameron1024
3个回答

14

编辑: 升级至sha256 = "1.0.3"应该会解决这个问题

问题在于digest_file在内部将文件读取为一个String,这需要它包含有效的UTF-8,但显然这不是你想要的。

相反,你可以将文件以字节形式读取,并将其传递给sha256::digest_bytes

let bytes = std::fs::read(path).unwrap();  // Vec<u8>
let hash = sha256::digest_bytes(&bytes);

1
这个解决方案可行。为了完整起见,Rust Cookbook 中提供的解决方案大约快了 10 倍:https://rust-lang-nursery.github.io/rust-cookbook/cryptography/hashing.html - grmmgrmm

11

依赖于sha2库的支持,可以对Read对象进行哈希处理,无需将整个文件读入内存。请参阅hashes自述文件中的示例。

use sha2::{Sha256, Digest};
use std::{io, fs};

let mut hasher = Sha256::new();
let mut file = fs::File::open("file.tar.gz")?;

let bytes_written = io::copy(&mut file, &mut hasher)?;
let hash_bytes = hasher.finalize();

1
最好不要将整个文件读入内存。 - BallpointBen

6
这里使用了 sha2 代码库的一种实现方式,它不会将整个文件读入内存,并且不需要依赖于 ring 代码库。在我的情况下,ring 不是纯 rust 的,这会导致交叉编译出现困难。
use data_encoding::HEXLOWER;
use sha2::{Digest, Sha256};
use std::fs::File;
use std::io::{BufReader, Read};
use std::path::{Path, PathBuf};

/// calculates sha256 digest as lowercase hex string
fn sha256_digest(path: &PathBuf) -> Result<String> {
    let input = File::open(path)?;
    let mut reader = BufReader::new(input);

    let digest = {
        let mut hasher = Sha256::new();
        let mut buffer = [0; 1024];
        loop {
            let count = reader.read(&mut buffer)?;
            if count == 0 { break }
            hasher.update(&buffer[..count]);
        }
        hasher.finalize()
    };
    Ok(HEXLOWER.encode(digest.as_ref()))
}

2
请注意,您不需要使用HEXLOWER,您可以使用format!("{:X}", digest) - Samir Aguiar

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接