Hare by Example: Hashing

Hashing, especially SHA256, is used a lot for generating short hashes for binary and text blobs.

use crypto::hmac;
use crypto::mac;
use crypto::sha256;
use encoding::hex;
use fmt;
use hash;
use hash::crc64;
use hash::fnv;

export fn main() void = {
	// CRC64, a non-cryptographic hash, is good for sending data to
	// verify checksums.
	const crc64state = crc64::crc64(&crc64::iso_table);
	defer hash::close(&crc64state);

	// Since the state embeds hash::hash we can use those functions
	// to write data to it.
	const crc64sz = hash::write(&crc64state,
		['h', 'a', 'r', 'e']: []u8);
	fmt::printf("Wrote {} bytes to the hash\n", crc64sz)!;
	fmt::println("CRC64 hash", crc64::sum64(&crc64state))!;

	// FNV is another non-cryptographic hash available which is well
	// suited for hashmap keys.
	const fnv64state = fnv::fnv64a();
	defer hash::close(&fnv64state);
	
	const fnv64sz = hash::write(&fnv64state,
		['h', 'a', 'r', 'e']: []u8);
	fmt::printf("Wrote {} bytes to the hash\n", fnv64sz)!;
	fmt::println("FNV64 hash", fnv::sum64(&fnv64state))!;

	// FNV has functions to quicky hash strings
	fmt::println(fnv::string64("hare"))!;

	// Moving over to cryptographic hashes like SHA256 we can first
	// create the state and perform the same hashing.
	const sha256state = sha256::sha256();
	defer hash::close(&sha256state);
	const sha256sz = hash::write(&sha256state,
		['h', 'a', 'r', 'e']: []u8);
	fmt::printf("Wrote {} bytes to the hash\n", sha256sz)!;

	// In order to get the results of the hash we set up an array
	// with the final size of the hash.
	let sha256sum: [sha256::SZ]u8 = [0...];

	// We use sum to compute the result and tell it to write it to
	// the array we defined above, but we need to convert it to a
	// slice with the [..] notation.
	hash::sum(&sha256state, sha256sum[..]);

	// Finally, we can encode those bytes to a hex-encoded string
	// for printing
	fmt::println("SHA256 hash", hex::encodestr(sha256sum[..])!)!;

	// As an added bonus here we can go even further and use the
	// SHA256 hash to create HMAC signatures of data.
	//
	// Let's create a SHA256 HMAC state with a secret key.
	const secret: []u8 = ['s', '3', 'c', 'r', '3', 't', '!'];

	// Sign the SHA256 state from above with the provide secret and
	// write it to the buffer we set up.
	const sha256mac = hmac::sha256(secret);
	defer mac::finish(&sha256mac);

	const sha256macsz = mac::write(&sha256mac,
		['h', 'a', 'r', 'e']: []u8);
	fmt::printf("Wrote {} bytes to the mac\n", sha256macsz)!;

	let sha256macsum: [sha256::SZ]u8 = [0...];
	mac::sum(&sha256mac, sha256macsum);

	// Use the same hex encoding from before to see the final
	// signature.
	fmt::println("SHA256 HMAC signature",
		hex::encodestr(sha256macsum[..])!)!;
};
$ hare run hashing.ha
4/4 tasks completed (100%)
Wrote 4 bytes to the hash
CRC64 hash 3660797679748775936
Wrote 4 bytes to the hash
FNV64 hash 3331174764119615823
3331174764119615823
Wrote 4 bytes to the hash
SHA256 hash e39f1cde9e9c4fac32cdd2c6772fe79c2f9fdce918679462009de1887c5ac4d7
Wrote 4 bytes to the mac
SHA256 HMAC signature d0989ad9f3abea0dab302b563a0599e5544539afd1d0545ac9e92ddae75fc6eb