There have been a lot of discussion and misconceptions about Battle.net’s authentication lately. Having done a lot of work on the Battle.net protocol, I wanted to lay some to rest.
The first thing to understand is that, at least at the time I was working on this, there were three different login methods (this is before they combined the Web logins with Battle.net logins - I can’t speak on those). The three methods are:
- CHAT protocol - deprecated a long, long time ago
- Old Login System (OLS) - used by Diablo, Warcraft 2 BNE, Starcraft, and Diablo II
- New Login System (NLS) - used by Warcraft 3, World of Warcraft, and in some fashion by newer games. Also supported - but unused - by Diablo II
I’ll describe, in detail, how each of these work. The summary is, though, that at no point does a game client ever send a plaintext password to the server. The closest is the SHA1 of the password, which is used for account creation in old games. For more information, read on!
Creating an account
You can’t create an account on CHAT.
The login was plaintext, over an insecure connection.
Old Login System
The Old Login System (or OLS) is the original login system used by Battle.net. It uses a modified version of SHA1 hashing that I’m going to call Broken-SHA1 for the remainder of this post. It’s identical to SHA1 with the exception of an inverted shift or two. There’s been a lot of discussion on whether or not this was intentional - to throw off reverse engineers - or a simple bug. Personally, I lean towards a bug, since there were other similar bugs throughout the login sequence on older games. Additionally, there is no evidence of anti-reverse engineering attempts in any of the Blizzard games I’ve worked on, even Warden and Lockdown and other measures are pure anti-hacking.
Account creation under OLS
This is quite possibly the riskiest part of the entire process. When you create an account under the OLS, you send it the username and a Broken-SHA1() hash of the uppercased password. The server validates that the username is valid, and if it is, it stores the Broken-SHA1 hash.
Interestingly, you can use any 160-bit value for the Broken-SHA1 hash, it doesn’t actually have to be Broken-SHA1. This is because Battle.net simply stores the value it receives on the server side, and validates it when you log in.
Account login under OLS
When you log in, three values are used:
- A 32-bit token generated by the server (server seed)
- A 32-bit token generated by the client (client seed)
- The password
The client seed and server seed are exchanged before the authentication is attempted. As before, the password is converted to uppercase, and hashed with Broken-SHA1. Then, the client and server seeds are appended to the password, and the Broken-SHA1 is calculated again. This gives a 160-bit value that is sent to the server. The server takes the same two values - which were exchanged - and the Broken-SHA1 of the uppercased password - which it has stored - and performs the same calculation. The server compares the hash it generated to the hash that the client sent, and if everything goes according to plan, the hashes match.
New Login System
The New Login System (NLS) first appeared - as unused code - in Diablo 2. It was then used in Warcraft 3, and a slightly modified version was used in World of Warcraft. I can’t comment on Starcraft 2 or Diablo 3, having never worked on them.
Account creation under NLS
When a user creates an account, the client sends three values, defined as s, v, and the username.
- s is the salt. It is randomly generated from a cryptographically secure random number generator.
- v is the validator. It is generated from the formula v = gx % N.
- g and N are known constants, shared between all installations of all games.
- x is defined as x = SHA1(s + SHA1(C + ":" + P))
- C and P are the username and password, respectively, converted to uppercase.
So, to summarize, the client sends the salt, and the verifier. The salt is randomly generated, and the verifier is based on the username, password, and salt.
Account login under NLS
Account logins are a little more complicated. There are four steps.
First, the client sends A and C.
- A is a 256-bit public key based on a, which is a randomly generated session key. It is derived from A using the following formula: A = ga % N. As before, g and N are constants. a is a private key generated from random bytes.
- C is the username, converted to uppercase.
Second, the server replies with s and B
- s - the salt - is the same one the client sent when it created the account.
- B is a public session key, generated by the server much the same way as A is generated by the client - B = (v + gb) % N, where b is randomly generated.
Third, the client sends M1 (otherwise known as the proof). It is calculated by a fairly complex formula: M1 = SHA1(I, SHA1(C), s, A, B, K)
- I is a constant, calculated in a fairly weird way (see my wiki for more details)
- C is, as before, the uppercase username
- s, A, and B are the salt, client public key, and server public key, as exchanged
- K is a shared key derived from S
S is where all the magic happens. It’s generated through a different formula on the server and the client - since the server knows A, B, and b, while the client knows A, a, and B. Here are the respective formulas:
- (client) S = ((N + B - v) % N)(a + ux) % N
- (server) S = (A * (vu % N))b % N
Keep in mind that v - the verifier - is what was sent when the account was created, and is based on the uppercase username, uppercase password, and the salt. The variable u is derived from B and x is derived from the username and password.
So, the client calculates its value, and sends it to the server. The server calculates its value, and compares it to what the client sent. If they match, authentication (of the client) is successful and the server returns M2.
And that brings us to the last step. Not only does the client prove to the server that it knows the password, the server also proves to the client that it knows the password. When implementing the protocol, this isn’t technically necessary, but it’s a security measure so I implemented it anyway.
M2 is simply: M2 = SHA1(A + M1 + K), where A is, as before, the client’s public key; M1 is the value from the previous step; and K is, as with M1, the shared key derived from S, which is the complex formula discussed above.
The server and client both generate M2, and the server sends M2 to the client. If the client verifies that M2 is correct, then the client is sure that it’s talking to a valid server and it continues the connection.
At this point, the protocols continue on as before. In the beta for Warcraft 3, all messages after the login completed were encrypted with RC4 using K as a shared key. Once Warcraft 3 was released, the encryption was disabled and all traffic after the login is currently plaintext.
What's the point?
The point of writing this blog is to give some insight into Battle.net’s protocols, and to demonstrate that, at no time during the normal use of any Battle.net games does Blizzard have access to your plaintext password. Yes, the passwords are converted to uppercase before hashing. That’s probably a bad idea - especially in the modern world - but it really dates back to their first Battle.net game - Diablo - from 1996. Since then, they’ve developed much better login systems, but they’ve kept the passwords case insensitive.
If you fail a certain number of logins against Battle.net, your IP address is temporarily banned. This makes it fairly difficult to bruteforce most accounts. It requires significant resources (proxies, bots, etc.) to make an attack feasible. It’s much easier to compromise accounts via phishing and malware, so that’s what attackers do.
There are lots of things I didn’t go over - upgrading OLS accounts to NLS, email password resets, password changes, etc. - but they’re all fairly logical, and use the minimum amount of information required (typically the same information as you used to create the account). You can find more information on my Battle.net SRP page and on BNetDocs Redux.
There are also Battle.net authenticator tokens, which I haven’t researched at all so I can’t talk about.