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.

  • 1
    You seem to be asking for a full code review or an algorithm/design review. I brings out multiple options for each sub-question, leading to lengthy but hard to conclude (coz each of them are good in their own way) answers. Perhaps you could focus on one essential element first (whatever is most important for you)? – Sas3 Jul 10 '17 at 3:24
  • Hi @Sas3. I suppose my most important issue is: Would it be a good idea to compute the symmetric key for encrypted messages, by concatenating a 0 byte, the password, and salt, getting the HMAC/SHA-512 of that, and then iterating the hash of the hash to take some time, and taking the first 32 bytes of that as the symmetric key? – uncaged Jul 10 '17 at 3:35

No, no, no, no, no!
The first rule of encryption club is do not roll your own algorithm. ;-)

Seriously, just don't. Even if it sounds good to you, or to everyone on this forum, don't do it! It is easy to create an algorithm which you can't break. This is rarely if ever actually secure.

My first question would be, what's wrong with using SSL/TLS? It pretty much exists to solve the problem you are facing. If we knew why it was unsuitable for your use case, we might be able to offer a better alternative.

The most obvious problems with your design are: 1) all the goodies, including the symmetric key are being stored on your server, and 2) you are re-using the same symmetric key for each message from a given user.

This is very bad.

If the server DB is ever compromised, everyone's messages, past, present, and future are now revealed. There is no forward secrecy. At a bare minimum, message symmetric keys with the server should be limited to per-session.

As always, "is your security good enough" depends entirely on your threat model. Who is going after your stuff, and what is it worth to them, and to you if it gets compromised.

This is not what I would call a production-ready approach to security.

  • Hi JesseM, thank you for your frank comments. I've got to store something on my server in order to authenticate them without them sending me their password in the clear, and the symmetric key for message encryption. Instead of rolling my own, I'll use (.NET's) Rfc2898DeriveBytes.GetBytes (since its CryptDeriveKey doesn't seem to support AES). – uncaged Jul 11 '17 at 3:16
  • What do you think of this change: Client app sends user name, server sends back that user's salt bytes and some random bytes, client generates 2 keys with .NET's Rfc2898DeriveBytes, constructed with the password and salt, with 3000 iterations, calling GetBytes(32) for the 1st key, and again for the 2nd. Client encrypts the random bytes with the 1st key using AES (CBC) with a random IV, and sends the IV and encrypted random bytes to the server for authentication. The 2nd key would be used for message encryption. – uncaged Jul 11 '17 at 3:17

Going by the revised question in your comment:

Would it be a good idea to compute the symmetric key for encrypted messages, by concatenating a 0 byte, the password, and salt, getting the HMAC/SHA-512 of that, and then iterating the hash of the hash to take some time, and taking the first 32 bytes of that as the symmetric key?

No. Don't "compute" the symmetric key, that's inefficient and theoretically insecure (in the sense that the key is "derivable" to some extent, from knowledge of input).

Keys are supposed to be random / not guessable / not-derivable.

Much simpler, efficient and more secure is to use a good random number generator - either directly from /dev/urandom or from random() like function in your favorite programming language.

  • If I use a random symmetric key, then I'm going to need to encrypt and store that in the database, and after each authentication, I'll need to send that encrypted key to the client, which would decrypt it using the user's password and salt. I'm unclear why that would be better, especially since the salt and encrypted key would be visible. – uncaged Jul 10 '17 at 4:04
  • 1
    Several issues in your logic - which was why reviewing your whole algorithm was a bigger issue. Short answer: Use SSL/TLS (PKI) so that you don't have to send symmetric encryption keys in the open. TLS handles generation and managing symmetric keys, so you don't have to do it. Also: Don't process passwords on client side. Client-side should never be trusted by the server. Salts shouldn't leave the server. – Sas3 Jul 10 '17 at 4:23
  • As I said, I don't wish to use SSL/TLS. As for salts on the client, that must happen. The client knows the user's password, and the server doesn't, and they're talking over an unsecured line during authentication. – uncaged Jul 10 '17 at 4:32
  • 1
    You know your use case best, so if you don't want to use SSL/TLS, you'd probably need to implement portions of it. i.e., that functionality is that part of the problem you're trying to solve. This format is hard to for any discussion - so for the other part, I'll just leave this here: "the server has to compare something that comes from the user's side with what was issued at registration/password-reset time". Whether that's password or something derived from it, is logically irrelevant. Keeping the password "secret" from the server has no advantage in this scenario. – Sas3 Jul 10 '17 at 5:09

Your Answer

 

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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