Secure Remote Password (SRP-6a) compatibility issues

Secure Remote Password (SRP) is a clever protocol for secure username + password based authentication where the client doesn’t reveal the actual password to the server, at any time. The password remains entirely private to the user. What the server stores and deals with during authentication are cryptographically secure one-way values that the client derived from the password. So if communication between client and server or the underlying server database is compromised, there is no technical risk of the intruder finding out the user password. The merits of SRP are discussed at greater detail on Tom Wu’s website who is the protocol designer.

Despite its virtues SRP is not particularly well known and widely used. I personally found about it when a customer requested SRP-6a authentication to be added to the Json2Ldap directory web service. My guess is that most developers find transmission of passwords over TLS/SSL and storing them in some hashed form (e.g. SHA1) good enough for most purposes. If you however run a low-budget web service and don’t want to purchase an SSL certificate SRP is the best alternative for secure authentication over plain HTTP.

There are a number of libraries implementing the latest version of Secure Remote Password, namely SRP-6a. I was particularly interested in Java and JavaScript support as these are the languages I use most. For example, a good general purpose Java crypto library that covers SRP-6a is the one produced by Bouncy Castle.

Mixing libraries, however, is very likely to lead to compatibility issues. This may occur if you want to use one library on the server side (e.g. Java) and another on the client (e.g. JavaScript).

Here is a list of the four likely compatibility issues with SRP:

Compatibility factor #1: Choice of initial crypto constants – N and g

SRP must be preset with two initial (public) parameters – a safe prime number (N) and a generator (g). These must be agreed in advance between client and server. The practical approach is to have the server manage these and make them available to clients on request. This way, the client does not need to anticipate or otherwise keep track of which parameters are used for which users or servers; it only needs to verify their validity, which can be done mathematically or by simple table lookup.

Unfortunately, many client libraries employ hard-wired values for N and g.

For servers, look for implementations which have configurable N and g, to allow a choice of optimal bitsize lengths for N, e.g. 256 bit, 512 bit and 1024 bit. Shorter primes speed up the crypto computations while longer ones provide better protection.

For clients, look for generic implementations which allow N and g to be set through the client API, typically from values passed by the server.

Compatibility factor #2: Hash function

Similar to the N and g parameters, client and server must also employ the same hash function throughout the crypto computations. Otherwise the protocol will not work. Commonly used hash functions are SHA-1, SHA-256 and SHA-512. Here again it is advisable to have the server set the preferred hash function.

For servers, look for implementations which allow selection of a digest algorithm, so you can choose the optimal one for your application case. While SHA-1 may be good enough for many situations, SHA-512 provides increased security.

For clients, look for implementations which allow the hash algorithm to be set externally, to fit the server’s choice.

Compatibility factor #3: Verifier computation

The verifier (v) is a special value derived cryptographically from the user password. The client stores it on the server when a new password is set; the server uses it then during authentication. The authenticating clients must use the same method for verifier computation as the client that uploaded the original verifier to the server, else authentication will fail.

The verifier (v) is computed from

v = g^x (mod N)

where x is a hash of the password and the password salt (s), and sometimes the username too.

Some clients base the hash on the simple concatenation of the salt (s) and password:

x = H(s || password)

while others make use of additional hashing that includes the username as well:

x = H(s || H(username) || H(password))

or something more complicated as (from SRP/TLS – see RFC 5054).

x = SHA1( s || SHA1( username || ":" || password))

When devising a web application or service, you have to make sure all clients employ the same method for x computation. This can be done by providing a default client and/or by communicating to developers the expected formula for the x hash creation.

When not to include username in the x hash?

Do not make username part of the x hash if users can have more than one login (e.g. username and email) or if the username is expected to change without an update to the password.

Compatibility factor #4: Validation messages

At the end of the authentication session client and server exchange validation messages.

M1 – а message from client to server. It completes the user authentication.

M2 – оptional message from server to client. It proves that the server has a valid verifier (v) for the user’s password.

Together M1 and M2 can be used for mutual authentication.

The problem is that SRP implementations may have different methods for computing the validation messages, for example:

M1 = H(A || B || S)
M1 = H(A || B || H(S))
M1 = H(H(N) xor H(g) || H(username) || s || A || B || H(S))

M2 = H(A || M1 || S)
M2 = H(A || M1 || H(S))

If the M computation methods differ authentication will fail. Make sure that both server and client use the same method for computing M1 and M2.

How do some of Java libraries fare in terms of the above compatibility factors?

  • Bouncy Castle: Allows for arbitrary choice of N, g and hash function. The implementation is however geared towards use of SRP for TLS (RFC 5054) and doesn’t provide methods for validating M1 and M2. Also x = SHA1( s || SHA1( username || “:” || password)). The verifier (v) generator should have been an interface, not a hard-wired class.
  • Jordan Zimmermann’s SRPforJava: Hard wired parameters and hash function, the verifier is based on non-conventional concatenation of password and salt (s) so client and server implementations must use the same library.

With JavaScript (client) implementations the situations is more or less the same – hard-wired constants and hash function.

So what to do if you encounter a compatibility issue between the server and client libraries you’re using? There’s not much choice but modify the existing code. Fortunately, SRP code is relatively simple and the required touches to match the parameters, the hash function or the V, M1 or M2 methods small. If you need a good and flexible base Java server implementation, Bouncy Castle’s is probably the best point to start.

Update 18 November 2011

Following our unsatisfactory experience with the listed Java SRP libraries we put ourselves to work and produced the ultimate Java library for Secure Remote Password authentication. It addresses all of the above issues: it is fully configurable in terms of crypto parameters ‘N’ and ‘g’ as well as hash algorithm ‘H’; it provides interfaces for custom password key ‘x’ as well as client and server evidence message ‘M1’ + ‘M2’ routines. The library was extensively tested in the context of our Json2Ldap service and incorporates a number of lessons to make it easier to integrate into a complex server environment (session management, timeouts, arbitrary attributes).

Check it out for yourself: Nimbus SRP