I have the following setup:
A server and a client will be connected over tcp. The server and client both have access to the preshared secret key. When the client connects to the server, the client and server both need to know the key, so they need to use the same key to authenticate or it will fail.

I came up with the following process and would like to know if there is anything obviously wrong with it that I didn't see. The actual encryption is not relevant in this case. So this is what happens when the client connects:

  1. Server generates a 16 byte salt (salt1).
  2. Server calculates hashServer1 = HASH(key + HASH(key + salt1)).
  3. Server sends salt1 to client.
  4. Clients calculates using the received salt1 hashClient1 = HASH(key + HASH(key + salt1)).
  5. Client generates 16 byte salt (salt2).
  6. Client calculates hashClient2 = HASH(key + HASH(key + salt2)).
  7. Client sends (hashClient1 + salt2) to server.
  8. Server checks if received hashClient1 == hashServer1 and if not breaks.
  9. Server calculates using received salt2 hashServer2 = HASH(key + HASH(key + salt2)).
  10. Server sends hashServer2 to client.
  11. Client checks if received hashServer2 == hashClient2 and if not breaks.

If I am not mistaken, this should be pretty secure when using a strong hash algorithm. The key is never send as clear text and due to the salt the data is different every time.

EDIT: Changed the salt used for hashing from saltX to HASH(key + saltX).

  • one downside: you MUST blacklist salts once used to prevent replay attacks. keeping a lot of salts around might hint towards weaknesses in the salt generation. you should also put your salt first in the concat. seems like you have an extra round-trip as well, why calc two secrets? you only need agree on one to authenticate.
    – dandavis
    Jun 9, 2017 at 9:11

3 Answers 3


Don’t roll your own crypto. Use TLS-PSK.



The only glaring problem I see here is that the salt is effectively useless. The point of a salt is to make cracking more difficult by adding an extra unknown factor to support the entropy of the hashing process. Salts do not necessarily need to be secrets, but we do want them to have a few properties when it comes to securing hashes:

  • never reuse salts
  • dont give salts out for free
  • dont use short salts

giving the salt each transaction will allow me to watch for patterns in their creation. of course, this depends on how true to random their generation is.

In this case if I'm an attacker sniffing this traffic, I have the salt every time. meaning I have effectively reduced the problem to cracking the PSK in every single case, and you provide two hashes and salts to me every connection, further increasing my pool.

This is still a big problem, I still have to see if I can find the value that leads to the hash you give, but in this case, once I have one hash+salt pair, I'm just cracking the PSK at that point.

I think the most important things here are that salts are usually used to protect large databases of information from reverse lookups or rainbow tables. But in your case, your entire server is subject to one password. Knowing the salt is a bigger deal here, and I would wager that makes the salt a secret even. Something worth guarding. Since if I really wanted to get in, I'd take one salt/hash pair and generate hashes until I found the PSK.

  • How would you use the salt if it remains unknown? Both parties have to know the salt or they can't produce matching hashes. Or do you refer to the salt beeing sent unencrypted? Jun 8, 2017 at 8:33
  • I was actually just editing that to make it more clear, salts dont have to be secret. But you also dont want to give clues as to how they are created. This is more a question for crypto i think, but off the top of my head maybe some form of information is transferred to a matching algorithm that is deterministic. generating a salt locally on each system based on the information transferred, a seed if you will.
    – Nalaurien
    Jun 8, 2017 at 8:37
  • Ok that helps. What if I actually use: realSalt = HASH(PSK + transferedSalt). And then only send hashedPSK = HASH(PSK + realSalt). Jun 8, 2017 at 8:39
  • This I think it a bit harder to figure out yeah. Mind you, I'm no expert in 2-way auth. But as someone who has attacked many systems, ill sit there and watch for patterns all day long. That's the kind of thing I would look for
    – Nalaurien
    Jun 8, 2017 at 8:46
  • I actually think in this case the salt should be protected. So some sort of system would need to be in place to allow a deterministic hash generation while allowing both machines to securely send "proof of PSK" I've amended my answer to reflect this. But ill be interested to see what others think.
    – Nalaurien
    Jun 8, 2017 at 8:55

This protocol is called Challenge Response Authentication, and you could simplify your process:

  1. Server sends the string timestampFromServer:HASH(PSK + timestampFromServer).

  2. Client validates the hash, trusts the server and sends the string timestampFromClient:HASH(PSK + timestampFromClient)

  3. Server validates the hash and trusts the client.

On 2, client only proceeds if the hash is valid, and that proves the server knows the key. On 3 the reverse is tested, and server knows the client also knows the key.

On 1, the server can even send timestampFromServer:HASH(PSK + timestampFromServer):TransientSessionKey. The client can now use the TransientSessionKey and the PSK to derive a symmetric key and use it on every communication with the server.

This way you don't need a salt storage, you don't need to worry about reusing salts (depending on the timestamp precision), and use less salts. You can authenticate both the client and server with less round trips too, decreasing latency, and if you use the TransientSessionKey you reduce the probability of leaking the PSK.

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.