3

I'm trying to implement iterated SHA256. This works:

use sha2::{Digest, Sha256}; // 0.8.2

fn main() {
    let preimage = [42; 80];
    let hash = Sha256::digest(&Sha256::digest(&preimage));
}

This doesn't:

use sha2::{Digest, Sha256}; // 0.8.2

fn main() {
    let preimage = [42; 80];
    let mut hash = preimage;
    for _ in 0..2 {
        hash = Sha256::digest(&hash);
    }
}

I get an error:

error[E0308]: mismatched types
 --> src/main.rs:7:16
  |
7 |         hash = Sha256::digest(&hash);
  |                ^^^^^^^^^^^^^^^^^^^^^ expected array `[u8; 80]`, found struct `generic_array::GenericArray`
  |
  = note: expected array `[u8; 80]`
            found struct `generic_array::GenericArray<u8, typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UInt<typenum::uint::UTerm, typenum::bit::B1>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>, typenum::bit::B0>>`

I would like the second style so I can easily re-iterate more than twice.

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
Snoop Catt
  • 2,169
  • 2
  • 10
  • 9

2 Answers2

5

As the compiler tells you, the types have to match to assign a new value to a variable. You can't store a bool where an i32 should be. An array and a GenericArray are different types.

Your first attempt might be to make everything a GenericArray:

use sha2::{Digest, Sha256}; // 0.8.2
use generic_array_0_12_3::GenericArray; // 0.12.3

fn main() {
    let mut hash = GenericArray::clone_from_slice(&[42u8; 80]);
    for _ in 0..2 {
        hash = Sha256::digest(&hash);
    }
}

However, this will panic because your input array is 80 elements and the returned GenericArray must have 32 elements — that's the length of the SHA-256 digest!

Instead, you need to introduce some middle steps and a smidge of indirection:

use sha2::{Digest, Sha256}; // 0.8.2

fn main() {
    let input = [42u8; 80];
    let mut intermediate;
    let mut hash = &input[..];
    
    for _ in 0..2 {
        intermediate = Sha256::digest(&hash);
        hash = &intermediate[..];
    }
}

input is an array with 80 elements, intermediate is a GenericArray<u8, 32>, and hash is a &[u8]. Both input and intermediate can be viewed as a slice, so hash counts as a common type between the two.

See also:

Shepmaster
  • 388,571
  • 95
  • 1,107
  • 1,366
  • Works indeed (I only tried your second proposal) although I must admit I don't understand why. What is the difference between this and my code? what happens under the hood there? – Snoop Catt Jul 01 '20 at 14:26
  • 1
    @Chipotle I think that it would make it more obvious if you write out the type of `hash` and what type is assigned to `hash` in each of the code examples. – Shepmaster Jul 01 '20 at 14:36
4

The probably easiest solution is to pull the first iteration out of the loop:

let preimage = [42; 80];
let mut hash = Sha256::digest(&preimage);
for _ in 1..2 {
    hash = Sha256::digest(&hash);
}

(Playground)

This makes hash a GenericArray right from the beginning, so the loop works just fine.

Sven Marnach
  • 574,206
  • 118
  • 941
  • 841
  • A benefit of this style is that you are guaranteed to hash at least once. The original question and other answer allow hashing zero times, which seems unlikely to be desired. – Shepmaster Jul 01 '20 at 18:38