2

I'm working on a simple utility to encrypt files as a learning experience. Everything seems to be working fine, but I'm wondering if I have setup the Key/IV/Salt data securely.

I have noticed that when it comes to cryptography most people seem to envision a working environment that is loaded with malware manned by a wizard remotely ready to dive through the memory of a running application/page file to get at these secure files.

Let's pretend that you're on a clean machine and you encrypt some files and turn off your computer. What I want to know is whether or not someone can take your hard drive and retrieve the contents of the files using the code I have proposed.

The attack vector I am most concerned with is ensuring that the page files/file caches are inaccessible. I also want to make sure that the Key/IV system used is not going to make a rainbow table/hash based attack feasible.

Entering the Password:

The password is entered using a text box with the passwordchar value set to true.

I'm not really concerned with the string being in memory as long as it is properly removed after the encryption. I read that using SecureString is kind of pointless at this point because if you have malware on your computer already, you could just as easily have a keylogger on there which renders everything else useless.

private static string salt = "02341235XadfaDADFexA8932F7Dz3J3X";

I salt the password using a hard coded 32 character string. (The above string is just an example.)

To get at it, it will require someone to decompile/view the .exe file itself with a hex editor (something that I know if very easy to do, but an extra step nonetheless).

I have considered making this salt editable, but I'm not sure how I could securely store it. I think it's a little ridiculous to encrypt your salt because then you will have the same issue etc, so just leaving it as a hard coded string inside the exe itself seems to make the most sense to me.

The way this works is if you decide to make your password "thepassword", it is actually saved as "thepasswordfaDADFexA8932F7Dz3J3X".

The main key here is that you always have a 32 character password, regardless of what you enter.

The Key and IV:

The Key and IV are also salted as follows. This is what I wanted to get some input on, because to be honest I'm not entirely sure what it's doing:

UnicodeEncoding UE = new UnicodeEncoding();
byte[] keysalt = UE.GetBytes("Xjafe231x42X423XadXCadfkhjeAdS");        //Another string of random characters hard coded in the exe
byte[] IVSodium = UE.GetBytes("83Xkda7l78Dkx85KdJazppoqq6SaxDs");        //Another string of random characters hard coded in the exe
byte[] key = new Rfc2898DeriveBytes(password, keysalt).GetBytes(32);   //Derive the key using the password and salt
byte[] IV = new Rfc2898DeriveBytes(password, IVSodium).GetBytes(16);    //Derive the IV using the password and salt

My main concern here is that the IV is based on the key. Again, I'm not sure if this will cause any issues and I was hoping you guys could let me know if there are issues, what they are.

Also, is this another scenario where hard coding the salt is a bad practice? Should this be stored in the encrypted file, and if so, does it really make it more secure? Should I make this editable as well?

The crypto streams are setup using the using keyword:

using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
    using (RijndaelManaged RMCrypto = new RijndaelManaged())
    {
        using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
        {
            using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
            {
                byte[] buffer = new byte[4096]; //4096 is kind of arbitrary - better idea?
                int data;
                long bytesRead = 0;
                while((data = fsIn.Read(buffer, 0, buffer.Length)) > 0)
                {
                    bytesRead += data;
                    /////////////////////////////////////////
                    // Handle Aborts and Update Progress Bar
                    /////////////////////////////////////////
                    if (!caller.isClosing)
                        caller.Invoke((MethodInvoker)delegate {
                            caller.fileProgressBar.Value = ((int)(((double)bytesRead / totalBytes) * 100)); 
                        });
                    else
                        return false; //Encryption Aborted
                    /////////////////////////////////////////
                    cs.Write(buffer, 0, data);
                    fsIn.Close();
                    cs.Close();
                    fsCrypt.Close();
                    return true;
                }
            }
        }
    }
}

Thanks for your time and please let me know if there is a better way to setup the Key/IV/Salt. I think that it is most likely secure enough as long as there is not a mathematical issue with the IV and Key containing similar characters. If so, should I use a hard coded IV as well? That seems weird.

Note that I'm not saving a hash of the password or anything like that. The password is not saved anywhere. It is just used to generate the Key and the IV.

