- From: Vijay Bharadwaj <Vijay.Bharadwaj@microsoft.com>
- Date: Fri, 14 Sep 2012 16:23:49 +0000
- To: Zooko Wilcox-OHearn <zooko@leastauthority.com>, "public-webcrypto@w3.org" <public-webcrypto@w3.org>
Zooko, You're right that people often use AES-ECB to mean "AES cipher function" and not "AES block cipher in ECB chaining mode" and that the one may be more likely to be used correctly than the other. You are also correct that sometimes, APIs may choose to encourage correct usage by making it more convenient than potentially-incorrect usage (e.g. restricting AES-ECB/base-AES/whatever-you-want-to-call-it to processing only one AES block at a time). However, for low-level APIs there is a fine line between encouraging good usage and making your API so incompatible with common practice that app writers simply chose not to use it. For example, take your point #3. AES-CTR is not "great". It has its flaws, chiefly the fact that it's malleable unless used in conjunction with an integrity check (if an attacker wants to flip a given bit of your plaintext, they just need to flip the same bit in the ciphertext). This vulnerability is fixed by using a mode such as GCM or CCM, which have integrity checks. AES-CTR is used incorrectly by many developers. However, it is also so widely used in streaming media applications that not supporting it would be silly. So we support it, instead of requiring everyone to use CCM/GCM. As you go higher in the API stack, you get an increasing ability (and arguably responsibility) to inject a point of view that saves developers from themselves. So for example JOSE has chosen not to support encryption that is not also accompanied by an integrity check. But this is a low-level API, so I suggest we err on the side of allowing things that could be used well by competent programmers while strongly recommending the safer options. I think ECB is one such capability, which can be used to good effect in the right hands. On a related note, I'm aware of a number of safe usages of ECB (ECB the chaining mode, not just the bare AES cipher) by various folks - sometimes these are part of a protocol that provides assurances in other ways, and sometimes they are performance optimizations. For a hypothetical example of the latter, consider the BitLocker example I gave below. You know that each call to any crypto API has a fixed overhead (imposed by the various API layers and the crypto engine) as well as a variable cost (based on the number of bytes to be processed and the algorithm). For a good AES implementation, the fixed overhead is often way higher than the per-byte cost. So a smart implementation that knows it is going to read a number of blocks would likely generate all the IVs with a single AES-ECB call. Why should such an implementer be penalized by our (low-level) API? -----Original Message----- From: Zooko Wilcox-OHearn [mailto:zooko@leastauthority.com] Sent: Friday, September 14, 2012 8:01 AM To: public-webcrypto@w3.org Subject: Re: Support for ECB Folks: I think there's some confusion about terminology here, but terminology is important! Misuse of ECB mode is one of the most common security flaws in code written by non-specialists, and it is typically a fatal flaw. Choosing our words carefully in our API spec and in our docs might help. Suppose you have a function that encrypts an input with a fixed AES key, like this: encryptor = window.crypto.createEncrypter(someAlgorithmSpec, aesKey1); encryptor.processData(inputData); Okay, now if inputData is exactly 16 bytes (one AES block), then it is clear what to do — encrypt the inputData with AES and emit the resulting ciphertext. If the inputData is 15 bytes then it is probably a good idea to raise an exception. (It might be tempting to automatically pad with zero bytes on the right, but then on subsequent decryption the programmer wouldn't know how many of the trailing zero bytes were provided by the initial input and how many were added by the processData() function.) But what if the inputData is 32 bytes, or some other multiple of 16 bytes? If you go ahead and encrypt the first block with aesKey1, and then encrypt the second block with aesKey1, then your function is implementing ECB mode, and "someAlgorithmSpec" should probably be something like { name: "AES-ECB" }. ECB mode is inherently insecure — once you encrypt the second block with the same AES key that you used to encrypt the first block, then it is not safe to expose both of the resulting ciphertext blocks to an enemy cryptanalyst. If instead, you handle a 32-byte input by raising an exception, then your function is implementing bare AES, and perhaps it shouldn't have an "algorithm spec" at all, because it isn't an encryption function the way AES-CTR, AES-CBC, and AES-ECB are. Instead maybe it could be a different API entirely, something like: aesFunc = window.crypto.createPrimitive({ name: "AES"}, aesKey1); aesFunc.processData(inputData); Now I have three assertions to make here, and you may disagree with some of them while accepting others, so I'll enumerate them. 1. ECB mode is a different beast from an AES function, with different security implications. 2. ECB mode is often misused by non-experts, resulting in insecure systems. 3. ECB mode is almost never used by experts, although a bare AES function often is. For example, the Bitlocker example suggested by Vijay: http://download.microsoft.com/download/0/2/3/0238acaf-d3bf-4a6d-b3d6-0a0be4bbb36e/BitLockerCipher200608.pdf The definition of IV_s in section 4.2 is as follows: IVₛ := E(Kₐₑₛ, e(s)) E() is the AES encryption function, and e() is an encoding function that maps each sector number s into a unique 16-byte value. When it says "the AES encryption function", it means the bare function that maps a key and a 16-byte plaintext block to a a 16-byte ciphertext block. (By the way, the author of Bitlocker—Niels Ferguson—is an expert indeed. Of the limited cryptography that I know, I learned much of it from Niels's writings and his personal instruction.) Okay, those are my assertions. Now I have a few recommendations: 1. Distinguish between AES-ECB and bare AES in docs and names. 2. Standardize the bare AES block encryption function. It is very useful. (If it isn't provided by the spec then programmers could kludge it by using AES-CBC mode on a single input block and fixing the IV to all zeroes. Thanks to David-Sarah Hopwood for suggesting this kludge.) 3. Include AES-CTR mode among the recommended algorithms. CTR mode is great! 4. I think I would recommend excluding AES-ECB mode entirely from the spec. I admit that there could exist cases where it could be useful. The only such case I can think of is the one Ryan suggested: efficiently implementing a non-standard CTR mode (where the counter doesn't increment normally, but increments some other bits than the lowest-order bits, or otherwise modifies the counter value in some deterministic way for each block). However, that is only a hypothetical case. I don't think I've seen a real-world case of ECB mode being used for anything correctly, and I've seen many cases of ECB mode being used incorrectly and resulting in security failures. Regards, Zooko Wilcox-O'Hearn
Received on Friday, 14 September 2012 16:24:29 UTC