For the the client and server to prove to each other that they have the same premaster shared key, the original author suggests this:

M = H(A | B | K) -->
                 <-- H(A | M | K)

The RFC 2945 recommends this:

M = H(H(N) XOR H(g) | H(U) | s | A | B | K) -->
                                            <-- H(A | M | K)

How many of these concatenations actually increase security? Also, how much freedom do I have to alter it without reducing security? I would imagine that it would be just as secure for to one replace the XOR with concatenation, for example.

  • A - public, random every time, determined by the client
  • B - public, random every time, determined by the server
  • U - private in my implementation, username (salted hash and salt are public)
  • s - public, associated with the user account password, determined by server
  • N and g - public, constants, determined by programmer
  • K - private, random every time, secret key between the server and client

Which definition should I use? The difference between the two is that more public values determined by the server are concatenated into the second hash. Is this actually worthwhile, since any attacker knows H(N) XOR H(g) | H(U) | s | A | B just as well as A | B?

EDIT: Uppercase U is actually the username. I was reading it as lowercase. Obviously not big on C#'s case-sensitivity.

  • 3
    I can't speak to the cryptomath, nor am I really familiar with this protocol - however, one truism you should keep in mind: Never try to "fix" crypto algorithms. The bits are the way they must be, and could not be any other way. Playing with it would probably just break it, in subtle ways you would never know about (but the attacker would, of course).
    – AviD
    Commented Jun 18, 2011 at 20:58
  • 2
    Thanks for the warning. When it comes to cryptography I know I can't trust my novice intuition - that's why I'm not changing a thing without asking the knowledgeable. But I believe in learning by doing and I don't believe in copying without thinking. If something really could not be any other way, I like to know mathematically why. The only reason I ask this particular question is that I see there is some degree of freedom. The original design and the RFC are quite different. Someone did "fix" this somewhere along the line.
    – jnm2
    Commented Jun 18, 2011 at 21:27
  • @Thomas Pornin, @this.josh: I updated my question to provide a little more information. Hopefully my question is clearer. Thanks!
    – jnm2
    Commented Jun 21, 2011 at 13:53

3 Answers 3


"How many of these concatenations actually increase security?"

By security I assume you mean confidentiality of K.

K is being protected by not sending it directly. Obviously if you send K as plaintext on an insecure channel, it will be intercepted, and any encryption using K will be compromised.

K could be sent encrypted, but that would require another key and doesn't get us anywhere.

So the protocol sends a hashed version of K. Hashes can be defeated using brute force or rainbow table attacks. If H(K) was sent, a successful attack on H(K) may reveal K to the attacker. Since there may be collisions for a hash, a successful attack on H(K) may produce a value other than K. Adding input data to a hash makes it harder to successfully attack the hash because there is a larger domain of values to attempt. The larger the input to the hash, the harder it is to attack successfully. So the answer is that every concatenation increases security.

"I would imagine that it would be just as secure for to one replace the XOR with concatenation, for example."

I do not know the purpose of the XOR. There are weaknesses in SHA1 and I suspect that the XOR of H(N) and H(g) is meant to mitigate the weakness. I think that since K is a hash of a function of B, g, x, a, u, and N that g and N are hashed and XORed to prevent some pattern from connecting parts of the input data. Theoretically you would like as much input as possible going into the hash function and XORing two values reduces the input data size compared to concatenating two values. Since N and g are publicly known the XOR is not attempting to protect those values in case of a successful attack on the hash.

"how much freedom do I have to alter it without reducing security?"

It is not possible to tell intuitively when a change on order or operation will improve or reduce security. The safe assumption is that any change will reduce security. Take note of the frequency of announcements about changes in algorithms which improve security versus the frequency of announcements about attacks breaking algorithms. Improving algorithm security is hard. Read How to break MD5 and other hash functions, and then find any mistakes the authors made. Wang's sufficient conditions of MD5 are not sufficient is at least one paper critical of the first papers findings.

  • I'm not sure this is correct. In SRP, the server stores the password verifier g^x mod n, so neither N or G can change without the user resetting his password. The protocol actually recommends never changing them, so they are basically constants. On the other hand, though A, B, U and s are publicly known as well, they are practically random. Does this change anything?
    – jnm2
    Commented Jun 20, 2011 at 11:07
  • Most importantly, can you give me any idea which definition (in my post) to go with? Also, do you suggest XORing everything?
    – jnm2
    Commented Jun 21, 2011 at 12:34
  • RFC 2945 looks to be the better recomendation. Don't XOR everything. Don't change the specified operations. The operations are constructed in very delicate ways to produce specific effects, like statistical randomness, non-repetition, spreading data across block boundries, etc.
    – this.josh
    Commented Jun 22, 2011 at 6:20
  • I wonder if part of this is just arbitrary complexity for the sake of complexity - it's simple for us to implement and hard to break.
    – jnm2
    Commented Jun 22, 2011 at 10:49

