06. Our first crypto primitive (hashing)

February 11, 2019 ยท View on GitHub

Now that we have a running bank and a threat model with a high risk target, let's look at what we can do to mitigate this risk. As mentioned previously, in this workshop we will be using the cryptographic library called libsodium, exposed to Javascript by the sodium-native module. This suite of primitives has a strong focus on being friendly, hard to misuse and "high level". Many of the primitives we will be looking at here also have equivalents in the core crypto module, which is based on OpenSSL.

Side quest

sodium-native is a native module, which in itself can be a side quest to install. However we (@mafintosh and @emilbayes) try hard to provide prebuilt binaries for all major platforms and releases of node and electron. This means you shouldn't have to compile anything when installing the module.

npm install sodium-native

Buffers

One of the fundamental building blocks that make cryptography practical and safe (and fun!) in Javascript is the core Buffer prototype. Buffers today are the same as Uint8Arrays with some extra methods to make working with them easier. Buffers in node is the closest you get to raw memory access, but in a safe manner, so you don't have overflow bugs like you do in C/C++. Below are some of the most important Buffer methods you will be using in the workshop. Namely, allocating a specific number of bytes, checking for equality and converting the Buffer back to something that is a printable string:

// Creating buffers
Buffer.alloc(32) // Allocate empty 32 byte Buffer
Buffer.from('Hello, World!') // Allocate buffer and write 'Hello world'
Buffer.from('48656c6c6f20776f726c64', 'hex') // Decode string from `hex`
Buffer.alloc(32).fill('Hello') // Allocate 32 byte Buffer and repeat 'Hello'

buf1.equals(buf2) // Check whether buf1 and buf2 are equal byte by byte

// Converting to printable strings
buf.toString('hex') // Octets in as hexadecimal
buf.toString('base64') // Octets as ascii safe string (base64)

Being intimate with Buffers is key to working efficiently with crypto.

Another side quest

Spin up a node REPL and play around with the Buffer commands listed above. For example, try generating a 13 byte Buffer and write Hello, World! to it. Also try with a 32 byte Buffer and see the difference. Decoding different encodings such as hex and base64 is also very important for later exercises.

Hashing

Hash functions are a class of mathematical functions that transform an arbitrary sequence of data into a fixed size digest. The digest is also often called a hash or a fingerprint, because the idea is that a hash function should produce a unique result for every unique piece of data. There are many varieties of hash functions but here we will only work with cryptographically secure hash functions.

Cryptographic hash functions provide a much strong guarantee regarding the uniqueness of the fingerprint, but at the cost of requiring more computation, and hence, time. However for the particular hash function used by libsodium, context switching from V8 to C will become a bottleneck before hashing does :)

In libsodium the general purpose hash function is called BLAKE2b, and is exposed as crypto_generichash(outputBuf, inputBuf, [key]).

Problem

Write a new program, hash-example.js. Using sodium-native hash the string "Hello, World!" using the sodium.crypto_generichash primitive and print out the result as a hex string. Have a look at the constants defined in the sodium-native documentation (especially the crypto_generichash_BYTES), as these are important guidelines for working effectively with libsodium.

Testing

Running your program it should produce the output 511bc81dde11180838c562c82bb35f3223f46061ebde4a955c27b3f489cf1e03.

Continue to problem 07