10

In rust, using sha256 = "1.0.2" (or similar), how do I hash a binary file (i.e. a tar.gz archive)?

I'm trying to get the sha256 of that binary file.

This doesn't work:

fn hash() {
    let file = "file.tar.gz";
    let computed_hash = sha256::digest_file(std::path::Path::new(file)).unwrap();
    computed_hash
}

the output is:

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

3 Answers 3

17

Edit: Upgrading to sha256 = "1.0.3" should fix this

The issue is that digest_file is internally reading the file to a String, which requires that it contains valid UTF-8, which is obviously not what you want in this case.

Instead, you could read the file in as bytes and pass that into sha256::digest_bytes:

let bytes = std::fs::read(path).unwrap();  // Vec<u8>
let hash = sha256::digest_bytes(&bytes);
Sign up to request clarification or add additional context in comments.

1 Comment

This solution works. For the sake of completion the solution provided in the rust cookbook is roughly 10 times faster: rust-lang-nursery.github.io/rust-cookbook/cryptography/…
15

The sha2 crate upon which depends supports hashing Readable objects without needing to read the entire file into memory. See the example in the hashes readme.

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 Comment

Definitely better to not read the entire file into memory.
6

Here's an implementation using the sha2 crate that doesn't read the entire file into memory, and doesn't depend on the ring crate. In my case, ring isn't pure rust, which leads to cross-compilation difficulties.

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()))
}

1 Comment

Note that you don't need HEXLOWER, you can use format!("{:X}", digest)

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.