# Difference between revisions of "SRP"

(10 intermediate revisions by the same user not shown) | |||

Line 1: | Line 1: | ||

The Battle.net SRP is a variation the standard [http://srp.stanford.edu/ndss.html SRP] protocol, with a few minor changes. There are several important packets I'll go over, and then I'll discuss each variable and how we come up with it, including a sample implementation in Java. As far as I know, me, Maddox, and TheMinistered are the first (and only) to reverse this publicly, so enjoy! | |||

The Battle.net SRP is a variation the standard | |||

==Packets== | ==Packets== | ||

===SID_AUTH_ACCOUNTCREATE (C -> S)=== | ===SID_AUTH_ACCOUNTCREATE (C -> S)=== | ||

* (byte[32]) | * (byte[32]) [[#s|s]] | ||

* (byte[32]) | * (byte[32]) [[#v|v]] | ||

* (byte[32]) Username | * (byte[32]) Username | ||

===SID_AUTH_ACCOUNTLOGON (C -> S)=== | ===SID_AUTH_ACCOUNTLOGON (C -> S)=== | ||

* (byte[32]) | * (byte[32]) [[#A|A]] | ||

* (ntstring) | * (ntstring) [[#C|C]] | ||

===SID_AUTH_ACCOUNTLOGON (C <- S)=== | ===SID_AUTH_ACCOUNTLOGON (C <- S)=== | ||

* (dword) status | * (dword) status | ||

* (byte[32]) | * (byte[32]) [[#s|s]] | ||

* (byte[32]) | * (byte[32]) [[#B|B]] | ||

===SID_AUTH_ACCOUNTLOGONPROOF (C -> S)=== | ===SID_AUTH_ACCOUNTLOGONPROOF (C -> S)=== | ||

* (byte[20]) | * (byte[20]) [[#M1|M1]] | ||

===SID_AUTH_ACCOUNTLOGONPROOF (C <- S)=== | ===SID_AUTH_ACCOUNTLOGONPROOF (C <- S)=== | ||

* (byte[20]) | * (byte[20]) [[#M2|M2]] | ||

===SID_AUTH_ACCOUNTCHANGE (C -> S)=== | ===SID_AUTH_ACCOUNTCHANGE (C -> S)=== | ||

* (byte[32]) | * (byte[32]) [[#A|A]] (for old password) | ||

===SID_AUTH_ACCOUNTCHANGE (C <- S)=== | ===SID_AUTH_ACCOUNTCHANGE (C <- S)=== | ||

* (byte[32]) | * (byte[32]) [[#s|s]] (for old password) | ||

* (byte[32]) | * (byte[32]) [[#B|B]] (for old password) | ||

===SID_AUTH_ACCOUNTCHANGEPROOF (C -> S)=== | ===SID_AUTH_ACCOUNTCHANGEPROOF (C -> S)=== | ||

* (byte[20]) | * (byte[20]) [[#M1|M1]] (for old password) | ||

* (byte[32]) | * (byte[32]) [[#s|s]] (for new password) | ||

* (byte[32]) | * (byte[32]) [[#v|v]] (for new password) | ||

===SID_AUTH_ACCOUNTCHANGEPROOF (C <- S)=== | ===SID_AUTH_ACCOUNTCHANGEPROOF (C <- S)=== | ||

* (byte[20]) | * (byte[20]) [[#M2|M2]] (for old password) | ||

===SID_AUTH_ACCOUNTUPGRADE (C -> S)=== | ===SID_AUTH_ACCOUNTUPGRADE (C -> S)=== | ||

Line 49: | Line 47: | ||

* (dword) client token | * (dword) client token | ||

* (byte[20]) BrokenSHA1 (for old password) | * (byte[20]) BrokenSHA1 (for old password) | ||

* (byte[32]) | * (byte[32]) [[#s|s]] | ||

* (byte[32]) | * (byte[32]) [[#v|v]] | ||

===SID_AUTH_ACCOUNTCHANGEPROOF (S -> C)=== | ===SID_AUTH_ACCOUNTCHANGEPROOF (S -> C)=== | ||

* (dword) status | * (dword) status | ||

* (byte[20]) | * (byte[20]) [[#M2|M2]] | ||

==Functions== | ==Functions== | ||

Line 72: | Line 70: | ||

Addition | Addition | ||

==Variables== | |||

===C=== | |||

Your username <b>in upper case</b> | Your username <b>in upper case</b> | ||

===P=== | |||

Your password <b>in upper case</b> | Your password <b>in upper case</b> | ||

===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:<br> | 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:<br> | ||

Line 86: | Line 84: | ||

===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.<br> | 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.<br> | ||

===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:<br> | 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:<br> | ||

Decimal: 1415864289515498529999010855430909456942718455404<br> | Decimal: 1415864289515498529999010855430909456942718455404<br> | ||

Line 96: | Line 94: | ||

Hex: F8018CF0A425BA8BEB8958B1AB6BF90AED970E6C<br> | Hex: F8018CF0A425BA8BEB8958B1AB6BF90AED970E6C<br> | ||

===a=== | |||

a is a sessional private key. It is a random integer lower than N, and is regenerated for each log in. | 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. | 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. | 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 | x is a private key that is derived from [[#s|s]], [[#C|C]], [[#P|P]]. Note that in standard SRP, it's only derived from the [[#s|s]] and [[#P|P]]. The formula is:<br> | ||

<b>x = H(s, H(C, ":", P));</b><br> | <b>x = H(s, H(C, ":", P));</b><br> | ||

Line 124: | Line 121: | ||

</pre> | </pre> | ||

===v=== | |||

v is the "Password Verifier". It is basically a private key, which is derived from | v is the "Password Verifier". It is basically a private key, which is derived from [[#g|g]], [[#x|x]], and is modulo [[#N|N]]:<br> | ||

<b>v = g<sup>x</sup> % N</b><br> | <b>v = g<sup>x</sup> % N</b><br> | ||

Line 133: | Line 130: | ||

</pre> | </pre> | ||

===A=== | |||

A is a public key that exists only for a single login session. It is derived from | A is a public key that exists only for a single login session. It is derived from [[#g|g]], [[#a|a]], and of course is modulo [[#N|N]]:<br> | ||

<b>A = g<sup>a</sup> % N</b><br> | <b>A = g<sup>a</sup> % N</b><br> | ||

Line 142: | Line 139: | ||

</pre> | </pre> | ||

===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 | 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|B]]. However, in Battle.net SRP, it is actually equal to the first 4 bytes of H([[#B|B]]). Here is a sample implementation, which is, in Java, pretty yucky: | ||

<pre> | <pre> | ||

Line 155: | Line 152: | ||

===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 | 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|B]], [[#v|v]], [[#a|a]], [[#u|u]], [[#x|x]], and is, of course, modulo [[#N|N]]. On the server side, it's derived from [[#A|A]], [[#v|v]], [[#u|u]], and [[#B|B]]. The respective formulas are:<br> | ||

<b>(client) S = ((N + B - v) % N)<sup>(a + ux)</sup> % N</b><br> | <b>(client) S = ((N + B - v) % N)<sup>(a + ux)</sup> % N</b><br> | ||

Line 168: | Line 165: | ||

===K=== | |||

K is a value that is based on | K is a value that is based on [[#S|S]], and is generated by both the client and the server as proof that they actually know the value of [[#S|S]]. In standard SRP, it's just H(S); however, in Battle.net SRP, it's fairly complicated:<br> | ||

<ul> | <ul> | ||

<li>2 buffers are created; one is the even bytes of S, and the other is the odd bytes of S. | <li>2 buffers are created; one is the even bytes of S, and the other is the odd bytes of S. | ||

Line 200: | Line 197: | ||

Pretty stupid, if you ask me, but that's life. | Pretty stupid, if you ask me, but that's life. | ||

===M1=== | |||

M[1] is the proof that you actually know your own password. In standard SRP, it's derived from | M1 (or M[1] is the proof that you actually know your own password. In standard SRP, it's derived from [[#A|A]], [[#B|B]], and [[#K|K]]. Of course, that's too simple for Blizzard, so Battle.net SRP is derived from [[#I|I]], H([[#C|C]]), [[#s|s]], [[#A|A]], [[#B|B]], and [[#K|K]]. Since [[#I|I]] is constant, and [[#C|C]] and [[#s|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:<br> | ||

<b> | <b>M1 = H(I, H(C), s, A, B, K)</b><br> | ||

My Java implementation looks like this: | My Java implementation looks like this: | ||

<pre> | <pre> | ||

Line 217: | Line 214: | ||

</pre> | </pre> | ||

===M2=== | |||

M[2] is the proof that the server actually knows your password. It is derived by hashing the values of | M2 (or M[2]) is the proof that the server actually knows your password. It is derived by hashing the values of [[#A|A]], [[#M1|M1]], and [[#K|K]]. When the server sends it back, you should verify it to make sure that the server actually knows your password (if it doesn't, then there's likely an attack going on). The formula is:<br> | ||

<b> | <b>M2 = H(A, M1, K)</b><br> | ||

My Java implementation looks like this: | My Java implementation looks like this: | ||

<pre> | <pre> | ||

Line 231: | Line 228: | ||

</pre> | </pre> | ||

==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! | 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! | ||

-[mailto:ron@skullsecurity.org Ron] | |||

< | ==Credits== | ||

I have to thank the following people:<br> | |||

* Maddox, SneakCharm -- Reverse engineering the code with me<br> | |||

* Arta -- For convincing me to figure out what it did, and get it on BNetDocs<br> | |||

* Cloaked - For actually reading it and pointing out mistakes :)<br> | |||

==Legal stuff== | |||

All information on this page is public domain. If for any reason you want to copy/use this, feel free | 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 | and have fun. All software and source directly distributed by me is public domain, and may be used |

## Latest revision as of 23:36, 28 July 2008

The Battle.net SRP is a variation the standard SRP protocol, with a few minor changes. There are several important packets I'll go over, and then I'll discuss each variable and how we come up with it, including a sample implementation in Java. As far as I know, me, Maddox, and TheMinistered are the first (and only) to reverse this publicly, so enjoy!

## Packets

### SID_AUTH_ACCOUNTCREATE (C -> S)

### SID_AUTH_ACCOUNTLOGON (C -> S)

### SID_AUTH_ACCOUNTLOGON (C <- S)

### SID_AUTH_ACCOUNTLOGONPROOF (C -> S)

- (byte[20]) M1

### SID_AUTH_ACCOUNTLOGONPROOF (C <- S)

- (byte[20]) M2

### SID_AUTH_ACCOUNTCHANGE (C -> S)

- (byte[32]) A (for old password)

### SID_AUTH_ACCOUNTCHANGE (C <- S)

### SID_AUTH_ACCOUNTCHANGEPROOF (C -> S)

### SID_AUTH_ACCOUNTCHANGEPROOF (C <- S)

- (byte[20]) M2 (for old password)

### SID_AUTH_ACCOUNTUPGRADE (C -> S)

- [blank]

### SID_AUTH_ACCOUNTUPGRADE (S -> C)

- (dword) status
- (dword) server token

### SID_AUTH_ACCOUNTUPGRADEPROOF (C -> S)

### SID_AUTH_ACCOUNTCHANGEPROOF (S -> C)

- (dword) status
- (byte[20]) M2

## Functions

### H()

Standard SHA-1

### %

Modulo Division

### *

Multiplication

### -

Subtraction

### +

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 = g ^{x} % 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 = g ^{a} % 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 * (v**

^{u}% N))^{b}% NIf 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:

- 2 buffers are created; one is the even bytes of S, and the other is the odd bytes of S.
- Each buffer is hashed with SHA-1.
- The even bytes of K are the even bytes of S, and the odd bytes of K are the odd bytes of S.

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.

### M1

M1 (or 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:

**M1 = 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();

### M2

M2 (or M[2]) is the proof that the server actually knows your password. It is derived by hashing the values of A, M1, and K. When the server sends it back, you should verify it to make sure that the server actually knows your password (if it doesn't, then there's likely an attack going on). The formula is:

**M2 = H(A, M1, K)**

My Java implementation looks like this:

MessageDigest shaM2 = getSHA1(); shaM2.update(A); shaM2.update(M); shaM2.update(K); M2 = shaM2.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!

-Ron

## Credits

I have to thank the following people:

- Maddox, SneakCharm -- Reverse engineering the code with me
- Arta -- For convincing me to figure out what it did, and get it on BNetDocs
- Cloaked - For actually reading it and pointing out mistakes :)

## Legal stuff

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.