At the end of SRP, client and server are implicitly authenticated to each other. "Implicit" means: "I do not know if I talked with someone who really knows the shared secret, but I know that he knows the symmetric key I just got from the protocol only if he knows the shared secret". If you want to make sure of that, challenge the peer: have it use the symmetric key.

Now it is not as easy as it seems. In particular, an attacker could try to make a machine talk to itself. To ensure security, you shall derive several symmetric keys from the one which was produced by SRP: one key to encrypt from client to server, another distinct key from server to client. And a pair of keys for integrity checks, too. This is hard to get right, so it is highly suggested to use a protocol where all those details were painfully tuned through years of attacks and counterattacks -- namely, TLS. Did I talk about RFC 5054 ? TLS includes the Finished messages which are, basically, the challenges I am talking about. When a machine has received a Finished message from the peer, and it could be processed (proper MAC, proper value when decrypted), then the peer is duly, explicitly authenticated.

  • Actually in both the original document and the RFC 2945 the protocol quite explicitly includes an explicit authentication both ways. Though I found RFC 5054 helpful, I am not implementing TLS. What is the benefit in sending a known plaintext though AES for explicit verification instead of using the last two steps of the SRP protocol? Also, while this may be otherwise helpful, I'm still curious about what I asked in my question. (Don't get me wrong - I really appreciate the time you've been putting into these interminable SRP questions!)
    – jnm2
    Commented Jun 19, 2011 at 0:02

As Thomas Pornin noted, you should prefer the RFC(engineer's specification) over the paper(cryptographer's specification). If RFC5054 does not work for you, use 2945.

From the little that I know of cryptographic protocols, values A,B,M are critical to prevent the adversary from modifying the order of messages and their replay in the same or parallel connections with the same or other hosts.

Values H(N) and H(g) are not used in the original paper because I think they are common parameters there, while in practice these group parameters are selected by some initial protocol negotiation. Hence, attacks can be possible if the parameter suddenly change. A real-life server may even have multiple SRP verifier values for each user for each of the supported (g,N), or even multiple verifier values for each with different salts 's'. I suppose Tom Wu integrated these into the hash to assure that the protocol's security is independent of such strange settings, which is how such protocols should be designed. Maybe he did not even identify a specific attack but only did it for "good form".

Maybe, hopefully, you only forgot to fix your explanation of U, but:

You write that U is "private" in your case but that salted hash and salt are public. The last bit, that the verifier value is public, is very strange and not intended in SRP. Public verifier values only happen after compromise of the server. In this case, SRP is as secure as a standard hash-based challenge-response exchange: The verifier value is vulnerable to offline bruteforce.

If the password (P?) is very strong, this is no concern, otherwise it is a big problem. In either case, you are no better off than with standard challenge response.

U cannot be "private", it is transferred in clear in the very first step.

  • I am following the SRP protocol precisely except that I treat U as U = H(A | H(<username>)) rather than U = <username> to increase username privacy slightly. This does not change the the underlying SRP mechanism. It slightly increases the user lookup time. It also provides the opportunity to use H(<username>) which is known to the server instead of U = H(A | H(<username>)), that's all.
    – jnm2
    Commented Jun 23, 2011 at 2:33
  • Wait a minute - where did I say the verifier is public? It isn't.
    – jnm2
    Commented Jun 23, 2011 at 2:38
  • Okay, your note on "salted hash and salt are public" was a bit confusing. SRP has no salted hash but only the verifier value, which should not be public :-) From your description of U=H(..) it is not quite clear what the client will send in the very first message. Since A is random, the server cannot lookup such an U to return s. If A is not random or disclosed in a different step, you again risk the security of the protocol. The order in which values are disclosed in SRP is critical to achieve the zero-knowledge property.
    – pepe
    Commented Jun 23, 2011 at 12:21
  • A is random and does not depend on the server's response. Both the original document and the RFC mention that you can combine the first two steps because the operations do not rely on each other. It's easily provable that it does not change the protocol. So I had the client send U = H(A | H(<username>)) and A = <random> on the first step. So the server has A and it's trivial to compute U from its database to find a match.
    – jnm2
    Commented Jun 23, 2011 at 13:04
  • In other words, H(A | H(<username>)) is public, but H(<username>) and <username> are private. You can confirm a guess but not decrypt. As I said, slight privacy. Also I prefer random-looking data so a name doesn't catch someone's attention.
    – jnm2
    Commented Jun 23, 2011 at 13:08

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .