Implementing Crypto

I’d like to share some interesting findings I made while implementing a cross platform crypto format.

Basics

Usually you will need these features from a crypto implementation:

  1. Random data generation
  2. Checksum and HMAC calculations, like using SHA2
  3. Key derivation from a password, like using PBKDF2
  4. Encryption and decryption, like using AES

This is a pretty basic set of requirements and most crypto implementations support these. But as always the devil is in the details, because if you make a small mistake you will just get a wrong result and this result will not help you find the cause of the bug. It is wrong or it is right. Basta.

Binary Data

So cryptography is applied on raw binary data. This may already be the source of troubles:

  • Did you use the correct encoding, like UTF-8?
  • Is the implementation of your Base64 and Hex helpers correctly working?
  • Is there a good way to concatenate data or slice it?

On iOS and macOS you will usually use NSData which is fine. In the Javascript world you will soon be in trouble. If your target is node.js you can use Buffer which is quite nice, but on Web you will find yourself using Uint8Array pretty soon, which has no nice native toolset for converting between UTF-8 strings, hex and base64 presentations.

IV and Salt

Random data makes it harder for an attacker to crack encrypted data and passwords, so using initialization vectors (IV) for encryption and a salt for password generation is a good practice.

But IV support on node.js is totally broken and ends in an exception for certain circumstances. I was pretty surprised about that, but I was not able to find a workaround, which made me switch to WebCryptography.

Another trap you can fall into eventually while testing is exactly this randomness of IV and salt if you forget to preset those in your test cases, because the result obviously will always differ if different random elements are created. Sounds obvious, but will happen ;)

Choosing Algorithms

You will read a lot about which algorithms are the best, but in real life you’ll end up with taking what is there and implemented on all platforms you are targeting.

For checksums SHA256 and SHA512 are standard. For keyword derivation you will usually only find support for PBKDF2 everywhere. For symmetric encryption the standard is AES. But it comes with different flavors. I found CBC was a good compromise that is available almost everywhere. CTR is also pretty widely supported.

Package the Secret

Ok, you now have what you need and start happy encryption. It would be great, if all you need were self-contained, but usually it is not. You will need to store the salt somewhere to rebuild your key and you will also need the IV to decrypt your data.

So let’s store the IV with the encrypted data, like: IV + cipherText = package.

But we should also make sure the data cannot be manipulated, therefore we add an HMAC over the data of the previous package and add it as well: IV + cipherText + HMAC = package.

That’s nice. Now you can use the handy tools I mentioned in Binary Data to deconstruct it for decryption ;)

Warning

I’m not a crypto expert and I’ll be very happy to hear your feedback about it e.g. on Twitter @holtwick.

Published on February 13, 2018

 
Back to posts listing