7

I couldn't find the right answer in specs how to handle improper key sizes which are greater than AES_KEY_SIZE. (I found that keys which size are to short will be right filled with 0x00 values).

So for example, if a 144-bit key (instead of 128-bit key) would be used for AES-128-ECB, what should be correct behavior?

  1. returning an error ?
  2. truncating the key to 128-bit ?
  3. treat the key as password and use some kdf function?
5
  • 4
    If you design an API, throw an error if the key size isn't one of the specified sizes. Don't pad with zeros or truncate. Not doing so only leads to people abusing crypto and causes maintenance headaches when people need to match the broken API/implementation using another implementation. Commented Dec 2, 2016 at 17:50
  • 1
    Right. That's called the principle of least surprise, which is the main reason in my answer not to use #2 or #3 :) Commented Dec 2, 2016 at 18:03
  • I don't know why this question should be off topic - I asked about correct behavior handling improper key sizes (according to the answers the standard doesn't handle this cases). OpenSSL for example rejects longer keys, but pads shorter keys with zero's.
    – Georg Richter
    Commented Dec 2, 2016 at 19:21
  • 1
    I'm not sure if this is on topic or not , but wouldn't it be a better fit for Security.SE? It is basically a policy choice.
    – otus
    Commented Dec 2, 2016 at 21:14
  • ITYM OpenSSL commandline enc zero-pads too-short -K (also -iv) but rejects too-long. OpenSSL library is a C API and usually takes the defined number of bytes found at the pointer you pass, whether or not that is the number of bytes correctly set-up in your code. Commented Dec 3, 2016 at 0:58

2 Answers 2

7

I'll answer as developer and designer of some additions to Crypto-API's.

First of all, Poncho is correct that no specification of AES or mode of operation (such as the insecure ECB) will indicate what to do in this case. They will almost certainly not propose #2 or #3, if just because that would severely upset any security proof of the algorithm.

1. returning an error ?

The only correct answer in my opinion. AES only accepts 128, 192 or 256 byte keys. This is called a pre-condition. Failing to comply with a pre-condition should result in a failure. In Java I would quickly upgrade it to a RuntimeException as providing fewer bits for a symmetric key is always a programming error (you don't expect a user to directly enter a key, and if a hexadecimal key does need to be entered, you need to verify it before offering it to the crypto API).

The only place where this might not be the best answer is very low level API's where just a pointer to a key with known size is returned. But users should never call or even have access to that low level API.

Returning an error (or exception, for higher level languages) is also fail-fast, which is a good security design practice.

2. truncating the key to 128-bit ?

No that's horrible. You should not perform a cryptographic function with less security relevant data than the user provided. You're would be deciding for the user that less security is acceptable, something that obviously is not the role of an API designer.

Extending the key with zero's is equally horrible. You're telling your user that they are using AES-256 while the actual security offered is lower than that (and possibly even next to zero).

Just like (3) this will also fail the principle of least surprise.

The PHP mcrypt based functions take this route and there is a never ending stream of questions on SO trying to replicate mcrypt functionality in well behaved crypto API's.

3. treat the key as password and use some KDF function?

No. If the user wants to use a KDF, then offer them a KDF. Performing a KDF in a mode of encryption for AES violates all kind of design principles, including the principle of least surprise. An AES API should just do that: offer a cipher interface to obtain confidentiality. An API could make it easy to use a KDF, but it should not itself choose a KDF and set of parameters and make that a default (which at that time seems secure, and can never be changed afterwards).

There are many users of the CryptoJS API that mistake the encryption routine that uses a password with the one that uses a key, just because they have the same name.

Creating an encryption function that accepts a password is OK for a well defined higher level API such as one implementing OpenPGP.


There is the PKCS#5 Password Based Encryption standard. Unfortunately, because the that standard leaves too many configuration open without specifying a specific binary container format, it can still be hard to make PKCS#5 compatible ciphertext compatible between runtimes. So it can only be used as a start to define a password protected container format.


Notes:

  • Some Crypto API's like Java do not directly accept a binary key. The key must first be generated from the bytes. One reason to not directly use bytes as key is that hardware devices (HSM's, smart cards) may not even expose the bytes to the software in the first place. In that case the error may be thrown in advance, depending on the implementation.
2
  • Finally, if you want to keep your data secure then do not use mcrypt which fails on way more than this issue when it comes to least surprise. Commented Dec 2, 2016 at 18:10
  • It doesn't change your conclusions, but: for asymmetric crypto, Java requires you to process an encoding or ThisAlgSpec through a factory to get a usable ThisAlgKey, but for symmetric, SecretKeySpec 'implements' (as an interface) SecretKey and may just be passed to Cipher.init -- but the provider SPI underneath throws an exception if invalid length (for Oracle builds this includes a length okay for the algorithm but over 128 bits without the 'unlimited policy' files installed). Commented Dec 3, 2016 at 1:11
8

This is an API question, not an cryptography question. The AES standard (FIPS 197) is silent on how nonstandard key sizes are handled. The AES implementations I've seen all reject nonstandard key sizes with an error; however a higher level crypto implementation (that does more than 'just AES') might choose to provide more services (depending on the job it is designed for).

On the other hand, the question typically doesn't come up; we most often generate an AES key with a key derivation process, where we specifically ask that process "give us 128/192/256 bits of key", and that's how much key it gives us.

As for your alternatives, IMHO, silently truncating the key value is Evil; what is the right thing would depend on what you're actually doing (and who's actually generating the key, and why might it not be a standard size).

Also, ECB mode? Unless you know exactly what you're doing (and if you have to ask this question, you don't), you should avoid it.

3
  • The reason why I asked is the strange behavior of MySQL, which weakens a larger key by xoring it with redundant bytes of the key: e.g. specifying a 256-bit key containg two identical 128-bit keys: repeat(X'000102030405060708090A0B0C0D0E0F',2) becomes X'0000000000000000000000. Thanks for your answer!
    – Georg Richter
    Commented Dec 2, 2016 at 17:49
  • 1
    SQL is definitely not the best example when it comes to seeing how cryptography should be applied. You could probably say this of most of the encryption applied to databases. DB designers are often not top of the line cryptographers, they are restricted in what they can achieve in the high level languages and - finally - the cryptography that is present is often outdated (and cannot be deprecated due to compatibility issues that would ensue). Commented Dec 2, 2016 at 18:06
  • poncho: PHP mcrypt used to accept invalid key sizes (truncating or padding with zeros), and I suspect that the same is true for the underlying C-library. Both the OpenSSL command line utilities and some derived libs use a KDF while the main command argument or method name can indicate either a key or a password. So unfortunately this kind of API implementations do exist. Commented Dec 2, 2016 at 18:15

You must log in to answer this question.