PageRenderTime 41ms CodeModel.GetById 16ms RepoModel.GetById 0ms app.codeStats 0ms

/SteamKit2/SteamKit2/Steam2/AuthServerClient.cs

https://bitbucket.org/VoiDeD/steamre/
C# | 329 lines | 206 code | 77 blank | 46 comment | 9 complexity | 74630031066898309dd709df201f4a28 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.1, Apache-2.0, BSD-3-Clause
  1. /*
  2. * This file is subject to the terms and conditions defined in
  3. * file 'license.txt', which is part of this source code package.
  4. */
  5. using System;
  6. using System.Collections.Generic;
  7. using System.Text;
  8. using System.Diagnostics;
  9. using System.IO;
  10. using System.Xml;
  11. namespace SteamKit2
  12. {
  13. /// <summary>
  14. /// Represents a client capable of connecting to an authentication server.
  15. /// </summary>
  16. public sealed class AuthServerClient : ServerClient
  17. {
  18. uint internalIp, externalIp;
  19. uint salt1, salt2;
  20. byte[] aesKey, iv;
  21. /// <summary>
  22. /// Initializes a new instance of the <see cref="AuthServerClient"/> class.
  23. /// </summary>
  24. public AuthServerClient()
  25. {
  26. }
  27. /// <summary>
  28. /// Represents a set of possible results for calling Login.
  29. /// </summary>
  30. public enum LoginResult
  31. {
  32. /// <summary>
  33. /// An error occurred while requesting the client's external IP.
  34. /// </summary>
  35. ErrorRequestIP,
  36. /// <summary>
  37. /// An error occurred while reading the salt.
  38. /// </summary>
  39. ErrorGetSalt,
  40. /// <summary>
  41. /// An error occured while attempting the login command.
  42. /// </summary>
  43. ErrorDoLogin,
  44. /// <summary>
  45. /// An error occured while reading account info.
  46. /// </summary>
  47. ErrorGetAccountInfo,
  48. /// <summary>
  49. /// Successfully logged in.
  50. /// </summary>
  51. LoggedIn,
  52. /// <summary>
  53. /// Ocurrs when the password for the account is invalid, or the client's system clock is not accurate.
  54. /// </summary>
  55. InvalidPassword,
  56. /// <summary>
  57. /// The specified account does not exist.
  58. /// </summary>
  59. AccountNotFound,
  60. /// <summary>
  61. /// The account is disabled.
  62. /// </summary>
  63. AccountDisabled,
  64. }
  65. /// <summary>
  66. /// Attempts a login to the connected authentication server.
  67. /// </summary>
  68. /// <param name="user">The username.</param>
  69. /// <param name="pass">The pass.</param>
  70. /// <param name="clientTgt">The client TGT.</param>
  71. /// <param name="serverTgt">The server TGT.</param>
  72. /// <param name="accRecord">The client account record.</param>
  73. /// <returns>A LoginResult value describing what result.</returns>
  74. public LoginResult Login( string user, string pass, out ClientTGT clientTgt, out byte[] serverTgt, out AuthBlob accRecord )
  75. {
  76. clientTgt = null;
  77. serverTgt = null;
  78. accRecord = null;
  79. if ( !this.RequestIP( user ) )
  80. return LoginResult.ErrorRequestIP;
  81. if ( !this.GetSalt( user ) )
  82. return LoginResult.ErrorGetSalt;
  83. if ( !this.DoLogin( pass ) )
  84. return LoginResult.ErrorDoLogin;
  85. sbyte accResult = this.GetAccountInfo( out clientTgt, out serverTgt, out accRecord );
  86. switch ( accResult )
  87. {
  88. case -1:
  89. return LoginResult.ErrorGetAccountInfo;
  90. case 0:
  91. return LoginResult.LoggedIn;
  92. case 1:
  93. return LoginResult.AccountNotFound;
  94. case 2:
  95. return LoginResult.InvalidPassword;
  96. case 4:
  97. return LoginResult.AccountDisabled;
  98. default:
  99. return LoginResult.ErrorGetAccountInfo;
  100. }
  101. }
  102. bool RequestIP( string user )
  103. {
  104. user = user.ToLower();
  105. try
  106. {
  107. internalIp = NetHelpers.GetIPAddress( Socket.GetLocalIP() );
  108. byte[] userHash = CryptoHelper.JenkinsHash( Encoding.ASCII.GetBytes( user ) );
  109. uint userData = BitConverter.ToUInt32( userHash, 0 ) & 1;
  110. BinaryWriterEx bb = new BinaryWriterEx(true);
  111. bb.WriteType<uint>( 0 );
  112. bb.WriteType<byte>( 4 );
  113. bb.Write( internalIp );
  114. bb.Write( userData );
  115. Socket.Send( bb.ToArray() );
  116. byte result = Socket.Reader.ReadByte();
  117. if ( result != 0 )
  118. {
  119. DebugLog.WriteLine( "AuthServerClient", "RequestIP failed. Result: {0}", result );
  120. Socket.Disconnect();
  121. return false;
  122. }
  123. externalIp = NetHelpers.EndianSwap( Socket.Reader.ReadUInt32() );
  124. }
  125. catch ( Exception ex )
  126. {
  127. DebugLog.WriteLine( "AuthServerClient", "RequestIP threw an exception.\n{0}", ex.ToString() );
  128. Socket.Disconnect();
  129. return false;
  130. }
  131. return true;
  132. }
  133. bool GetSalt( string user )
  134. {
  135. ushort userLen = ( ushort )Encoding.ASCII.GetByteCount( user );
  136. user = user.ToLower();
  137. if ( !this.SendCommand( 2, userLen, user, userLen, user ) )
  138. {
  139. DebugLog.WriteLine( "AuthServerClient", "GetSalt command '2' failed." );
  140. Socket.Disconnect();
  141. return false;
  142. }
  143. try
  144. {
  145. salt1 = Socket.Reader.ReadUInt32();
  146. salt2 = Socket.Reader.ReadUInt32();
  147. }
  148. catch ( Exception ex )
  149. {
  150. DebugLog.WriteLine ("AuthServerClient", "GetSalt threw an exception while reading the salt.\n{0}", ex.ToString() );
  151. Socket.Disconnect();
  152. return false;
  153. }
  154. return true;
  155. }
  156. bool DoLogin( string pass )
  157. {
  158. try
  159. {
  160. aesKey = AuthServerClient.GenerateAESKey( salt1, salt2, pass );
  161. iv = CryptoHelper.GenerateRandomBlock( 16 );
  162. MicroTime mt = MicroTime.Now;
  163. mt = mt ^ AuthServerClient.GetObfuscationMask( internalIp, externalIp );
  164. byte[] plainText = AuthServerClient.GetPlaintext( mt, internalIp );
  165. byte[] cipherText = CryptoHelper.AESEncrypt( plainText, aesKey, iv );
  166. TcpPacket packet = new TcpPacket();
  167. packet.Write( iv );
  168. packet.Write( ( ushort )plainText.Length );
  169. packet.Write( ( ushort )cipherText.Length );
  170. packet.Write( cipherText );
  171. Socket.Send( packet );
  172. }
  173. catch ( Exception ex )
  174. {
  175. DebugLog.WriteLine( "AuthServerClient", "DoLogin threw an exception while sending login packet.\n{0}", ex.ToString() );
  176. Socket.Disconnect();
  177. return false;
  178. }
  179. return true;
  180. }
  181. sbyte GetAccountInfo( out ClientTGT clientTgt, out byte[] serverTgt, out AuthBlob accRecord )
  182. {
  183. clientTgt = null;
  184. serverTgt = null;
  185. accRecord = null;
  186. try
  187. {
  188. byte result = Socket.Reader.ReadByte();
  189. if ( result != 0 )
  190. {
  191. DebugLog.WriteLine( "AuthServerClient", "GetAccountInfo failed. Result: {0}", result );
  192. Socket.Disconnect();
  193. return ( sbyte )result;
  194. }
  195. ulong loginTime = Socket.Reader.ReadUInt64();
  196. ulong loginExpiry = Socket.Reader.ReadUInt64();
  197. TcpPacket packet = Socket.ReceivePacket();
  198. DataStream ds = new DataStream( packet.GetPayload(), true );
  199. ushort versionNum = ds.ReadUInt16();
  200. byte[] tgtIv = ds.ReadBytes( 16 );
  201. ushort tgtPlainSize = ds.ReadUInt16();
  202. ushort tgtCipherSize = ds.ReadUInt16();
  203. byte[] tgtCipher = ds.ReadBytes( tgtCipherSize );
  204. byte[] tgtPlain = CryptoHelper.AESDecrypt( tgtCipher, aesKey, tgtIv );
  205. clientTgt = ClientTGT.Deserialize( tgtPlain );
  206. ushort serverTgtSize = ds.ReadUInt16();
  207. serverTgt = ds.ReadBytes( serverTgtSize );
  208. uint accRecordSize = ds.ReadUInt32();
  209. byte[] accData = ds.ReadBytes( ds.SizeRemaining() );
  210. using (BlobTypedReader<AuthBlob> blobParser = BlobTypedReader<AuthBlob>.Create(new MemoryStream(accData)))
  211. {
  212. blobParser.SetKey(clientTgt.AccountRecordKey);
  213. blobParser.Process();
  214. accRecord = blobParser.Target;
  215. }
  216. }
  217. catch ( Exception ex )
  218. {
  219. DebugLog.WriteLine( "AuthServerClient", "GetAccountInfo threw an exception while reading account info.\n{0}", ex.ToString() );
  220. Socket.Disconnect();
  221. return -1;
  222. }
  223. return 0;
  224. }
  225. static byte[] GenerateAESKey( uint salt1, uint salt2, string pass )
  226. {
  227. BinaryWriterEx bb = new BinaryWriterEx();
  228. bb.Write( salt1 );
  229. bb.Write( Encoding.ASCII.GetBytes( pass ) );
  230. bb.Write( salt2 );
  231. byte[] digest = CryptoHelper.SHAHash( bb.ToArray() );
  232. byte[] aesKey = new byte[ 16 ];
  233. Array.Copy( digest, 0, aesKey, 0, 16 );
  234. return aesKey;
  235. }
  236. static ulong GetObfuscationMask( uint internalIp, uint externalIp )
  237. {
  238. BinaryWriterEx bb = new BinaryWriterEx();
  239. bb.Write( externalIp );
  240. bb.Write( internalIp );
  241. byte[] digest = CryptoHelper.SHAHash( bb.ToArray() );
  242. return BitConverter.ToUInt64( digest, 0 );
  243. }
  244. static byte[] GetPlaintext( ulong timeStamp, uint internalIp )
  245. {
  246. BinaryWriterEx bb = new BinaryWriterEx();
  247. bb.Write( timeStamp );
  248. bb.Write( internalIp );
  249. return bb.ToArray();
  250. }
  251. }
  252. }