Battle.net SRP (by iago)

The Battle.net SRP is a variation the standard SRP protocol, with a few minor and several useless changes. There are 5 important packets I'll go over, and then I'll discuss each variable and how we come up with it.

Packets

Functions

H() is standard SHA-1
% is Modulo Division
* is Multiplication
- is Subtraction
+ is Addition

Variables

C

Your username in upper case

P

Your password in upper case

N

N IS THE "modulus". It is a large 32-byte unsigned integer, and all calculations are done modulus N. That means no value in SRP will ever go over N. Its value is:
Decimal: 112624315653284427036559548610503669920632123929604336254260115573677366691719
Hex: 0xF8FF1A8B619918032186B68CA092B5557E976C78C73212D91216F6658523C787

g

g is the "generator" variable. It is used to generate public keys, based on private keys. The value is "47" in decimal, or 0x2F in hex.

I

I is not in the original SRP. I actually invented it to optimize Battle.net SRP, since it was being calculated. The way to calculate it, if you need to, is H(g) xor H(N). What this means is that you calculate the SHA-1 values of both g and N, then xor each byte of them together. The value you come out with is:
Decimal: 1415864289515498529999010855430909456942718455404
Hex: F8018CF0A425BA8BEB8958B1AB6BF90AED970E6C

a

a is a sessional private key. It is a random integer lower than N, and is regenerated for each log in.

B

B is the server's temporary public key, derived from b (which I won't bother showing here). It is sent to the client in SID_AUTH_ACCOUNTLOGON.

s

s is the "salt" value. You choose it randomly when you create your account, and then it never changes. Every time you log into Battle.net, it's sent back to you (in SID_AUTH_ACCOUNTLOGON), and is used to help scramble the password.

x

x is a private key that is derived from
s, C, P). Note that in standard SRP, it's only derived from the s and P. The formula is:
x = H(s, H(C, ":", P));
Which means that you hash the salt along with the hash of the username, a colon, and the password. Here is a sample implementation of it:
        MessageDigest mdx = getSHA1();
        mdx.update(username.getBytes());
        mdx.update(":".getBytes());
        mdx.update(password.getBytes());
        byte []hash = mdx.digest();

        mdx = getSHA1();
        mdx.update(salt);
        mdx.update(hash);
        hash = mdx.digest();

v

v is the "Password Verifier". It is basically a private key, which is derived from
g, x, and is modulo N:
v = gx % N
A sample implementation of this might be:
		g.modPow(x, N);

A

A is a public key that exists only for a single login session. It is derived from
g, a, and of course is modulo N:
A = ga % N
A sample implementation for this might be:
	g.modPow(a, N);

u

u is used to help "scramble" the private key. In regular SRP, it's generated by the server and sent to the client along with
B. However, in Battle.net SRP, it is actually equal to the first 4 bytes of H(B). Here is a sample implementation, which is, in Java, pretty yucky:
        byte []hash = getSHA1().digest(B); // Get the SHA-1 digest of B
        byte []u = new byte[4]; // Allocate 4 bytes for U
        u[0] = hash[3];
        u[1] = hash[2];
        u[2] = hash[1];
        u[3] = hash[0];

S

S is where a lot of the magic happens. It is generated by both the client and the server, using different values and a different formula, and it ends up as the same value. On the client, it's derived from
B, v, a, u, x, and is, of course, modulo N. On the server side, it's derived from A, v, u, and B. The respective formulas are:
(client) S = ((N + B - v) % N)(a + ux) % N
(server) S = (A * (vu % N))b % N
If you really enjoy math, you can go ahead and figure out how these work out to the same value. It's actually a pretty interesting equation. Here is my Java implementation:
        
        S_base = N.add(B).subtract(v).mod(N);
        S_exp = a.add(get_u(B).multiply(x));
        S = S_base.modPow(S_exp, N);

K

K is a value that is based on
S, and is generated by both the client and the server as proof that they actually know the value of S. In standard SRP, it's just H(S); however, in Battle.net SRP, it's fairly complicated:
Here is my Java implementation:
        byte []K = new byte[40]; // Create the buffer for K
        byte []hbuf1 = new byte[16]; // Create the 2 buffers to each hold half of S
        byte []hbuf2 = new byte[16];
        
        for(int i = 0; i < hbuf1.length; i++) // Loop through S
        {
            hbuf1[i] = S[i * 2];
            hbuf2[i] = S[(i * 2) + 1];
        }

        byte []hout1 = getSHA1().digest(hbuf1); // Hash the values
        byte []hout2 = getSHA1().digest(hbuf2);

        for(int i = 0; i < hout1.length; i++)
        {
            K[i * 2] = hout1[i]; // Put them into K
            K[(i * 2) + 1] = hout2[i];
        }
Pretty stupid, if you ask me, but that's life.

M[1]

M[1] is the proof that you actually know your own password. In standard SRP, it's derived from
A, B, and K. Of course, that's too simple for Blizzard, so Battle.net SRP is derived from I, H(C), s, A, B, and K. Since I is constant, and C and s are sent across the network, I'm pretty sure that the change adds no security, but as long as it makes them feel better. The formula is actually very simple, at least:
M[1] = H(I, H(C), s, A, B, K)
My Java implementation looks like this:
        MessageDigest totalCtx = getSHA1();
        totalCtx.update(I);
        totalCtx.update(getSHA1().digest(username.getBytes()));
        totalCtx.update(s);
        totalCtx.update(A);
        totalCtx.update(B);
        totalCtx.update(K);
        
        M1 = totalCtx.digest();

Conclusion

I've went over all the packets that I use in SRP, so there should be more than enough there to get you going. Good luck!

-iago


Credits

I feel obligated to thank the following people:

All information on this page is public domain. If for any reason you want to copy/use this, feel free and have fun. All software and source directly distributed by me is public domain, and may be used in any way. Any copyrights I use (Particularely Starcraft, Brood War, Diablo, Warcraft, and Blizzard) are copyrights of their respective owners (in this case, Blizzard). Please respect all copyrights, and enjoy any public domain source code and software.