2

When encrypting a file with symmetric key, most common utilities (such as gpg, mcrypt, etc) store information in the encrypted message which can be used to verify integrity of the key during decryption. E.g., if the wrong key is entered during decryption, gpg will retort with:

gpg: decryption failed: bad key

Suppose I am encrypting a file containing a string which is random. Then the key integrity check used in the standard utilities adds a vulnerability.

Is there a common utility which will not store any information or redundancy for verifying key/message integrity (and so will "decrypt" an encrypted file for any supplied key)?

  • 1
    This is not true, a wrong key will not always give you that message, it is just a quick check. What do you consider common in a utility, and why is that necessary? Are you going to sent around symmetric encrypted files to other systems and pass the key along in some secure way? I would just increase the key entropy to compensate, if you think this increases vulnerability, instead of using some less reviewed tool. – Anthon Feb 18 '15 at 8:00
  • Thanks for your comment, but is there a utility shipped with standard unix/linux installations which does not implement any such "quick check"? The answer given by ppp below mentions such a tool, but it is not a standard well-grilled tool. Does gpg, mcrypt, ccrypt, etc have any option which will encrypt without any "quick check"? – udkLpqc Feb 18 '15 at 14:40
1

As an alternative to my other answer, I'd like to offer something else. Something beautiful ... dm-crypt.

Plain dm-crypt (without LUKS) doesn't store anything about the key; on the contrary, cryptsetup is perfectly happy to open a plain device with any password and start using it. Allow me to illustrate:

[root:tmp]# fallocate -l 16M cryptfile
[root:tmp]# cryptsetup --key-file - open --type plain cryptfile cfile-open <<<"pa55w0rd"

