RustCrypto / hashes

Collection of cryptographic hash functions written in pure Rust
1.89k stars 257 forks source link

Finalising a sha256 has into an [u8; 32] #572

Closed WhyNotHugo closed 8 months ago

WhyNotHugo commented 8 months ago

I'm trying to generate the sha256 for some data into a [u8; 32]. I currently have some code that hashes my data into a string (e.g.: a hex representation of the hash):

pub(crate) fn hash(raw_data: impl AsRef<str>) -> String {
    let mut hasher = Sha256::new();

    // ...
    hasher.update(some_data);
    hasher.update(some_data);
    // ...

    format!("{:X}", hasher.finalize())
}

I'd like for this to return a [u8;8] instead, but I'm having a really hard time understanding what's going on beneath so many layers of abstractions. Sha256::new() returns a CoreWrapper<CtVariableCoreWrapper<Sha256VarCore, UInt<UInt<UInt<UInt<UInt<UInt<UTerm, B1>, B0>, B0>, B0>, B0>, B0>, OidSha256>>. It's not very clear to me what CoreWrapper<CtVariableCoreWrapper<Sha256VarCore, UInt<UInt<UInt<UInt<UInt<UInt<UTerm, B1>, B0>, B0>, B0>, B0>, B0>, OidSha256>> is or or how I'm supposed to reason about it beyond the basic examples in the README. The documentation for CoreWrapper points to block_buffer::BlockBuffer, but that seems to be something used when implementing a hash function, not when using one.

CoreWrapper<CtVariableCoreWrapper<Sha256VarCore, UInt<UInt<UInt<UInt<UInt<UInt<UTerm, B1>, B0>, B0>, B0>, B0>, B0>, OidSha256>> implements Digest, which has a finalize_into method, but that expects an un-exported type as input, so it doesn't seem to be what I'm looking for.

Is there any way to write the digest directly into a [u8;32]?

newpavlov commented 8 months ago

finalize for SHA-256 returns GenericArray<u8; U32> (you can not click on type in the digest docs because of a certain rustdoc bug) which effectively is [u8; 32]. It implements Into<[u8; 32]> trait, so you can write let hash: [u8; 32] = hasher.finalize().into();. Now, if you want to return only 8 bytes, you have to decide which bytes to take and which to discard. GenericArray supports all the usual slice operations, so it should be easy to do.

tarcieri commented 8 months ago

@WhyNotHugo regarding the diagnostics, here's a tracking issue for that: https://github.com/RustCrypto/traits/issues/1069

WhyNotHugo commented 8 months ago

Er, sorry, I meant [u8;32], my bad.

WhyNotHugo commented 8 months ago

let hash: [u8; 32] = hasher.finalize().into(); would generate a GenericArray<u8; U32> and then convert that into an array. Is there some way to get the hash directly without the intermediate type?

tarcieri commented 8 months ago

You should be able to do something like:

let mut buf = [0u8; 32];
digest.finalize_into(buf.as_ref());

(it might require .as_ref().into())

However note that there's no cost to the "intermediate type" and these two are really equivalent.

WhyNotHugo commented 8 months ago

Thanks, let hash: [u8; 32] = hasher.finalize().into(); works fine.

I prefer to avoid adding generic-array as a direct dependency and using GenericArray directly since I don't want internal implementation details of sha2 leaking into my own codebase.

WhyNotHugo commented 8 months ago

However note that there's no cost to the "intermediate type" and these two are really equivalent.

This is a good-to-know. Do you think it's worth including the example above in the README or in the docs?

newpavlov commented 8 months ago

We are in the process of migrating to hybrid-array, which will make extraction of array easier, so it's probably not worth to write new GenericArray-centric docs now.

WhyNotHugo commented 8 months ago

Nice, happy to see that there is also a path to move onto const-generic, that will definitely make things simpler on the API side :)