I have a plan for handling authentication and message encryption, and I'd like some others with more cryptography knowledge to let me know if my approach is sound, or how it should be improved.
Client apps will be connecting to my server via a TCP/IP socket. I'd like to avoid SSL/TLS. On the server, I'll be storing for each user (and changed whenever the user changes their password): a random salt, the hash of the UTF-8 of their password plus the salt, a symmetric key, and possibly the encryption of that symmetric key (if the symmetric key is random, instead of being derived from the password/salt).
When the users logs in, the client will send the user name, the server will send back the salt, and the client will send the hash. If the hash matches, then the server will send the encrypted symmetric key, and the client will decrypt it, and use the decrypted symmetric key for subsequent encrypted messages. If I can derive the symmetric key from the password/salt, then there'll be no need to compute and store the encrypted symmetric key, or send it to the client for decryption.
For hashing the password/salt, I had been planning on just using SHA-384, but it was pointed out to me (by zaph) that I should "iterate over an HMAC with a random salt for about a 100ms duration and save the salt with the hash". I don't want to use another library, so I'm thinking that this is what he meant:
int
i;
byte[]
passwordBytes,
saltBytes,
saltedPasswordBytes,
hash;
passwordBytes = Encoding.UTF8.GetBytes( "MyPass" );
using ( RNGCryptoServiceProvider rngProvider = new RNGCryptoServiceProvider() )
{
saltBytes = new byte[ 128 ];
rngProvider.GetBytes( saltBytes );
}
// Salt the password.
saltedPasswordBytes = new byte[ passwordBytes.Length + saltBytes.Length ];
System.Buffer.BlockCopy(
passwordBytes, 0, saltedPasswordBytes, 0, passwordBytes.Length );
System.Buffer.BlockCopy(
saltBytes, 0, saltedPasswordBytes, passwordBytes.Length, saltBytes.Length );
using ( HMACSHA512 hmac = new HMACSHA512( saltedPasswordBytes ) )
{
// Get the hash of the salted password.
hash = hmac.ComputeHash( saltedPasswordBytes );
// Iterate, getting the hash of the hash.
for ( i = 0; i < 2000; i++ )
hash = hmac.ComputeHash( hash );
}
Another big concern is how to encrypt the symmetric key. I was originally thinking of AES, and sending the encrypted symmetric key to the client, but I'm wondering if I should use something like the above, but varied slightly, such as putting a zero in the front of the salted password bytes before hashing it, and then taking the first 32 bytes of that for the key. That way, I won't even need to encrypt the symmetric key.
My next problem is message encryption. After reading through NIST publication 800-38A and blog posts, I'm so confused about cipher modes. CTR sounds good, but it isn't supported in .NET, and I don't want to use 3rd party libraries. Since my messages can get pretty long, and can have a lot of repeating portions, ECB sounds like a bad choice. Using another one, like CBC, now I need to deal with the IV. One approach I read is to use a new random IV with every message, and send it before the encrypted bytes. That sounds okay for the first message sent, but I'd like to keep messages small, so I was thinking about altering the IV for each subsequent message, using an algorithm that both sides apply. I read that the IV for subsequent messages needs to be unpredictable, so I guess I'd need to take the hash of a counter, and XOR that to the previous IV or something.
So, experts, am I going about this the right way? Is that iteration correct? Is it a good idea to use that approach (varied slightly, such as with a zero in front of the password and salt) for determining the symmetric key? What cipher mode (and IV approach, if applicable) should I use for encrypting messages?
Thank you.