This week, my teammate — Ram Valsky and I received a very unique task: To convert a crypto-related module we wrote in Node to JavaScript using WebCrypto. Since both Ram and I love anything crypto-related, our reaction was as follows:
Short Introduction to PBKDF2
PBKDF2 (Password Based Key Derivation Function), like the name implies, is a function used to derive a key from a password which can then be used as a cryptographic key by an encryption algorithm. It aims to reduce the vulnerability of encryption keys to brute force and dictionary attacks.
**When is it used? **A key derivation function is typically used to secure a password storage and to create a cryptographic key from a password or passphrase (i.e. key stretching).
**What is the advantage of PBKDF2 on top of other hash functions? **Hash function is designed to be quick while PBKDF2 is purposely slow. The slowing is achieved by adding big number of iterations and a long random salt, which makes the brute force attack on key created with PBKDF2 much harder.
Deriving a PBKDF2 key
Our first task was to derive a PBKDF2 key from a known set, comprised of a hash function, secret password, salt, iteration count and IV length.
We begin by creating the signature for our getDerivation function:
To derive a key using WebCrypto we needed to use the deriveBits function of the crypto.subtle object. There was one caveat though: deriveBits needs the secret password as a CryptoKey object while we only had a string 😕
While browsing through the WebCrypto documentation over at MDN we noticed the importKey function. This function basically returns a promise which resolves to a aCryptoKey object based on its parameter. Looking at the input parameters list we noticed something important: A password must be given as either an ArrayBuffer or a JSONWebKey. This is where TextEncoder class comes into play. TextEncoder exposes the encode function which takes a string parameter and returns a Uint8Array object, which we can then use for the importKey function!
Now that we have transformed our password to aCryptoKey we could use it to derive the hashed key. As mentioned above, we are going to use the deriveBits function to do so. Before calling the deriveBits function we should create an object that holds the configuration for it:
👉 Please note that just like the importKey function, deriveBits requires the salt to be passed as an ArrayBuffer and not a simple string.
Calling deriveBits require three parameters:
- The parameters object we created earlier
- The secret key we imported earlier
- The number of bits we wish to derive
😱 You may be asking — Why did we multiply the keyLength by 8?
If you recall in the beginning of the post I mentioned we are going to decrypt an object that was encrypted in NodeJS. NodeJS uses bytes, so the key length we used (48) was, in reality, 48 bytes. WebCrypto however, uses bits. Thus to tell WebCrypto we wish to derive 48 bytes from the deriviation, we multiple it by 8 and get the bits value of the key length (48*8=384).
Sum it all up!
WebCrypto syntax may seem strange at times, but it’s actually pretty easy to use. We found out using bits instead of strings makes things easier to work with, as no conversion is done to them in the process.
In part 2 we will describe how to use the PBKDF2 key that we derived and use it with the encryption and decryption modules of WebCrypto.
🔥 Special thanks to Ram Valsky and Barak Amar for helping me with this post!