Beware Crypto Defaults

You don't want your secrets exposed, do you? Your conversations evesdropped? Your identity stolen?

Say hello to cryptography.

Thankfully, we have standard algorithm to encrypt and decrypt secrets (and all of the above, too). Of course, you should never write your own library (if this doesn't apply to you, you know who you are). So you reach for your trusty operating system or platform library, and code away.

Let's say you get started - you can encrypt your secrets, decrypt them, and all is good.

Now, you try to encrypt something on one platform, decrypt it on another one, and suddently things don't work anymore. Your plaintext is garbled, your hashes don't match, everything looks off. What's up with that? Aren't the standard algorithms, well, standard?

Well, yes, but even if you don't have any bugs in usage, it's easy to overlook differences in parameter defaults. Standard algorithms may have some parameters or variations, and it's not too hard to forget one that the defaults might vary across different platforms. For example, defaults for padding blocks might be different (the extra bytes used when you need your content to fit a specific block size).

This is particularly tricky because it's a bug by omission. You didn't specify a value explicity, things work because it's the default value anyway, but then if it changes elsewhere things break with no code of your own to blame.

My approach is to isolate the code for different platforms such that I can test them in isolation, and make sure I'm getting the correct values for known inputs. Don't bother trying to debug these things live if you can help it at all.

Also, remember to set every option you can on your algorithms, even if it's already the default. This lets you compare your code side-by-side with other platforms to make sure that you're got matching parameters.

A few other gotchas

Let's say you're using .NET as your platform and you want to verify network messages with the HMACSHA256 algorithm. This matches the C++ bcrypt BCRYPT_SHA256_ALGORITHM algorithm with the BCRYPT_ALG_HANDLE_HMAC_FLAG flag set.

On the C++ side, if you don't set the HMAC flag, you will get an error when you specify a secret in the call to BCryptCreateHash or one of its wrappers (likely 0xC000000D, with symbol STATUS_INVALID_PARAMETER, described as "An invalid parameter was passed to a service or function.")

Another one: if you use HMACSHA256.Create() to create your algorithm in C#, this will happily compile. You can set the Key property to your secret and away you go. Stop! The resulting algorithm is some default HMAC algorithm - you just invoked HMAC.Create via a subclass. You should either use the string overload, or better yet if you know the subtype, just use something like new HMACSHA256(secret) instead.

Enjoy your secrets!

Tags:  crypto

Home