The Incredible Hunk
A hunk is a very simple data structure:
#![allow(unused)] fn main() { struct Hunk { data: [u8; 32] } }
It represents 32 bytes of data. This has multiple uses:
- It can store a hash.
- It can store a private key.
- It can store a public key.
- It can store a rainbow id.
- Probably other things.
However, there are some common operations that are often used.
Hash
The first operation we will look at is hashing an integer. A lookup table would be handy, but does take 32 * 2^32 bytes, or 128 GiB just for this one table.
#![allow(unused)] fn main() { impl Hunk { pub fn hash(value: u32) -> Hunk { Hunk { data: blake3::hash(value.to_be_bytes()).into() } } } }
Hunk Hash
One such operation is to hash the Hunk. We call this the hunk_hash,
which is simply the blake3 of the Hunk.
#![allow(unused)] fn main() { impl Hunk { pub fn hunk_hash(&self) -> Hunk { Hunk { data: blake3::hash(self.data).into() ) } } }
Combine
Another operation is to combine two hashes together, to really mix up some bits.
#![allow(unused)] fn main() { impl Hunk { pub fn combine(&self, other: Hunk) -> Hunk { (self ^ other).hunk_hash() } } }
Make It Rain
And, finally, the star of the show. This is the actual hashing algorithm used by
our rainbow tables. We call it raindrop. This will be used for building
our chains in the rainbow table, if you happen to already know what that means.
#![allow(unused)] fn main() { impl Hunk { pub fn raindrop(&self, value: u32) -> Hunk { self.combine(Hunk::hash(value)) } } }
Getting back to a (different) challenge
We will want to take a Hunk, and turn it into a 32-bit value. A sort
of "reverse-hash". But, not a reverse hash. We want to generate a new, unique
32-bit value from a hash. This will be the key to making rainbow tables work.
#![allow(unused)] fn main() { impl Hunk { pub fn evaporate(&self) -> u32 { u32::from_be_bytes([self.data[0], self.data[1], self.data[2], self.data[3]]) } } }