Thanks for your time.

Edit: Here are the changes recommended for anyone looking in the future.

Note that this is not using a pepper - just a random salt, although it would be easy enough to add

byte[] salt = new byte[32];                                        //Create a 32 byte salt
rand.NextBytes(salt);                                              //Fill it with random values (use RandomNumberGenerator rand = new RNGCryptoServiceProvider(); to be safe
byte[] IV = new byte[16];                                          //Create a 16 byte IV
rand.NextBytes(IV);                                                //Fill it with random numbers
byte[] key = new Rfc2898DeriveBytes(password, salt).GetBytes(32);  //Derive our Key by mixing our password with the salt

using (FileStream fsCrypt = new FileStream(cryptFile, FileMode.Create))
{
    using (RijndaelManaged RMCrypto = new RijndaelManaged())
    {
        using (CryptoStream cs = new CryptoStream(fsCrypt, RMCrypto.CreateEncryptor(key, IV), CryptoStreamMode.Write))
        {
            using (FileStream fsIn = new FileStream(inputFile, FileMode.Open))
            {
                fsCrypt.Write(salt, 0, salt.Length); //Write our salt to the file
                fsCrypt.Write(IV, 0, IV.Length);     //Write our IV to the file
                fsIn.CopyTo(cs);                     //Encrypt and Write
            }
        }
    }
}
user1274820
  • 7,786
  • 3
  • 37
  • 74
  • 1
    Don't use fixed salts, the point of a salt is if a password is reused you can't tell that it was reused. The salt does not need to be kept secret, just append it to the front of the file that is being encrypted. – Scott Chamberlain Jan 02 '15 at 20:53
  • Okay, so just write the salt directly to the beginning of the file being encrypted? Also, is the salt even really necessary here? I'm not hashing anything. There are no stored passwords. I wanted to make sure the password can be any length and still work with the algorithm, so I know it needed to be a certain number of characters long. Short of hard coding something or appending random characters to the beginning of the file (which is basically the same thing, no?) will it effect anything? Basically, I may be explaining it incorrectly by calling it a salt. – user1274820 Jan 02 '15 at 20:54

2 Answers2

3

The salt is used for two purposes:

  1. to prevent rainbow table attacks (and it does if applied correctly);
  2. to prevent identical passwords to generate the same password hash.

To do this the salt needs to be 8 to 16 bytes (not characters) of random data, stored with the password hash. Using a static hash as you do defeats both purposes of the hash.

If you need strings, use base 64 to encode the salt and password hash. If you want you can add static data (sometimes called "pepper") to the salt before calling the password hash function. This may add some security if the program data cannot be easily read by an attacker.

You should never directly mix the salt and the password yourself; the Rfc2898DeriveBytes (which is an implementation of PBKDF2) already mixes the two. You should also never store the password, nor should you have to append any data to it. PBKDF2 can handle any size of input, so it doesn't add any functionality.

Now the IV can be taken from the PBKDF2 function (using GetBytes). There is however a problem, it's likely that this will double the initial amount of iterations of PBKDF2 function, which costs CPU time and reduces the advantage over an attacker. It's probably better to just generate a random IV and prefix it to the ciphertext.

So in the end you should store salt | IV | ciphertext, then use salt | pepper as salt and calculate your key, then encrypt/decrypt using the calculated key and IV.