At this point, you would want to write all your random data out to the /dev/mapper/cfile-open. It would seem prudent to me that you size the original cryptfile appropriately ahead of time so that you will use all the space; however, you could just as easily treat this as another added bit of security-through-obscurity and make a note of exactly how much data you wrote. (This would only really work if the underlying blocks were already semi-random, i.e., if you're not going to completely fill the file, you should create it with openssl rand or dd if=/dev/urandom instead of fallocate.) ... You could even use dd to start writing somewhere in the middle of the device.

For now, I'll do something simpler.

[root:tmp]# cryptsetup status cfile-open
/dev/mapper/cfile-open is active.
  type:    PLAIN
  cipher:  aes-cbc-essiv:sha256
  keysize: 256 bits
  device:  /dev/loop0
  loop:    /tmp/cryptfile
  offset:  0 sectors
  size:    32768 sectors
  mode:    read/write
[root:tmp]# b $((32768*512))
B         KiB      MiB    GiB  TiB  PiB  EiB
16777216  16384.0  16.00  .01  0    0    0
[root:tmp]# ll cryptfile
-rw-r--r--. 1 root root 16777216 Feb 21 00:28 cryptfile
[root:tmp]# openssl rand -out /dev/mapper/cfile-open $((32768*512))
[root:tmp]# hexdump -n 16 -C /dev/mapper/cfile-open
00000000  00 1d 2d 11 ac 38 c4 d3  cc 81 4f 32 de 64 01 ca  |..-..8....O2.d..|
00000010
[root:tmp]# cryptsetup close cfile-open

At this point I've filled my encrypted file with 16 MiB of random data. Watch what happens when I open it again using the wrong passphrase and then just to be clear, I'll open it again with the correct one and you'll see the original data is still intact.

[root:tmp]# cryptsetup --key-file - open --type plain cryptfile cfile-open <<<"pass"
[root:tmp]# hexdump -n 16 -C /dev/mapper/cfile-open
00000000  89 97 91 26 b5 46 87 0c  67 87 d8 4a cf 78 e6 d8  |...&.F..g..J.x..|
00000010
[root:tmp]# cryptsetup close cfile-open
[root:tmp]# cryptsetup --key-file - open --type plain cryptfile cfile-open <<<"pa55w0rd"
[root:tmp]# hexdump -n 16 -C /dev/mapper/cfile-open
00000000  00 1d 2d 11 ac 38 c4 d3  cc 81 4f 32 de 64 01 ca  |..-..8....O2.d..|
00000010
[root:tmp]# 

Enjoy.

  • @ppp: plain mode dm-crypt without luks -- this is another fine solution where we just have blocks of ciphertext without any header. In fact there is a block-by-block one-to-one correspondence between the plaintext and the ciphertext (if the plaintext size is whole number of blocks). This method has a slight extra overhead of setting up the block file, however. Also if the plaintext data size is not a multiple of the block size then there is additional overhead on the user to track the size of the plaintext (even more overhead if one uses the security through obscurity that you mention). – udkLpqc Feb 22 '15 at 5:06
  • I meant @rsaw. – udkLpqc Feb 22 '15 at 6:09
1

What you want cannot be done with GnuPG. It can however be done with OpenSSL. You would need to use one of the ciphers (preferably AES) in a stream mode like cfb or ofb. (See: http://en.wikipedia.org/wiki/Block_cipher_mode_of_operation)

Typically, when I use openssl to encrypt data, I use cbc as follows (with or without the base64-encoding (-a) ... and of course there are other ways to specify the passphrase and the input data (see man openssl):

[rsaw:~]$ openssl aes-256-cbc -e -a -pass pass:pa55w0rd <<<inputdata
U2FsdGVkX180a9K5gBgip7/lgdCGCLLlRflAjK8+YwY=
[rsaw:~]$ openssl aes-256-cbc -e -a -pass pass:pa55w0rd <<<inputdata
U2FsdGVkX1+4uSv4uCNj2J4g7441XDioDoAb6JNn2RU=
[rsaw:~]$ openssl aes-256-cbc -e -a -pass pass:pa55w0rd <<<inputdata |
> openssl aes-256-cbc -d -a -pass pass:pa55w0rd
inputdata

The fact that you get different output each time tells you your passphrase is salted, which is generally good. Now watch what happens when I use a bad key.

[rsaw:~]$ openssl aes-256-cbc -e -a -pass pass:pa55w0rd <<<inputdata |
> openssl aes-256-cbc -d -a -pass pass:pa55w0r
bad decrypt
139867807664032:error:06065064:digital envelope routines:EVP_DecryptFinal_ex:bad decrypt:evp_enc.c:596:

Long story short, this mode (cbc) is widely used to encrypt files, but it obviously doesn't meet the requirements you laid out. Let's try something different.

[rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata
U2FsdGVkX1+p64nx+/K6yCHdHw+Nmn6fSOg=
[rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata |
> openssl aes-256-cfb1 -d -a -pass pass:pa55w0rd
inputdata
[rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata |
> openssl aes-256-cfb1 -d -a -pass pass:pa55w0r
'G�疏s�v

While the above meets your requirements, I make no guarantees. I'm no encryption expert. Encryption is a big deal. It's complicated. I will say that aes*cfb* and aes*ofb also meet your requirements ... and that you should skip aes*ecb.

I'll offer 2 more interesting tidbits:

  1. I normally would never recommend using unsalted keys, but in the case of what you're doing (encrypting random data) ... you could skip the salt as it adds more clearly defined structure to the beginning of the data. E.g.:

    [rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata
    U2FsdGVkX18aMT3eK4IH+XWGhp4dOSG9UJQ=
    [rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata
    U2FsdGVkX18uIlFFMbsZib11UgjuITY9rNw=
    [rsaw:~]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd <<<inputdata
    U2FsdGVkX1+G9lAIj7RjafT9YNfO9RQXDjU=
    [rsaw:~]$ openssl aes-256-cfb1 -e -nosalt -a -pass pass:pa55w0rd <<<inputdata
    X2zi09uo6ale8A==
    
  2. When I store data (including encrypted data), integrity is always one of my top concerns. If there is any data rot, I want to know so I can throw out the whole file. Using a block cipher like aes*cbc with openssl (or AFAIK using GnuPG for that matter), any little bit-flip will be caught and cause decryption to fail. On the other hand, if you do it right, using a stream mode can allow you to recover as much data as possible -- it keeps corruption local to the part of the stream where it exists. Check it:

    [rsaw:tmp]$ openssl aes-256-cfb1 -e -a -pass pass:pa55w0rd </etc/services >services.asc
    [rsaw:tmp]$ wc -l services.asc 
    13965 services.asc
    [rsaw:tmp]$ sed '6000q;d' services.asc
    e6AAnnXAF74c8p52q7+klGC+JHfK91QOx+oFonAzKFoJt0DSNg2WQkdBaxv4YLst
    [rsaw:tmp]$ sed -i '6000s/^e/f/' services.asc 
    [rsaw:tmp]$ sed '6000q;d' services.asc
    f6AAnnXAF74c8p52q7+klGC+JHfK91QOx+oFonAzKFoJt0DSNg2WQkdBaxv4YLst
    [rsaw:tmp]$ openssl aes-256-cfb1 -d -a -pass pass:pa55w0rd <services.asc | diff - /etc/services
    5029c5029,5030
    < veronica        2770/tcp           %���#��*����@jeronica        2770/udp                # Veronica
    ---
    > veronica        2770/tcp                # Veronica
    > veronica        2770/udp                # Veronica
    

Enjoy.

PS: Don't you dare use anything other than gpg, openssl, or dm-crypt. Stick to the 3 big ones. Nothing else.

  • @rsaw: Thanks - good answer about using the cfb/ofb mode of operation in openssl. This does meet my requirement. – udkLpqc Feb 22 '15 at 4:50
  • So among the standard tools gpg, openssl, and cryptsetup/dm-crypt, it seems dm-crypt is the only one which can satisfy my requirement in CBC mode. – udkLpqc Feb 22 '15 at 6:03
-1

This tool does not store anything to verify the key.

https://madebits.github.io/#r/cpp-aes-tool.md

  • 1
    I took a look at the source code of this tool. True, this program does not store anything to verify the key, but it is unusable novice-level code. Not only it has many security holes (echoes password, uses strlen instead of strnlen, etc, etc), it has serious bugs in its PKCS#7 padding code (due to convoluted logic). So if you encrypt a file and then decrypt it, you may not even get the same file back! Example: If the 16-character C string "uvwxyz\n\n\n\n\n\n\n\n\n\n" is encrypted and decrypted back, it will drop all the trailing newline characters! – udkLpqc Feb 19 '15 at 5:11
  • 1
    Or, if you encrypt the string "hello, world!!\r\n" then decryption will only give you back "hello,", dropping the last ten characters! (This example is not even a valid PKCS7 padded block.) – udkLpqc Feb 19 '15 at 5:21
  • Uhm, no. Just say no. – rsaw Feb 21 '15 at 7:22
-1

@udkLpqc: I cannot comment above, it is up to you to use the tool or not, but what you wrote is somehow not what I see. I compiled it own my own on Ubuntu:

$echo -e "uvwxyz\n\n\n\n\n\n\n\n\n\n" | wc -l
11
$echo -e "uvwxyz\n\n\n\n\n\n\n\n\n\n" | ./aes -p "t" | ./aes -d -p "t" | wc -l
11

This looks like same number of lines to me (I had same result using files). The tool is old, the source states it does PKCS5, not PKCS7. It documents why it echoes password, and the use of strlen instead of strnlen, well, that has to be seen in context, the tool expects input comes from a user directly and it uses strlen only for file names and password. If you plan to use it in some automated context where file name and password come from untrusted sources, I agree, it may not work for you.

  • 1
    @ppp: Your echo command is wrong: echo -e <string> will output <string> followed by an additional newline character (that's why you are incorrectly getting 11 instead of 10 in your wc -l output). You will see the error if you use echo -en instead: echo -en "uvwxyz\n\n\n\n\n\n\n\n\n\n" | wc -l gives 10, while echo -en "uvwxyz\n\n\n\n\n\n\n\n\n\n" | ./aes -p "t" | ./aes -d -p "t" | wc -l gives zero. BTW, I downloaded source from your link as madebits-cpp-aes-tool-v1.0.5-0-g7edec5e.zip. – udkLpqc Feb 19 '15 at 16:45
  • 1
    @ppp: Or even simpler, any text string with a trailing newline character whose total length is a multiple of 16 (including the newline character) will reveal the bug. E.g., echo 'CRYPTOGRAPHICAL' | wc -c returns 16, while echo 'CRYPTOGRAPHICAL' | ./aes -p t | ./aes -d -p t | wc -c returns 6. More generally, any plaintext string whose last character is between 0x1 and 0xf and whose length is a multiple of 16 will be decrypted incorrectly after encryption. I did not find this by experimentation; I found this by looking at the (awful) source code logic. – udkLpqc Feb 19 '15 at 17:26

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.