void HandleRealmLogonChallenge() { ServerAuthChallenge challenge = new ServerAuthChallenge(new BinaryReader(connection.GetStream())); switch (challenge.error) { case AuthResult.SUCCESS: { Game.UI.LogDebug("Received logon challenge"); BigInteger N, A, B, a, u, x, S, salt, unk1, g, k; k = new BigInteger(3); #region Receive and initialize B = challenge.B.ToBigInteger(); // server public key g = challenge.g.ToBigInteger(); N = challenge.N.ToBigInteger(); // modulus salt = challenge.salt.ToBigInteger(); unk1 = challenge.unk3.ToBigInteger(); Game.UI.LogDebug("---====== Received from server: ======---"); Game.UI.LogDebug(string.Format("B={0}", B.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("N={0}", N.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("salt={0}", challenge.salt.ToHexString())); #endregion #region Hash password x = HashAlgorithm.SHA1.Hash(challenge.salt, PasswordHash).ToBigInteger(); Game.UI.LogDebug("---====== shared password hash ======---"); Game.UI.LogDebug(string.Format("g={0}", g.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("x={0}", x.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("N={0}", N.ToCleanByteArray().ToHexString())); #endregion #region Create random key pair var rand = System.Security.Cryptography.RandomNumberGenerator.Create(); do { byte[] randBytes = new byte[19]; rand.GetBytes(randBytes); a = randBytes.ToBigInteger(); A = g.ModPow(a, N); } while (A.ModPow(1, N) == 0); Game.UI.LogDebug("---====== Send data to server: ======---"); Game.UI.LogDebug(string.Format("A={0}", A.ToCleanByteArray().ToHexString())); #endregion #region Compute session key u = HashAlgorithm.SHA1.Hash(A.ToCleanByteArray(), B.ToCleanByteArray()).ToBigInteger(); // compute session key S = ((B + k * (N - g.ModPow(x, N))) % N).ModPow(a + (u * x), N); byte[] keyHash; byte[] sData = S.ToCleanByteArray(); if (sData.Length < 32) { var tmpBuffer = new byte[32]; Buffer.BlockCopy(sData, 0, tmpBuffer, 32 - sData.Length, sData.Length); sData = tmpBuffer; } byte[] keyData = new byte[40]; byte[] temp = new byte[16]; // take every even indices byte, hash, store in even indices for (int i = 0; i < 16; ++i) { temp[i] = sData[i * 2]; } keyHash = HashAlgorithm.SHA1.Hash(temp); for (int i = 0; i < 20; ++i) { keyData[i * 2] = keyHash[i]; } // do the same for odd indices for (int i = 0; i < 16; ++i) { temp[i] = sData[i * 2 + 1]; } keyHash = HashAlgorithm.SHA1.Hash(temp); for (int i = 0; i < 20; ++i) { keyData[i * 2 + 1] = keyHash[i]; } Key = keyData.ToBigInteger(); Game.UI.LogDebug("---====== Compute session key ======---"); Game.UI.LogDebug(string.Format("u={0}", u.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("S={0}", S.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("K={0}", Key.ToCleanByteArray().ToHexString())); #endregion #region Generate crypto proof // XOR the hashes of N and g together byte[] gNHash = new byte[20]; byte[] nHash = HashAlgorithm.SHA1.Hash(N.ToCleanByteArray()); for (int i = 0; i < 20; ++i) { gNHash[i] = nHash[i]; } Game.UI.LogDebug(string.Format("nHash={0}", nHash.ToHexString())); byte[] gHash = HashAlgorithm.SHA1.Hash(g.ToCleanByteArray()); for (int i = 0; i < 20; ++i) { gNHash[i] ^= gHash[i]; } Game.UI.LogDebug(string.Format("gHash={0}", gHash.ToHexString())); // hash username byte[] userHash = HashAlgorithm.SHA1.Hash(Encoding.ASCII.GetBytes(Username)); // our proof byte[] m1Hash = HashAlgorithm.SHA1.Hash ( gNHash, userHash, challenge.salt, A.ToCleanByteArray(), B.ToCleanByteArray(), Key.ToCleanByteArray() ); Game.UI.LogDebug("---====== Client proof: ======---"); Game.UI.LogDebug(string.Format("gNHash={0}", gNHash.ToHexString())); Game.UI.LogDebug(string.Format("userHash={0}", userHash.ToHexString())); Game.UI.LogDebug(string.Format("salt={0}", challenge.salt.ToHexString())); Game.UI.LogDebug(string.Format("A={0}", A.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("B={0}", B.ToCleanByteArray().ToHexString())); Game.UI.LogDebug(string.Format("key={0}", Key.ToCleanByteArray().ToHexString())); Game.UI.LogDebug("---====== Send proof to server: ======---"); Game.UI.LogDebug(string.Format("M={0}", m1Hash.ToHexString())); // expected proof for server m2 = HashAlgorithm.SHA1.Hash(A.ToCleanByteArray(), m1Hash, keyData); #endregion #region Send proof ClientAuthProof proof = new ClientAuthProof() { A = A.ToCleanByteArray(), M1 = m1Hash, crc = new byte[20], }; Game.UI.LogDebug("Sending logon proof"); proof.Send(stream); #endregion break; } case AuthResult.NO_MATCH: Game.UI.LogLine("Unknown account name", LogLevel.Error); break; case AuthResult.ACCOUNT_IN_USE: Game.UI.LogLine("Account already logged in", LogLevel.Error); break; case AuthResult.WRONG_BUILD_NUMBER: Game.UI.LogLine("Wrong build number", LogLevel.Error); break; } // get next command ReadCommand(); }
void HandleRealmLogonChallenge() { ServerAuthChallenge challenge = new ServerAuthChallenge(new BinaryReader(connection.GetStream())); switch (challenge.error) { case AuthResult.SUCCESS: { Game.UI.LogLine("Received logon challenge", LogLevel.Debug); BigInteger N, A, B, a, u, x, S, salt, unk1, g, k; k = new BigInteger(3); #region Receive and initialize B = challenge.B.ToBigInteger(); // server public key g = challenge.g.ToBigInteger(); N = challenge.N.ToBigInteger(); // modulus salt = challenge.salt.ToBigInteger(); unk1 = challenge.unk3.ToBigInteger(); Game.UI.LogLine("---====== Received from server: ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("B={0}", B.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("N={0}", N.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("salt={0}", challenge.salt.ToHexString()), LogLevel.Debug); #endregion #region Hash password x = HashAlgorithm.SHA1.Hash(challenge.salt, PasswordHash).ToBigInteger(); Game.UI.LogLine("---====== shared password hash ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("g={0}", g.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("x={0}", x.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("N={0}", N.ToCleanByteArray().ToHexString()), LogLevel.Debug); #endregion #region Create random key pair var rand = System.Security.Cryptography.RandomNumberGenerator.Create(); do { byte[] randBytes = new byte[19]; rand.GetBytes(randBytes); a = randBytes.ToBigInteger(); A = g.ModPow(a, N); } while (A.ModPow(1, N) == 0); Game.UI.LogLine("---====== Send data to server: ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("A={0}", A.ToCleanByteArray().ToHexString()), LogLevel.Debug); #endregion #region Compute session key u = HashAlgorithm.SHA1.Hash(A.ToCleanByteArray(), B.ToCleanByteArray()).ToBigInteger(); // compute session key // TODO: session key computation fails for some reason // all variables match exactly to the server side, but // S is different S = (B - k * g.ModPow(x, N)).ModPow(a + u * x, N); byte[] keyHash; byte[] sData = S.ToCleanByteArray(); byte[] keyData = new byte[40]; byte[] temp = new byte[16]; // take every even indices byte, hash, store in even indices for (int i = 0; i < 16; ++i) temp[i] = sData[i * 2]; keyHash = HashAlgorithm.SHA1.Hash(temp); for (int i = 0; i < 20; ++i) keyData[i * 2] = keyHash[i]; // do the same for odd indices for (int i = 0; i < 16; ++i) temp[i] = sData[i * 2 + 1]; keyHash = HashAlgorithm.SHA1.Hash(temp); for (int i = 0; i < 20; ++i) keyData[i * 2 + 1] = keyHash[i]; Key = keyData.ToBigInteger(); Game.UI.LogLine("---====== Compute session key ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("u={0}", u.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("S={0}", S.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("K={0}", Key.ToCleanByteArray().ToHexString()), LogLevel.Debug); #endregion #region Generate crypto proof // XOR the hashes of N and g together byte[] gNHash = new byte[20]; byte[] nHash = HashAlgorithm.SHA1.Hash(N.ToCleanByteArray()); for (int i = 0; i < 20; ++i) gNHash[i] = nHash[i]; Game.UI.LogLine(string.Format("nHash={0}", nHash.ToHexString()), LogLevel.Debug); byte[] gHash = HashAlgorithm.SHA1.Hash(g.ToCleanByteArray()); for (int i = 0; i < 20; ++i) gNHash[i] ^= gHash[i]; Game.UI.LogLine(string.Format("gHash={0}", gHash.ToHexString()), LogLevel.Debug); // hash username byte[] userHash = HashAlgorithm.SHA1.Hash(Encoding.ASCII.GetBytes(Username)); // our proof byte[] m1Hash = HashAlgorithm.SHA1.Hash ( gNHash, userHash, challenge.salt, A.ToCleanByteArray(), B.ToCleanByteArray(), Key.ToCleanByteArray() ); Game.UI.LogLine("---====== Client proof: ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("gNHash={0}", gNHash.ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("userHash={0}", userHash.ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("salt={0}", challenge.salt.ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("A={0}", A.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("B={0}", B.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine(string.Format("key={0}", Key.ToCleanByteArray().ToHexString()), LogLevel.Debug); Game.UI.LogLine("---====== Send proof to server: ======---", LogLevel.Debug); Game.UI.LogLine(string.Format("M={0}", m1Hash.ToHexString()), LogLevel.Debug); // expected proof for server m2 = HashAlgorithm.SHA1.Hash(A.ToCleanByteArray(), m1Hash, keyData); #endregion #region Send proof ClientAuthProof proof = new ClientAuthProof() { A = A.ToCleanByteArray(), M1 = m1Hash, crc = new byte[20], }; Game.UI.LogLine("Sending logon proof", LogLevel.Debug); proof.Send(output); #endregion break; } case AuthResult.NO_MATCH: Game.UI.LogLine("Unknown account name", LogLevel.Error); break; case AuthResult.ACCOUNT_IN_USE: Game.UI.LogLine("Account already logged in", LogLevel.Error); break; case AuthResult.WRONG_BUILD_NUMBER: Game.UI.LogLine("Wrong build number", LogLevel.Error); break; } // get next command ReadCommand(); }