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