Maarten Bodewes
  • 90,524
  • 13
  • 150
  • 263
  • Thanks a lot for your follow up; you answered a couple questions I had but didn't ask. So basically, I don't need to salt the IV - I can just randomly generate one and write it to the file. I can salt the password and make one call to Rfc2898 when combining the key with the salt. I will write the salt (16 bytes), the IV (16 bytes) and then read the same number of bytes into those values during decryption. I like the pepper idea, so in that case I can maybe use an 8 byte static pepper and an 8 byte random salt that will be mixed using rfc2898 – user1274820 Jan 03 '15 at 03:50
  • 1
    Yep, sounds like you got the idea. 16 bytes each for salt and pepper is a bit more conservative and it would not add a lot of processing though. – Maarten Bodewes Jan 03 '15 at 03:59
  • Doesn't the key get added to it too though? I should only make the call to rfc2898 once is what it sounds like you're saying. DeriveBytes (key,salt+pepper).GetBytes (32); right? rand.NextBytes (new byte [16]) = IV – user1274820 Jan 03 '15 at 04:13
  • 1
    The key is *derived* from the salt & pepper. Yep, the rest seems right, although personally I would use AES-128. It's not often that a password is so good that it provides over 128 bits of entropy. I've seen too many insecure 256 bit AES implementations, just going for AES-128 seems more honest to me (and it makes sure your PBKDF2 is only executed once internally, so it may even be *more* secure). Adding a MAC over the ciphertext & IV would be more constructive. – Maarten Bodewes Jan 03 '15 at 15:25
  • I'm looking for the key to be derived from a password though. You need the password to be mixed in to use it as a key, right? I should have written DeriveBytes (password,salt+pepper)(32); – user1274820 Jan 03 '15 at 20:06
  • 1
    Use `byte[] key = new Rfc2898DeriveBytes(password, saltAndPepper).GetBytes(32);`. You can create `saltAndPepper` by using [byte array concatenation](http://stackoverflow.com/q/415291/589259). – Maarten Bodewes Jan 03 '15 at 20:12
  • You can still change the accept to this answer. Normally I would not ask for this, but the other answer is about 5/8 wrong in my opinion. – Maarten Bodewes Jan 03 '15 at 22:19
  • Hey, I can't figure out how to send a message on this site and I was wondering if you could help me with one more piece of this. Everything works fine when I write the salt and the IV to the file when I encrypt it once, but for some reason, if I encrypt it twice, it will no longer decrypt the second time. I think it might have something to do with the way the Salt/IV is being written/read. Any chance you could help me out in a conversation where I can send you the code or should I just post a new question? @Maarten – user1274820 Jan 06 '15 at 20:56
  • Please use a new question. You may just need to close the cryptostream to encrypt the last part though. Don't be staunchy with cryptostreams, use two. They're just small objects. – Maarten Bodewes Jan 06 '15 at 21:16
  • Thanks for your reply. I'm really trying to optimize the write speeds, but I want to show a progress bar, so I'm torn between using a read buffer and the CopyTo function ... CopyTo is a lot faster, but it looks like it would be a pain to code a wrapper to actually get some sort of progress out of it... As for the issue, I was using cs.Write(buffer, 0, buffer.Length); It should be cs.Write(buffer, 0, data); (Data being: data = fsIn.Read(buffer, 0, buffer.Length); Thanks again for your help – user1274820 Jan 06 '15 at 21:23
0

As far as I know,

  1. IV/Salt does not need to be private. It can be stored in plain-text on the hard drive. In fact, salt must be in plain-text, otherwise you cannot generate the same output with it.
  2. It is not a good idea to use your key information for generating the IV as it may leak your key information.
  3. There is no way you can prevent attacks like rainbow. But with salt, rainbow attack becomes expensive as the talbes only work with this salt value.
  4. There is a standard of key derivation fucntion that may useful to you (http://en.wikipedia.org/wiki/PBKDF2).
monkeyo
  • 9
  • 1
  • Thanks very much for your reply. So is it okay for me to use the same salt for the IV and the Key or do you think I should generate two random salts, write them to the file, generate an IV using the salt, and then generate the final key using the salt? Also, is it okay for me to append a static string to the end of the key so that it is always 32 characters? will that negatively affect anything? Obviously I would never write part of the key to the file so I am assuming I would have to make it static? Thanks again for your reply. – user1274820 Jan 02 '15 at 21:15
  • I think it is better to use two salt values for IV and key seperately. It is ok to append a static string to the end of the key but it will not make your original key more secure than without adding that string. – monkeyo Jan 02 '15 at 22:02
  • 1
    (1) true (2) true, but currently they are already separated by PBKDF2 (3) wrong, they can be avoided with a random salt and (4) `Rfc2898DeriveBytes` **is** PBKDF2. – Maarten Bodewes Jan 03 '15 at 03:25