How Not To Encrypt Files With Qt

in secure code, qt

A recent google search I typed for qt encrypt file led me to Qt wiki only to find how bad encryption can live forever. Let's examine the code there to learn what went wrong.

What Is Strong Encryption

The first result in my search was of course Simple encryption with SimpleCrypt. Truth must be said they did mention the code described in the page does not offer strong encryption, but they didn't say what strong means in the context, and thus we need to dive in to find exactly how weak it is.

When we talk about encryption schemes one usually refers to 4 types of attacks an encryption scheme should protect against:

  1. Known ciphertext attack: Where an attacker only has the ciphered file and knows nothing about its origin.
  2. Known plaintext attack: Where an attacker knows some of the plaintext used to create the ciphertext, and now needs to discover the rest of the text. This can happen for example due to a known header in a file or protocol.
  3. Chosen plaintext attack: Where an attacker has access to an encrypter and can encrypt any text they want.
  4. Chosen ciphertext attack: Where an attacker has access to a decryptor and can decipher any text they want except for a specific ciphered text that the attacker needs.

As we'll see, simplecrypt provides no protection against the second, third and fourth attack types.

Simplecrypt Fail

Let's head to simplecrypt code to see where it fails. In the cpp file lines 124-128 handle the actual encryption:

    while (pos < cnt) {
        ba[pos] = ba.at(pos) ^ m_keyParts.at(pos % 8) ^ lastChar;
        lastChar = ba.at(pos);
        ++pos;
    }

A normal stream cipher would use a key expansion function to expand seemingly random bits from the key, and XORed those bits with the message. That way the key function as a "seed". Knowing the key enables a partner on the receiving end to decipher the message by generating the same sequence of key bytes form the same seed.

One important feature of a key expansion function is that it should not be possible to return to the key seed from any number of generated key bits. The rationale is if the key seed could be generated, then a leaked message would reveal all future and past communication.

Now simplecrypt uses a strange kind of key expansion functions - instead of creating seemingly random bits it just cycles through the key. i.e. if the seed is "ninja" its expanded bits are:

"n", "i", "n", "j", "a", "n", "i", "n", "j", "a", ...

This can't be good. Having discovered any sequence of the key bits makes it trivial to return to the original key seed, and thus discover all other communication.

Hacking Simplecrypt

Let's say we're encrypting the following message with simplecrypt:

Hello

This is a secret message
It must never be decrypted
Burn after reading

The encrypted version in hex is:

00000000: 0302 664b a642 833b 7d1e 37cd 20e4 298e  ..fK.B.;}.7. .).
00000010: 84e1 b161 b935 e253 1a64 22a6 3ffe 3f98  ...a.5.S.d".?.?.
00000020: c1ac e87d ce2b fb0f 4831 61e5 7cbe 7fdd  ...}.+..H1a.|...
00000030: 92ec ef7d a12d ed5c 156b 31b1 7cb5 75ab  ...}.-.\.k1.|.u.
00000040: c3ba eb75 ec21 e343 0c72 71f3 2fe2 229f  ...u.!.C.rq./.".
00000050: dbb0 99                                  ...

Now let's assume we know something about the plaintext that created this message, perhaps its beginning: "Hello\n\nThis is". Turns out a simple XOR can help us return to the original key:

"H".ord ^ 0xa6 ^ 0x42 == 0xac

Which is one of the key bytes.

Repeat that in a ruby loop and we'll get:

filename = ARGV.shift
leak = "Hello\n\nThis is"
enc = IO.read(filename).bytes
key = []

(0..leak.size - 1).each do |i|
  key << (leak[i].ord ^ enc[i + 4] ^ enc[i + 5]).to_s(16)
end

puts key[5..12].reverse.join(' ')

Which takes any file encrypted by simplecrypt and prints the original key used. Later we can use that key to decipher the entire text.

Final Thoughts

You don't need to break simplecrypt to understand something's broken about it. Even without knowing anything about encryption there are some warning signs in the wiki page itself (saying it does not provide strong encryption).

I think the hard thing about encryption is to remember that unless we know what we're doing, it's generally better to rely on known algorithms and solutions rather than code found on the internet, even if that code is the first google result.

For readers who need to encrypt and decrypt files with Qt, the following two libraries work very well:

  1. Cryptopp
  2. nacl

Comments