qemu-devel.nongnu.org archive mirror
 help / color / mirror / Atom feed
From: Laszlo Ersek <lersek@redhat.com>
To: "Benoît Canet" <benoit.canet@irqsave.net>
Cc: Kevin Wolf <kwolf@redhat.com>,
	Paolo Bonzini <pbonzini@redhat.com>,
	qemu-devel@nongnu.org, stefanha@redhat.com,
	Stefan Hajnoczi <stefanha@gmail.com>
Subject: Re: [Qemu-devel] QCOW2 cryptography and secure key handling
Date: Wed, 31 Jul 2013 19:52:08 +0200	[thread overview]
Message-ID: <51F94EC8.5030203@redhat.com> (raw)
In-Reply-To: <20130731152714.GC4926@irqsave.net>

On 07/31/13 17:27, Benoît Canet wrote:
>> For example, current qcow2 encryption is vulnerable to a watermarking
>> attack.
>> http://en.wikipedia.org/wiki/Disk_encryption_theory#Cipher-block_chaining_.28CBC.29
> 
> void qcow2_encrypt_sectors(BDRVQcowState *s, int64_t sector_num,
>                            uint8_t *out_buf, const uint8_t *in_buf,
>                            int nb_sectors, int enc,
>                            const AES_KEY *key)
> {
>     union {
>         uint64_t ll[2];
>         uint8_t b[16];
>     } ivec;
>     int i;
> 
>     for(i = 0; i < nb_sectors; i++) {
>         ivec.ll[0] = cpu_to_le64(sector_num);
>         ivec.ll[1] = 0;
>         AES_cbc_encrypt(in_buf, out_buf, 512, key,
>                         ivec.b, enc);
>         sector_num++;
>         in_buf += 512;
>         out_buf += 512;
>     }
> }
> 
> CBC mode would imply that each sector would be crypted by combining the
> plaintext with the previous sector.
> It's does not look to be the case as the IV is reset to sector_num for each
> sector.
> It look like CTR mode.

No, it's CBC.

Each individual sector is encrypted with CBC. For each sector, the IV is
selected with a scheme that "resembles" CTR, but (a) IV selection is a
different concept from chaining mode, (b) the IV generation used above
is *not* CTR either.

http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Cipher-block_chaining_.28CBC.29

http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation#Counter_.28CTR.29

The watermarking attack Paolo linked does work here:

    If the IVs are predictable an adversary can manage to store a file
    specially created to zero out the IV, it is possible to leave a
    "watermark" on the disk, proving that the specially created file is,
    indeed, stored on disk. The exact method of constructing the
    watermark depends on the exact function providing the IVs; but the
    general recipe is to create two encrypted sectors which have
    identical first blocks b1 and b2;  these two are then related to
    each other by

      b1 xor IV1 == b2 xor IV2

    Thus, when these two blocks are encrypted, they both encrypt to the
    same thing, leaving a watermark on the disk.

We can "xor IV2" both sides of the above equation:

  b1 xor IV1 xor IV2 == b2

Suppose that you as the attacker can have a good guess at the
"sector_num" parameter of the qcow2_encrypt_sectors() function. Then you
create two (consecutive) sectors:

sector X:
- Let's denote the first 16 bytes with B1. The contents of these bytes
doesn't matter, you can fill them from /dev/urandom eg. for good
selectivity. (16 bytes because that's the block size of AES.)

- Let's denote the remaining 512-16 = 496 bytes with S. The same idea
covers the contents of S.

Then, sector Y:
- Let's denote the first 16 bytes with B2. Prepare their contents as
follows:

  B2 := B1 ^ sector_num ^ (sector_num + 1)

- The remaining 496 bytes should be filled with the same S.


When qcow2_encrypt_sectors() is called with the "sector_num" you
predicted, with "in_buf" pointing at X directly followed by Y, the
following happens:

In the first sector (X),
- The first 16 bytes are encrypted using "sector_num" as IV:

  cipher1 := encrypt(sector_num ^ B1, secret_key)

- The remaining 496 bytes (considered one big message) use "cipher1" as IV.

In the next sector (Y),
- The first 16 bytes are encrypted using "sector_num+1" as IV:

  cipher2 := encrypt((sector_num+1) ^ B2, secret_key)

However,

  (sector_num+1) ^ B2 ==
  (sector_num+1) ^ (B1 ^ sector_num ^ (sector_num + 1)) ==
  (sector_num+1) ^ (sector_num + 1) ^ B1 ^ sector_num ==
  sector_num ^ B1

Therefore,

  cipher2 == encrypt(sector_num ^ B1, secret_key) ==
  cipher1

Which means that the first 16 bytes of these two sectors will look the
same in the encrypted image.

- The remaining 496 bytes in the second block are encrypted using
cipher2==cipher1 as IV. However, since the plaintext contents ("S") is
shared between the trailing 496 bytes of both sectors, and secret_key is
the same, and the cipher2==cipher1 IVs are the same too, these encrypt
to the same data as well.

You'll end up with two identical sectors in the encrypted image.

    To protect against the watermarking attack, a cipher or a hash
    function is used to generate the IVs from the key and the current
    sector number, so that an adversary cannot predict them. In
    particular, the ESSIV approach uses a block cipher in CTR mode to
    generate the IVs.

Laszlo

  reply	other threads:[~2013-07-31 21:54 UTC|newest]

Thread overview: 21+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2013-07-23 12:47 [Qemu-devel] QCOW2 cryptography and secure key handling Benoît Canet
2013-07-23 13:00 ` Daniel P. Berrange
2013-07-23 13:21   ` Benoît Canet
2013-07-23 14:40   ` Benoît Canet
2013-07-23 15:22     ` Stefan Hajnoczi
2013-07-23 15:38       ` Kevin Wolf
2013-07-23 15:57         ` Daniel P. Berrange
2013-07-24 13:07           ` Benoît Canet
2013-07-24 15:30           ` Paolo Bonzini
2013-07-24 15:33             ` Daniel P. Berrange
2013-07-24 15:40               ` Paolo Bonzini
2013-07-24 15:46                 ` Daniel P. Berrange
2013-07-29 11:21             ` Markus Armbruster
2013-07-29 11:25               ` Kevin Wolf
2013-07-29 11:32                 ` Daniel P. Berrange
2013-07-29 16:07                   ` Benoît Canet
2013-07-31 15:33               ` Benoît Canet
2013-07-31 15:27             ` Benoît Canet
2013-07-31 17:52               ` Laszlo Ersek [this message]
2013-07-31 18:31                 ` Laszlo Ersek
2013-07-23 15:40       ` Daniel P. Berrange

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=51F94EC8.5030203@redhat.com \
    --to=lersek@redhat.com \
    --cc=benoit.canet@irqsave.net \
    --cc=kwolf@redhat.com \
    --cc=pbonzini@redhat.com \
    --cc=qemu-devel@nongnu.org \
    --cc=stefanha@gmail.com \
    --cc=stefanha@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).