[cryptography] "Combined" cipher modes

Kevin W. Wall kevin.w.wall at gmail.com
Mon Feb 20 14:40:08 EST 2012


First of all, let me thank all who have responded for lending
your expertise. I am just picking out Ian's to respond to
because of his suggesting dividing up the IV into

    random||counter||time

but I do appreciate everyone else's comments as well.

On Mon, Feb 20, 2012 at 7:11 AM, ianG <iang at iang.org> wrote:
> On 20/02/12 18:11 PM, Kevin W. Wall wrote:
>>
>> Hi list,
>>
>> This should be a pretty simple question for this list, so please pardon
>> my ignorance. But better to ask than to continue in ignorance. :-)
>>
>> NIST refers to "combined" cipher modes as those supporting *both*
>> authenticity and confidentiality, such as GCM and CCM.
>
> My personal impression of such things is that although they can give a paper
> sense of authenticity, it is not good enough to rely on at an application
> level because of software layering issues.  In the past, I've preferred to
> use a heavyweight signed plaintext packet, then encrypted with a light-level
> HMAC or similar.  So, yes it is authenticated twice, but they are at
> different strengths / semantics / layers.

Yes, well, that is all well and good for some things, but when the primary
use of encryption nowadays is to encrypt short strings like credit card
numbers and bank account numbers, most developers are not going to
put up with the additional space & CPU overhead of both a dsig and an
HMAC. Based on your recommendation from several years ago, we had originally
used an HMAC-SHA1, but changed it to an HMAC-SHA256 after recommendations
from the initial NSA review. However, we (OWASP ESAPI) only do this for
when the user decides to use an unauthenticated cipher mode.

>> So my first question: Are there ANY "combined" cipher modes
>> for block ciphers that do not cause the ciphers to act as a key
>> stream? (That seems to be cause most of the ones I found build
>> the confidentiality piece around CTR mode.) If "yes", please name
>> a few (especially those with no patent restrictions).
>>
>> I know when you have a cipher that acts in a streaming mode,
>> that you need to be careful to use a unique IV for every encryption
>> performed with the same key.
>
>
> Well.  With basic modes like CBC, if there is no variation in the early
> parts of the packet, those blocks will encrypt the same.
>
> A good plaintext packet design can push strong variation into the first
> bytes.  e.g., the MAC can go at the beginning not the end.  It used to be
> customary to put the MAC at the end because hardware calculated it and
> streamed it at the same time, but software doesn't work that way.
>
> (There was a paper suggesting that encrypt-then-mac was better than
> mac-then-encrypt, but I vaguely recall this result only applies under some
> circumstances.  Does anyone recall how important this issue was?)

I've read a few papers and blogs on this topic. The one that sticks in
my mind was one of Nate Lawson's blogs.  I just looked it up and I
think it was:
http://rdist.root.org/2010/09/10/another-reason-to-mac-ciphertext-not-plaintext/

Based on this and some additional comments from Duong & Rizzo, we decided to
use the encrypt-then-MAC approach to ensure the integrity of the ciphertext.
(Keep in mind this was designed around the time that Duong and Rizzo
automated a padding oracle attack with their POET software.)

However, we skipped the MAC calculation if the cipher mode chosen
was an authenticated mode like CCM or GCM. The assumption (hopefully
a correct one), was that an authenticated cipher mode was sufficient.

In ESAPI (ignoring all error / exception handling, etc.), using such
a "combined" mode, distills down essentially to something like this
for the encryption:

// This is in essence what ESAPI does for combined cipher modes like CCM & GCM
// Assume a 128-bit AES key for this example.
public class Encryptor {
    private static SecureRandoma prng = new SecureRandom();
    ...
    public byte[] encrypt(SecretKey sk, byte[] plain) throws Exception {
        ...
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        byte[] ivBytes = new byte[ 128 / 8 ];   // 16 bytes (AES block size)
        prng.nextBytes(ivBytes);
        IvParameterSpec ivSpec = new IvParameterSpec(ivBytes);
        cipher.init(Cipher.ENCRYPT_MODE, sk, ivSpec);
        return cipher.doFinal( plain );
    }
    ...
}
///////////////////////////

However, as you can plainly see, there is no attempt here to prevent
the reuse of an IV for a given key. The assumption was originally,
that the random IV use was sufficient, but after reading recent comments
on entropy pools at boot-up time (when application servers typically
are started from /etc/init.d scripts), I'm not so sure.

However, above more or less is considered best practice to using Java for
symmetric encryption when you are using an authenticated mode.

The question is whether or not this is sufficient. I suppose like
most everything in information security, it depends on one's threat
model. Unfortunately, when trying to provide a generally reusable
(and simple) security API that does not require ordinary developers
to be experts in applied cryptography, one cannot go over-the-top.
If the crypto is too hard to use, has too much CPU overhead, or
makes the resulting cipher text excessively long, developers simply
will not use it. The will instead just use ECB mode and not worry
about things like cryptographic attacks that they don't know anything
about and couldn't be bothered with anyhow, even if they did.

So our goal is to ensure that the crypto is strong enough to protect
things like credit card #s, bank account info, passwords (where
cleartext passwords are needed for things like DB connections),
and producing cryptographic tokens. We are not concerned about
protecting nuclear lauch codes. Besides, it is more than likely
if the crypto is the weakest link (and it rarely is; usually
XSS and SQLi vulnerabilities abound), the weak part of the crypto
will be in the way that the dev teams manage their encryption keys.

>> So my second question is, if all the "combined" cipher modes all
>> cause a cipher to act as if it is in a streaming mode, is it okay
>> to just choose a completely RANDOM IV for each encryption?
>> Because it sure doesn't seem to be feasible to record all the IVs
>> for a given key to make sure that an IV isn't reused. If that is not
>> acceptable, then how does one ever address this?
>
>
> Random is good.  Also, using a counter that always goes up is good. Another
> possibility is to use a sufficiently fine timesource.
>
> All of these devices look good on paper but have some edge cases.  One way
> is to cram them all into the IV as one lump:
>
>   random||counter||time

So, are you advising that rather than requesting 16 bytes of
randomness and turning that into an IvParameterSpec, I should request
something less and then combine it w/ a counter and time?  In Java,
time is a long (4 bytes)? So how much for the 'random' piece and
how much to allocate for the 'counter' portion?

And if a dev team were to use 3DES, we only have an IV of 8 bytes?
How should that be divided up?

Also, if we are doing this, is it important to remember the 'counter'
across Java VM restarts or can we just reset it to 0 at that point?

> With most algorithms these days, you've got 16 bytes in the first block.

Major exception being w/ 3DES, which some developers still use for
legacy reasons.

Thanks,
-kevin
-- 
Blog: http://off-the-wall-security.blogspot.com/
"The most likely way for the world to be destroyed, most experts agree,
is by accident. That's where we come in; we're computer professionals.
We *cause* accidents."        -- Nathaniel Borenstein



More information about the cryptography mailing list