Notes by Biasha:
Structure (fields, size) and sequence of the packets is valid for 1.8.0
Algorithm is not checked for 1.8.0 yet
Original URL: http://www.shockerz.net/wowresource/forum/index.php?showtopic=42
SRP algorithm: RFC2945
Information about SRP6: http://srp.stanford.edu/doc.html
[quote=Zam,Feb 11 2005, 09:26 PM]
[Version 1.03]
Ok, this only covers the first handshacking packets. :-) I haven't had time to disect any other packets.
[size=18]If you are having problems; please verify your math with my math; there is a reason I supply the output to every function. This will allow you to see if there is typo's or any problem on either side.
Please note that some libraries may have bugs with Big Numbers; hense the reason I used openssl, as it has been used by crypto products for years. :)
[/size]
Changes:
V1.00 - Initial Release
V1.01 - Removal of the Second phase and adding where I forgot to up that B (Public) is reveresed before it is stored for use later.
V1.02 - Added Temp variables results to S calc
- Fixed the missing UPPERCASE in M1 calculation (Username)
- Fixed colors. :o
- Added some more notes.
V1.03 - Update Warning, and added a couple more intermediate numbers in the M1 Calcs.
I have been informed that only the first 4 packets are needed and then the client should ask for the realm list
This is for version 1.1.1. I do not know if anything changed srp wise in 1.2.1; but I will be confirming and diferences between 111 and 121 once I have some spare time. According to several people; srp has NOT changed between 1.1.1 and 1.2.1
Kudo's to those who have shared there knowledge. Those who have selfishly held onto most or all of their hard work so as not to advance the community; well just disappear -- you are selfish losers and the community would be better off without you...
-------------------------------------------------------------
Packets as shared by sYkez on this topic (http://www.blizzhackers.com/viewtopic.php?t=21457)
Kudo's to Amso for publically sharing his attempts at decoding srp; he actually was the one to inspire me to do this.
Notes:
Reverse means reverse the String. DO NOT USE strrev --
Treat this string as an array of numbers. 0 is a valid number; strrev will stop reversing when it incounters an NULL(0) character. My reverse routine is passed the length that it needs to reverse. In fact all of these variables are strored in a simple structure {char *data; int len;} and I pass this structure around.
uppercase means upper case all letters in string.
All values are in hex.
& means concatanate the string valuse. "HI" & "THERE" = "HITHERE"
Common Q & A:
Yes, Username and Password are Uppercased.
Yes, there are reversed string used in certain snippits of code. Don't ask my why; I don't know why Bliz deviated from the standard -- but it works. I assume the reason why is endian-ness of the machines. There server might be a main-frame...
I am using an UNMODIFIED openssl (latest version) library.
And I am using the wrapper functions in the t_math.c file that comes from the srp demo page. They just make the names of the functions much more readable. :-)
Each Calculation assumes the byte order is in the initial stored state. So N=894B...
When I reverse it in a routine N is only reversed for that Routine. When you get to the next routine, N is again equal to 894B... Some routines work with a reversed version of the calculation and some don't. Exception to this rule is B, it is reveresed before it is stored for use in later calculations.
Lets Go at it:
The Client Connects and sends the Client_Challenge packet. (Pretty self explanitory)
`
struct {
uint8 opcode; // 0x00
uint8 error; // 0x02
uint16 size; // 0x0023 (size of the rest of packet)
char gameid[4]; //gameid[4]=0; // 'WoW'
uint8 version1; // 0x00
uint8 version2; // 0x12 build version(0.12.0)
uint8 version3; // 0x00
uint16 build; // 3988
char platform[4]; // platform[4]=0; // 'x86'
char os[4]; // os[4]=0; // 'Win'
char language[4]; // language[4]=0; // 'enUS'
uint32 timezone_bias; // -419
uint32 ip; // client ip
uint8 acclen; // length of account name
char *AccountName;
} Client_Challenge;
`
The Server Generates the Following Packet:
`
struct {
uint8 opcode; // 0x00
uint8 error; //no error
uint8 ukn1;
char B[32];
uint8 g_length; //1
char g;
uint8 n_len; // 32; //N_len
char N[32];
char salt[32];
char unk3[16];
} Server_Challenge;
`
Ok, N is is hard coded in hex as:
`894B645E89E1535BBDAD5B8B290650530801B18EBFBF5E8FAB3C82872A3E9BB7`
G Length = 1
G = 7
K = 3
salt = randomly generated 32 characters.
Server also internally generates a random generated "b" 20 characters
We are going to assume some values to make it easier to duplicate:
`Username = "TEST"
Password = "TEST"
salt = 33f140d46cb66e631fdbbbc9f029ad8898e05ee533876118185e56dde843674f
b = 8692E3A6BA48B5B1004CEF76825127B7EB7D1AEF
`
Ok, Now for the calculations:
To Generate x:
`Temp = "Username" (Supplied in the client challenge) & ":" & "Password" (Supplied to whatever user registration script).
TempHash = SHA1(uppercase(Temp))
x = sha1(Salt & TempHash)
Your x should equal: 1b70dd2ad03c1ed140223f8f8741c00ec3a4ce73
`
To Generate v (Verifier):
`N = Reverse(N) // 32 Characters
x = Reverse(x) // 20 Characters
v = g ^ x mod (N)
// Exact call I Use: BN_mod_exp_mont(v, g, x, N, ctx, NULL);
Your v Should equal: 996ec7b349d5827043ecd0e6efba3daea5590f944d0184fee1b83ff4f59ecfa8
`
To Generate B (public B, not private B):
`/* B = kv + g^b mod n */
b = Reverse(b)
N = Reverse(N)
Temp1 = K * v
Temp2 = g ^ b mod (N)
Temp3 = Temp1 + Temp2
B = Temp3 mod (N)
B = Reverse(B) // Store this one PERMANATLY Reversed!!!
// code I use is:
//------------------------------
BigIntegerMul(B, k, v, NULL);
BigIntegerModExp(k, g, b, N, NULL, NULL);
BigIntegerAdd(k, k, B);
BigIntegerMod(B, k, N, NULL);
//------------------------------
Your B should equal: 645d1f78973073701e12bc98aa38ea99b4bc435c32e8447c73ab077ae4d75964
`
Stuff B, g, N, and Salt in the packet and send it.
The client will respond with the following:
`struct {
uint8 opcode; // 0x01
uint8 A[32];
uint8 M1[20];
uint8 crc_hash[20];
uint8 number_of_keys;
} Client_Proof;
Were going to assume A came over as follows:
232fb1b88529643d95b8dce78f2750c75b2df37acba873eb31073839eda0738d
And M1 came over as:
eeb4adca80f4de02f9a9fe8d000d682e3ddfad6f
`
A is generated by the client by its secret (a). You will never know what the secret (a) is, so you don't need to worry about how to calc A. (See SRP demo page if you really need to generate an A because you are working on a client program.
M1 however is quite important, this will be used to detect if the user is authenticly loging on.
crc_hash, appears to be unimportant in 1.1.1 (Anybody have any info?)
number_of_keys = 0
Server will respond with:
`struct {
uint8 opcode; // 0x01
uint8 error; // 0;//error
uint8 M2[20]; //M2
uint8 ukn1; // 0;
uint8 ukn2; // 0;
uint8 ukn3; // 0;
uint8 ukn4; // 0;
} Server_Proof;
`
To Generate M1 & M2, we need to generate some other values:
Generate U:
`U = SHA1(A & B)
Your U should equal: 2f4969ac9f387fd672236f9491a516777cdde1c1
`
Generate S: // S=(Av^u) ^ b (mod N)
`U = Reverse(U)
N = Reverse(N)
A = Reverse(A)
b = Reverse(b) // Private b
Temp1 = v ^ U mod (n) // Result: 84DC9E9F96AE7A2D9B05CD264A75EB5671EA02FD067B243848543E2713C8D91
Temp2 = Temp1 * A // Result: 496985FE28C394E7B8FB881105E4CD02F7DF4F378736332BC2F4733ABDEF0903F5BE9084C42B32E3615DF28B069A9D275A8A31DE7FBFD0DA76870B2B285F9D3
S = Temp2 ^ b mod (n)
Code I use:
//------------------------------
prod = BigIntegerFromInt(0);
BigIntegerModExp(prod, v, U, N, NULL, NULL);
S = BigIntegerFromInt(0);
BigIntegerMul(prod, prod, A, NULL);
BigIntegerModExp(S, prod, b, N, NULL, NULL);
BigIntegerToBytes(S, Buffer, 20);
//------------------------------
Your S should equal: 7666dc8a226dd0e3de093dddf6bc2b7929df2936a8cf15a972de4042766380ba
`
Next we Take S and Split it into Two Hashes. Even / Odd
`
S = reverse(S) // Yup we revsere the prior result.
Then We take only Odd digits
So S1 Should equal:
ba63..[other hex digits]..8a66
And S2 Should Equal:
8076..[other hex digits]..dc76
Then We Hash Each One:
S1_Hash = SHA(S1) // Should Equal: 02f448b61a6e79d8b714387c0d2f22cc83f2877f
S2_Hash = SHA(S2) // Should Equal: 61eb916a82b72864ea54dbfd3dc0ce4679c07814
And then we Re-combine them into S_Hash interleaving them togetner.
SS_Hash = S1_Hash[0] & S2_Hash[0] & S1_Hash[1] & S2_Hash[1]....
// SS_Hash should equal: 0261f4eb4891b66a1a826eb77928d864b7ea145438db7cfd0d3d2fc022cecc468379f2c087787f14
`
Ok, now lets calculate M1: You need to Calc M1 to verify it matches the M1 supplied by the client. This VERIFIES the username, password is correct.
`
N_Hash = SHA1(N) // Nothing is Reversed in this routine
G_Hash = SHA1(G)
User_Hash = SHA1(uppercase(Username))
// User Hash = 984816fd329622876e14907634264e6f332e9fb3
// XOr them
for(i=0;i<20;i++) NG_Hash = N_Hash[i] ^ G_Hash[i];
// NG_Hash = 1C76CF5659F68836A628404C431C1855E84D555B
Temp = NG_Hash & User_Hash & Salt & A & B & SS_Hash
// As you might Guess Temp is a really long string. :)
M1 = SHA1(Temp) // Should = eeb4adca80f4de02f9a9fe8d000d682e3ddfad6f
`
To Calculate M2:
`
Temp = A & M1 & SS_Hash
M2 = SHA1(Temp) // Should equal: 3a92ed0b783597be95654d6c66442046f9d389ae
`
Now you stuff the server packets M2 with the above M2, and unknowns are all Zero.
The client should request the realmlist packets at this time. You are logged IN!!! :-D
P.S. I would also like to see the Header Encryption Documented. I've seen some people have decoded it but they are being quite selfish with the knowledge, which sets everyone back. I mean how many development hours does it set everyone back attempting to re-inventing srp6? It has taken me a huge number of hours to get to this point... :o
[/quote]