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.