diff --git a/POGOLib.Official/Configuration.cs b/POGOLib.Official/Configuration.cs index 3d3e0f6c1..46969e532 100644 --- a/POGOLib.Official/Configuration.cs +++ b/POGOLib.Official/Configuration.cs @@ -1,9 +1,4 @@ using POGOLib.Official.Util.Hash; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace POGOLib.Official { diff --git a/POGOLib.Official/Constants.cs b/POGOLib.Official/Constants.cs index aa06472c3..867d41f5e 100644 --- a/POGOLib.Official/Constants.cs +++ b/POGOLib.Official/Constants.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace POGOLib.Official +namespace POGOLib.Official { public static class Constants { diff --git a/POGOLib.Official/Exceptions/SessionStateException.cs b/POGOLib.Official/Exceptions/SessionStateException.cs new file mode 100644 index 000000000..7bb781227 --- /dev/null +++ b/POGOLib.Official/Exceptions/SessionStateException.cs @@ -0,0 +1,11 @@ +using System; + +namespace POGOLib.Official.Exceptions +{ + public class SessionStateException : Exception + { + public SessionStateException(string message) : base(message) + { + } + } +} diff --git a/POGOLib.Official/Extensions/RandomExtension.cs b/POGOLib.Official/Extensions/RandomExtension.cs index 3e258e5a5..b3b4271c6 100644 --- a/POGOLib.Official/Extensions/RandomExtension.cs +++ b/POGOLib.Official/Extensions/RandomExtension.cs @@ -1,7 +1,5 @@ using System; -using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Extensions { diff --git a/POGOLib.Official/Logging/LogLevel.cs b/POGOLib.Official/Logging/LogLevel.cs index bb1b1584d..d18a75e06 100644 --- a/POGOLib.Official/Logging/LogLevel.cs +++ b/POGOLib.Official/Logging/LogLevel.cs @@ -1,9 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; - -namespace POGOLib.Official.Logging +namespace POGOLib.Official.Logging { public enum LogLevel { diff --git a/POGOLib.Official/Logging/Logger.cs b/POGOLib.Official/Logging/Logger.cs index 855508c4b..56d7347d8 100644 --- a/POGOLib.Official/Logging/Logger.cs +++ b/POGOLib.Official/Logging/Logger.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; namespace POGOLib.Official.Logging { @@ -43,7 +40,7 @@ public static void Error(string message) private static void Output(LogLevel logLevel, string message) { - foreach (var logOutput in LogOutputs) + foreach(var logOutput in LogOutputs) { logOutput(logLevel, message); } diff --git a/POGOLib.Official/LoginProviders/ILoginProvider.cs b/POGOLib.Official/LoginProviders/ILoginProvider.cs index 469ae3142..5ce89c667 100644 --- a/POGOLib.Official/LoginProviders/ILoginProvider.cs +++ b/POGOLib.Official/LoginProviders/ILoginProvider.cs @@ -1,8 +1,5 @@ -using POGOLib.Official.Net.Authentication.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Threading.Tasks; +using POGOLib.Official.Net.Authentication.Data; namespace POGOLib.Official.LoginProviders { diff --git a/POGOLib.Official/LoginProviders/PtcLoginException.cs b/POGOLib.Official/LoginProviders/PtcLoginException.cs index 7fcf03038..4b0f21814 100644 --- a/POGOLib.Official/LoginProviders/PtcLoginException.cs +++ b/POGOLib.Official/LoginProviders/PtcLoginException.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.LoginProviders { diff --git a/POGOLib.Official/LoginProviders/PtcLoginProvider.cs b/POGOLib.Official/LoginProviders/PtcLoginProvider.cs index 22d200374..fe856a7ae 100644 --- a/POGOLib.Official/LoginProviders/PtcLoginProvider.cs +++ b/POGOLib.Official/LoginProviders/PtcLoginProvider.cs @@ -1,16 +1,16 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using POGOLib.Official.Logging; -using POGOLib.Official.Net.Authentication.Data; -using System; +using System; using System.Collections.Generic; -using System.Linq; using System.Net.Http; using System.Text.RegularExpressions; using System.Threading.Tasks; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using POGOLib.Official.Logging; +using POGOLib.Official.Net.Authentication.Data; namespace POGOLib.Official.LoginProviders { + /// /// The for Pokemon Trainer Club. /// Use this if you want to authenticate to PokemonGo using a Pokemon Trainer Club account. diff --git a/POGOLib.Official/Net/Authentication/Data/AccessToken.cs b/POGOLib.Official/Net/Authentication/Data/AccessToken.cs index e012b7404..7a6cbfe4f 100644 --- a/POGOLib.Official/Net/Authentication/Data/AccessToken.cs +++ b/POGOLib.Official/Net/Authentication/Data/AccessToken.cs @@ -1,9 +1,6 @@ -using Newtonsoft.Json; +using System; +using Newtonsoft.Json; using POGOProtos.Networking.Envelopes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Net.Authentication.Data { diff --git a/POGOLib.Official/Net/Authentication/Data/LoginData.cs b/POGOLib.Official/Net/Authentication/Data/LoginData.cs index 3b52912f4..f86939ae1 100644 --- a/POGOLib.Official/Net/Authentication/Data/LoginData.cs +++ b/POGOLib.Official/Net/Authentication/Data/LoginData.cs @@ -1,8 +1,4 @@ using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Net.Authentication.Data { diff --git a/POGOLib.Official/Net/Authentication/Exceptions/WrongCredentialsException.cs b/POGOLib.Official/Net/Authentication/Exceptions/WrongCredentialsException.cs index bb04741ec..768587ec4 100644 --- a/POGOLib.Official/Net/Authentication/Exceptions/WrongCredentialsException.cs +++ b/POGOLib.Official/Net/Authentication/Exceptions/WrongCredentialsException.cs @@ -1,15 +1,14 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Net.Authentication.Exceptions { public class WrongCredentialsException : Exception { + public WrongCredentialsException(string message) : base(message) { - + } + } } diff --git a/POGOLib.Official/Net/Authentication/Login.cs b/POGOLib.Official/Net/Authentication/Login.cs index c7200a73a..7ea1769cc 100644 --- a/POGOLib.Official/Net/Authentication/Login.cs +++ b/POGOLib.Official/Net/Authentication/Login.cs @@ -1,11 +1,9 @@ -using GeoCoordinatePortable; +using System; +using System.Threading.Tasks; +using GeoCoordinatePortable; using POGOLib.Official.Logging; using POGOLib.Official.LoginProviders; using POGOLib.Official.Net.Authentication.Data; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using static POGOProtos.Networking.Envelopes.Signature.Types; namespace POGOLib.Official.Net.Authentication @@ -77,6 +75,6 @@ public static async Task GetSession(ILoginProvider loginProvider, GeoCo { return new Session(loginProvider, await loginProvider.GetAccessToken(), coordinate, deviceInfo); } - + } } diff --git a/POGOLib.Official/Net/Captcha/CaptchaEventArgs.cs b/POGOLib.Official/Net/Captcha/CaptchaEventArgs.cs new file mode 100644 index 000000000..52ee5050c --- /dev/null +++ b/POGOLib.Official/Net/Captcha/CaptchaEventArgs.cs @@ -0,0 +1,14 @@ +using System; + +namespace POGOLib.Official.Net.Captcha +{ + public class CaptchaEventArgs : EventArgs + { + public CaptchaEventArgs(string captchaUrl) + { + CaptchaUrl = captchaUrl; + } + + public string CaptchaUrl { get; } + } +} diff --git a/POGOLib.Official/Net/RpcClient.cs b/POGOLib.Official/Net/RpcClient.cs index b47fa046a..f6b9f4e06 100644 --- a/POGOLib.Official/Net/RpcClient.cs +++ b/POGOLib.Official/Net/RpcClient.cs @@ -1,31 +1,30 @@ -using GeoCoordinatePortable; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Net.Http; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using GeoCoordinatePortable; using Google.Protobuf; using Newtonsoft.Json; using POGOLib.Official.Logging; using POGOLib.Official.Util; -using POGOProtos.Enums; using POGOProtos.Map; using POGOProtos.Networking.Envelopes; -using POGOProtos.Networking.Platform; -using POGOProtos.Networking.Platform.Requests; -using POGOProtos.Networking.Platform.Responses; using POGOProtos.Networking.Requests; using POGOProtos.Networking.Requests.Messages; using POGOProtos.Networking.Responses; -using System; -using System.Collections.Concurrent; -using System.Collections.Generic; -using System.Linq; -using System.Net.Http; -using System.Text.RegularExpressions; -using System.Threading; -using System.Threading.Tasks; +using POGOProtos.Enums; +using POGOProtos.Networking.Platform; +using POGOProtos.Networking.Platform.Requests; +using POGOProtos.Networking.Platform.Responses; namespace POGOLib.Official.Net { public class RpcClient : IDisposable { - /// /// The authenticated . /// @@ -61,9 +60,8 @@ public class RpcClient : IDisposable private readonly ConcurrentDictionary _rpcResponses = new ConcurrentDictionary(); - private static readonly Semaphore RpcQueueMutex = new Semaphore(1, 1); + private readonly Semaphore _rpcQueueMutex = new Semaphore(1, 1); - public event EventHandler CheckChallengeReceived; public event EventHandler HatchedEggsReceived; public event EventHandler CheckAwardedBadgesReceived; @@ -79,17 +77,12 @@ internal RpcClient(Session session) internal DateTime LastRpcMapObjectsRequest { get; private set; } internal GeoCoordinate LastGeoCoordinateMapObjectsRequest { get; private set; } = new GeoCoordinate(); - + internal Platform GetPlatform() { return _session.DeviceInfo.DeviceBrand == "Apple" ? Platform.Ios : Platform.Android; } - internal void ResetLastTimestampSinceStart() - { - _rpcEncryption.ResetLastTimestampSinceStart(); - } - private long PositiveRandom() { long ret = _session.Random.Next() | (_session.Random.Next() << 32); @@ -115,7 +108,7 @@ private void IncrementRequestCount() break; } } - + /// /// Sends all requests which the (ios-)client sends on startup /// @@ -269,30 +262,38 @@ public async Task RefreshMapObjectsAsync() }.ToByteString() }); - var mapObjects = GetMapObjectsResponse.Parser.ParseFrom(response); - if (mapObjects.Status == MapObjectsStatus.Success) + if (response != null) { - // TODO: Cleaner? - var pokemonCatchable = mapObjects.MapCells.SelectMany(c => c.CatchablePokemons).Count(); - var pokemonWild = mapObjects.MapCells.SelectMany(c => c.WildPokemons).Count(); - var pokemonNearby = mapObjects.MapCells.SelectMany(c => c.NearbyPokemons).Count(); - var pokemonCount = pokemonCatchable + pokemonWild + pokemonNearby; + var mapObjects = GetMapObjectsResponse.Parser.ParseFrom(response); + if (mapObjects.Status == MapObjectsStatus.Success) + { + // TODO: Cleaner? + var pokemonCatchable = mapObjects.MapCells.SelectMany(c => c.CatchablePokemons).Count(); + var pokemonWild = mapObjects.MapCells.SelectMany(c => c.WildPokemons).Count(); + var pokemonNearby = mapObjects.MapCells.SelectMany(c => c.NearbyPokemons).Count(); + var pokemonCount = pokemonCatchable + pokemonWild + pokemonNearby; - Logger.Debug($"Received '{mapObjects.MapCells.Count}' map cells."); - Logger.Debug($"Received '{pokemonCount}' pokemons. Catchable({pokemonCatchable}) Wild({pokemonWild}) Nearby({pokemonNearby})"); - Logger.Debug($"Received '{mapObjects.MapCells.SelectMany(c => c.Forts).Count()}' forts."); + Logger.Debug($"Received '{mapObjects.MapCells.Count}' map cells."); + Logger.Debug($"Received '{pokemonCount}' pokemons. Catchable({pokemonCatchable}) Wild({pokemonWild}) Nearby({pokemonNearby})"); + Logger.Debug($"Received '{mapObjects.MapCells.SelectMany(c => c.Forts).Count()}' forts."); + + if (mapObjects.MapCells.Count == 0) + { + Logger.Error("We received 0 map cells, are your GPS coordinates correct?"); + return; + } - if (mapObjects.MapCells.Count == 0) + _session.Map.Cells = mapObjects.MapCells; + } + else { - Logger.Error("We received 0 map cells, are your GPS coordinates correct?"); - return; + Logger.Error($"GetMapObjects status is: '{mapObjects.Status}'."); } - - _session.Map.Cells = mapObjects.MapCells; } - else + else if (_session.State != SessionState.Paused) { - Logger.Error($"GetMapObjects status is: '{mapObjects.Status}'."); + // POGOLib didn't expect this. + throw new NullReferenceException(nameof(response)); } } @@ -379,8 +380,7 @@ private IEnumerable GetDefaultRequests() }.ToByteString() }); } - - + //If Incense is active we add this: //request.Add(new Request //{ @@ -471,14 +471,14 @@ public async Task SendRemoteProcedureCallAsync(RequestType requestTy }); } - public async Task SendRemoteProcedureCallAsync(Request request, bool sendDefaultRequests = true) + public async Task SendRemoteProcedureCallAsync(Request request, bool addDefaultRequests = true) { - return await SendRemoteProcedureCall(await GetRequestEnvelopeAsync(new[] { request }, sendDefaultRequests)); + return await SendRemoteProcedureCall(await GetRequestEnvelopeAsync(new[] {request}, addDefaultRequests)); } - public async Task SendRemoteProcedureCallAsync(Request[] request) + public async Task SendRemoteProcedureCallAsync(Request[] request, bool addDefaultRequests = false) { - return await SendRemoteProcedureCall(await GetRequestEnvelopeAsync(request, false)); + return await SendRemoteProcedureCall(await GetRequestEnvelopeAsync(request, addDefaultRequests)); } private Task SendRemoteProcedureCall(RequestEnvelope requestEnvelope) @@ -489,7 +489,7 @@ private Task SendRemoteProcedureCall(RequestEnvelope requestEnvelope try { - RpcQueueMutex.WaitOne(); + _rpcQueueMutex.WaitOne(); RequestEnvelope processRequestEnvelope; while (_rpcQueue.TryDequeue(out processRequestEnvelope)) @@ -511,7 +511,7 @@ private Task SendRemoteProcedureCall(RequestEnvelope requestEnvelope } finally { - RpcQueueMutex.Release(); + _rpcQueueMutex.Release(); } }); } @@ -520,6 +520,22 @@ private async Task PerformRemoteProcedureCallAsync(RequestEnvelope r { try { + switch (_session.State) + { + case SessionState.Stopped: + Logger.Error("We tried to send a request while the session was stopped."); + return null; + + case SessionState.Paused: + var requests = requestEnvelope.Requests.Select(x => x.RequestType).ToList(); + if (requests.Count != 1 || requests[0] != RequestType.VerifyChallenge) + { + Logger.Error("We tried to send a request while the session was paused. The only request allowed is VerifyChallenge."); + return null; + } + break; + } + using (var requestData = new ByteArrayContent(requestEnvelope.ToByteArray())) { Logger.Debug($"Sending RPC Request: '{string.Join(", ", requestEnvelope.Requests.Select(x => x.RequestType))}'"); @@ -540,10 +556,10 @@ private async Task PerformRemoteProcedureCallAsync(RequestEnvelope r switch (responseEnvelope.StatusCode) { // Valid response. - case ResponseEnvelope.Types.StatusCode.Ok: + case ResponseEnvelope.Types.StatusCode.Ok: // Success!? break; - + // Valid response and new rpc url. case ResponseEnvelope.Types.StatusCode.OkRpcUrlInResponse: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) @@ -557,7 +573,7 @@ private async Task PerformRemoteProcedureCallAsync(RequestEnvelope r break; // A new rpc endpoint is available. - case ResponseEnvelope.Types.StatusCode.Redirect: + case ResponseEnvelope.Types.StatusCode.Redirect: if (Regex.IsMatch(responseEnvelope.ApiUrl, "pgorelease\\.nianticlabs\\.com\\/plfe\\/\\d+")) { _requestUrl = $"https://{responseEnvelope.ApiUrl}/rpc"; @@ -622,7 +638,7 @@ private async Task PerformRemoteProcedureCallAsync(RequestEnvelope r var unknownPtr8Response = UnknownPtr8Response.Parser.ParseFrom(mapPlatform.Response); _mapKey = unknownPtr8Response.Message; } - + return HandleResponseEnvelope(requestEnvelope, responseEnvelope); } } @@ -702,7 +718,7 @@ private void HandleInventoryResponses(Request request, ByteString requestRespons private void HandleDefaultResponses(RequestEnvelope requestEnvelope, IList returns) { var responseIndexes = new Dictionary(); - + for (var i = 0; i < requestEnvelope.Requests.Count; i++) { var request = requestEnvelope.Requests[i]; @@ -773,15 +789,13 @@ private void HandleDefaultResponses(RequestEnvelope requestEnvelope, IList /// The authenticated . /// @@ -37,11 +36,6 @@ internal class RpcEncryption /// private long _lastTimestampSinceStart; - public void ResetLastTimestampSinceStart() - { - _lastTimestampSinceStart = 0; - } - internal RpcEncryption(Session session) { _session = session; @@ -55,8 +49,7 @@ internal RpcEncryption(Session session) } private long TimestampSinceStartMs => _stopwatch.ElapsedMilliseconds; - - + /// /// Generates a few random es to act like a real GPS sensor. /// @@ -67,73 +60,66 @@ private List BuildLocationFixes(RequestEnvelope requestEnvelope, lo { var locationFixes = new List(); - try - { - if (requestEnvelope.Requests.Count == 0 || requestEnvelope.Requests[0] == null) - return locationFixes; - - // Determine amount of location fixes. - // We look for the amount of seconds that have passed since the last location fixes request. - // If that results in 0, send 1 location fix. - var millisecondsPerFix = _session.Random.Next(995, 999); - var providerCount = Math.Min((timestampSinceStart - _lastTimestampSinceStart) / millisecondsPerFix, _session.Random.Next(8, 11)); - if (providerCount == 0) - providerCount = 1; - - // Determine size of the "play around" window. - // Not so relevant when starting up. - var totalMilliseconds = providerCount * millisecondsPerFix; - var baseTimestampSnapshot = Math.Max(timestampSinceStart - totalMilliseconds, 0); - var playAroundWindow = baseTimestampSnapshot - _lastTimestampSinceStart; - - if (playAroundWindow == 0 && providerCount == 1 && millisecondsPerFix >= timestampSinceStart) - { - // We really need an offset for this one.. - playAroundWindow = _session.Random.Next(0, (int)timestampSinceStart); - } - else - { - // A small offset between location fixes. - playAroundWindow = Math.Min(playAroundWindow, providerCount * 2); - } - - // Share "play around" window over all location fixes. - var playAroundWindowPart = playAroundWindow != 0 - ? playAroundWindow / providerCount - : 1; + if (requestEnvelope.Requests.Count == 0 || requestEnvelope.Requests[0] == null) + return locationFixes; - for (var i = 0; i < providerCount; i++) - { - var timestampSnapshot = baseTimestampSnapshot; - // Apply current location fix position. - timestampSnapshot += i * millisecondsPerFix; - // Apply an offset. - timestampSnapshot += _session.Random.Next(0, (int)((i + 1) * Math.Abs(playAroundWindowPart))); + // Determine amount of location fixes. + // We look for the amount of seconds that have passed since the last location fixes request. + // If that results in 0, send 1 location fix. + var millisecondsPerFix = _session.Random.Next(995, 999); + var providerCount = Math.Min((timestampSinceStart - _lastTimestampSinceStart) / millisecondsPerFix, _session.Random.Next(8, 11)); + if (providerCount == 0) + providerCount = 1; - locationFixes.Add(new LocationFix - { - TimestampSnapshot = (ulong)timestampSnapshot, - Latitude = LocationUtil.OffsetLatitudeLongitude(_session.Player.Coordinate.Latitude, _session.Random.Next(100) + 10), - Longitude = LocationUtil.OffsetLatitudeLongitude(_session.Player.Coordinate.Longitude, _session.Random.Next(100) + 10), - HorizontalAccuracy = (float)_session.Random.NextDouble(5.0, 25.0), - VerticalAccuracy = (float)_session.Random.NextDouble(5.0, 25.0), - Altitude = (float)_session.Random.NextDouble(10.0, 30.0), - Provider = "fused", - ProviderStatus = 3, - LocationType = 1, - Course = -1, - Speed = -1 - // Floor = 0 - }); - } + // Determine size of the "play around" window. + // Not so relevant when starting up. + var totalMilliseconds = providerCount * millisecondsPerFix; + var baseTimestampSnapshot = Math.Max(timestampSinceStart - totalMilliseconds, 0); + var playAroundWindow = Math.Max(0, baseTimestampSnapshot - _lastTimestampSinceStart); - _lastTimestampSinceStart = timestampSinceStart; + if (playAroundWindow == 0 && providerCount == 1 && millisecondsPerFix >= timestampSinceStart) + { + // We really need an offset for this one.. + playAroundWindow = _session.Random.Next(0, (int) timestampSinceStart); } - catch (Exception ex) + else { + // A small offset between location fixes. + playAroundWindow = Math.Min(playAroundWindow, providerCount * 2); + } + // Share "play around" window over all location fixes. + var playAroundWindowPart = playAroundWindow != 0 + ? playAroundWindow / providerCount + : 1; + + for (var i = 0; i < providerCount; i++) + { + var timestampSnapshot = baseTimestampSnapshot; + // Apply current location fix position. + timestampSnapshot += i * millisecondsPerFix; + // Apply an offset. + timestampSnapshot += _session.Random.Next(0, (int) ((i + 1) * playAroundWindowPart)); + + locationFixes.Add(new LocationFix + { + TimestampSnapshot = (ulong) timestampSnapshot, + Latitude = LocationUtil.OffsetLatitudeLongitude(_session.Player.Coordinate.Latitude, _session.Random.Next(100) + 10), + Longitude = LocationUtil.OffsetLatitudeLongitude(_session.Player.Coordinate.Longitude, _session.Random.Next(100) + 10), + HorizontalAccuracy = (float) _session.Random.NextDouble(5.0, 25.0), + VerticalAccuracy = (float) _session.Random.NextDouble(5.0, 25.0), + Altitude = (float) _session.Random.NextDouble(10.0, 30.0), + Provider = "fused", + ProviderStatus = 3, + LocationType = 1, + Course = -1, + Speed = -1 + // Floor = 0 + }); } + _lastTimestampSinceStart = timestampSinceStart; + return locationFixes; } @@ -157,7 +143,7 @@ internal async Task GenerateSignatureAsync(RequestEnvelope requ _session.Player.Coordinate.Altitude = locationFix.Altitude; requestEnvelope.Accuracy = _session.Player.Coordinate.Altitude; // _session.Player.Coordinate.HorizontalAccuracy; - requestEnvelope.MsSinceLastLocationfix = timestampSinceStart - (long)locationFix.TimestampSnapshot; + requestEnvelope.MsSinceLastLocationfix = timestampSinceStart - (long) locationFix.TimestampSnapshot; var signature = new Signature { @@ -204,8 +190,8 @@ internal async Task GenerateSignatureAsync(RequestEnvelope requ var requestsBytes = requestEnvelope.Requests.Select(x => x.ToByteArray()).ToArray(); var hashData = await Configuration.Hasher.GetHashDataAsync(requestEnvelope, signature, locationBytes, requestsBytes, serializedTicket); - signature.LocationHash1 = (int)hashData.LocationAuthHash; - signature.LocationHash2 = (int)hashData.LocationHash; + signature.LocationHash1 = (int) hashData.LocationAuthHash; + signature.LocationHash2 = (int) hashData.LocationHash; signature.RequestHash.AddRange(hashData.RequestHashes); @@ -217,9 +203,8 @@ internal async Task GenerateSignatureAsync(RequestEnvelope requ EncryptedSignature = ByteString.CopyFrom(Configuration.Hasher.GetEncryptedSignature(signature.ToByteArray(), (uint)timestampSinceStart)) }.ToByteString() }; - + return encryptedSignature; } - } } diff --git a/POGOLib.Official/Net/Session.cs b/POGOLib.Official/Net/Session.cs index 2afd67bd8..d76e4521b 100644 --- a/POGOLib.Official/Net/Session.cs +++ b/POGOLib.Official/Net/Session.cs @@ -1,18 +1,19 @@ -using GeoCoordinatePortable; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Threading; +using System.Threading.Tasks; +using GeoCoordinatePortable; +using POGOLib.Official.Exceptions; using POGOLib.Official.Logging; using POGOLib.Official.LoginProviders; using POGOLib.Official.Net.Authentication.Data; +using POGOLib.Official.Net.Captcha; using POGOLib.Official.Pokemon; using POGOLib.Official.Util.Device; using POGOLib.Official.Util.Hash; using POGOProtos.Settings; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Threading; -using System.Threading.Tasks; using static POGOProtos.Networking.Envelopes.Signature.Types; namespace POGOLib.Official.Net @@ -22,6 +23,7 @@ namespace POGOLib.Official.Net /// public class Session : IDisposable { + private SessionState _state; /// /// This is the which is responsible for retrieving events and updating gps location. @@ -49,6 +51,8 @@ internal Session(ILoginProvider loginProvider, AccessToken accessToken, GeoCoord throw new ArgumentException($"LoginProvider ID must be one of the following: {string.Join(", ", ValidLoginProviders)}"); } + State = SessionState.Stopped; + HttpClient = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate @@ -59,12 +63,26 @@ internal Session(ILoginProvider loginProvider, AccessToken accessToken, GeoCoord DeviceInfo = deviceInfo ?? DeviceInfoUtil.GetRandomDevice(this); AccessToken = accessToken; LoginProvider = loginProvider; - Player = new Player(geoCoordinate); + Player = new Player(this, geoCoordinate); Map = new Map(this); RpcClient = new RpcClient(this); _heartbeat = new HeartbeatDispatcher(this); } + /// + /// Gets the of the . + /// + public SessionState State + { + get { return _state; } + private set + { + _state = value; + + Logger.Debug($"Session state was set to {_state}."); + } + } + /// /// Gets the of the . /// @@ -114,34 +132,62 @@ internal Session(ILoginProvider loginProvider, AccessToken accessToken, GeoCoord public async Task StartupAsync() { + if (State != SessionState.Stopped) + { + throw new SessionStateException("The session has already been started."); + } + if (!Configuration.IgnoreHashVersion) { await CheckHasherVersion(); } - if (!await RpcClient.StartupAsync().ConfigureAwait(false)) + State = SessionState.Started; + + if (!await RpcClient.StartupAsync()) { return false; } - await _heartbeat.StartDispatcher().ConfigureAwait(false); + await _heartbeat.StartDispatcherAsync(); return true; } public void Pause() { - _heartbeat.PauseDispatcher(); + if (State != SessionState.Started && + State != SessionState.Resumed) + { + throw new SessionStateException("The session is not running."); + } + + State = SessionState.Paused; + + _heartbeat.StopDispatcher(); } - public void Resume() + public async Task ResumeAsync() { - RpcClient.ResetLastTimestampSinceStart(); - _heartbeat.ResumeDispatcher(); + if (State != SessionState.Paused) + { + throw new SessionStateException("The session is not paused."); + } + + State = SessionState.Resumed; + + await _heartbeat.StartDispatcherAsync(); } public void Shutdown() { + if (State == SessionState.Stopped) + { + throw new SessionStateException("The session has already been stopped."); + } + + State = SessionState.Stopped; + _heartbeat.StopDispatcher(); } @@ -158,7 +204,7 @@ public async Task CheckHasherVersion() var pogoVersion = new Version(pogoVersionRaw); var result = Configuration.Hasher.PokemonVersion.CompareTo(pogoVersion); - if (result != 0) + if (result < 0) { throw new HashVersionMismatchException($"The version of the {nameof(Configuration.Hasher)} ({Configuration.Hasher.PokemonVersion}) does not match the minimal API version of PokemonGo ({pogoVersion})."); } @@ -188,7 +234,7 @@ internal async Task Reauthenticate() { if (accessToken == null) { - var sleepSeconds = Math.Min(60, ++tries * 5); + var sleepSeconds = Math.Min(60, ++tries*5); Logger.Error($"Reauthentication failed, trying again in {sleepSeconds} seconds."); await Task.Delay(TimeSpan.FromMilliseconds(sleepSeconds * 1000)); } @@ -200,17 +246,43 @@ internal async Task Reauthenticate() ReauthenticateMutex.Release(); } + #region Events private void OnAccessTokenUpdated() { AccessTokenUpdated?.Invoke(this, EventArgs.Empty); } + internal void OnInventoryUpdate() + { + InventoryUpdate?.Invoke(this, EventArgs.Empty); + } + + internal void OnMapUpdate() + { + MapUpdate?.Invoke(this, EventArgs.Empty); + } + + internal void OnCaptchaReceived(string url) + { + CaptchaReceived?.Invoke(this, new CaptchaEventArgs(url)); + } + public event EventHandler AccessTokenUpdated; + public event EventHandler InventoryUpdate; + + public event EventHandler MapUpdate; + + /// + /// If you have successfully solved the captcha using VerifyChallegenge, + /// you can resume POGOLib by using . + /// + public event EventHandler CaptchaReceived; + #endregion + public void Dispose() { Dispose(true); - GC.SuppressFinalize(this); } protected virtual void Dispose(bool disposing) diff --git a/POGOLib.Official/Net/SessionState.cs b/POGOLib.Official/Net/SessionState.cs new file mode 100644 index 000000000..003b1e466 --- /dev/null +++ b/POGOLib.Official/Net/SessionState.cs @@ -0,0 +1,10 @@ +namespace POGOLib.Official.Net +{ + public enum SessionState + { + Started, + Stopped, + Paused, + Resumed + } +} diff --git a/POGOLib.Official/POGOLib.Official.csproj b/POGOLib.Official/POGOLib.Official.csproj index ab55383e1..804b184f5 100644 --- a/POGOLib.Official/POGOLib.Official.csproj +++ b/POGOLib.Official/POGOLib.Official.csproj @@ -40,6 +40,7 @@ + @@ -50,13 +51,16 @@ + + + @@ -65,6 +69,7 @@ + diff --git a/POGOLib.Official/Pokemon/HeartbeatDispatcher.cs b/POGOLib.Official/Pokemon/HeartbeatDispatcher.cs index 4d247b76d..167745f83 100644 --- a/POGOLib.Official/Pokemon/HeartbeatDispatcher.cs +++ b/POGOLib.Official/Pokemon/HeartbeatDispatcher.cs @@ -1,10 +1,8 @@ -using POGOLib.Official.Logging; -using POGOLib.Official.Net; -using System; -using System.Collections.Generic; -using System.Linq; +using System; using System.Threading; using System.Threading.Tasks; +using POGOLib.Official.Logging; +using POGOLib.Official.Net; namespace POGOLib.Official.Pokemon { @@ -23,8 +21,6 @@ internal class HeartbeatDispatcher private Task _heartbeatTask; - private bool _isActive; - internal HeartbeatDispatcher(Session session) { _session = session; @@ -35,7 +31,7 @@ internal HeartbeatDispatcher(Session session) /// private async Task CheckDispatch(TaskCompletionSource firstRefreshCompleted) { - while (!_heartbeatCancellation.IsCancellationRequested) + while (!_heartbeatCancellation.IsCancellationRequested && (_session.State == SessionState.Started || _session.State == SessionState.Resumed)) { var canRefresh = false; if (_session.GlobalSettings != null) @@ -93,21 +89,22 @@ private async Task CheckDispatch(TaskCompletionSource firstRefreshComplete // cancelled catch (OperationCanceledException) { - return; + break; } } + + Logger.Debug("Heartbeat got cancelled"); } - internal async Task StartDispatcher() + internal async Task StartDispatcherAsync() { if (_heartbeatTask != null) { throw new Exception("Heartbeat task already running"); } + var firstRefreshCompleted = new TaskCompletionSource(); _heartbeatCancellation = new CancellationTokenSource(); - - _isActive = true; _heartbeatTask = CheckDispatch(firstRefreshCompleted); // wait for first heartbeat to complete @@ -116,26 +113,13 @@ internal async Task StartDispatcher() internal void StopDispatcher() { - _isActive = false; - _heartbeatCancellation?.Cancel(); _heartbeatTask = null; } - internal void PauseDispatcher() - { - //_isActive = false; - } - - internal void ResumeDispatcher() - { - _isActive = true; - } - private async Task Dispatch() { - if (_isActive) - await _session.RpcClient.RefreshMapObjectsAsync(); + await _session.RpcClient.RefreshMapObjectsAsync(); } } } \ No newline at end of file diff --git a/POGOLib.Official/Pokemon/Inventory.cs b/POGOLib.Official/Pokemon/Inventory.cs index 5ebce7c6c..700962af4 100644 --- a/POGOLib.Official/Pokemon/Inventory.cs +++ b/POGOLib.Official/Pokemon/Inventory.cs @@ -1,9 +1,8 @@ -using Google.Protobuf.Collections; -using POGOProtos.Inventory; -using System; -using System.Collections.Generic; +using System.Collections.Generic; using System.Linq; -using System.Threading.Tasks; +using Google.Protobuf.Collections; +using POGOLib.Official.Net; +using POGOProtos.Inventory; namespace POGOLib.Official.Pokemon { @@ -12,8 +11,15 @@ namespace POGOLib.Official.Pokemon /// public class Inventory { + private readonly Session _session; + internal long LastInventoryTimestampMs; + public Inventory(Session session) + { + _session = session; + } + /// /// Gets the last received from PokémonGo.
/// Only use this if you know what you are doing. @@ -26,7 +32,8 @@ internal void RemoveInventoryItems(IEnumerable items) { InventoryItems.Remove(item); } - Update?.Invoke(this, EventArgs.Empty); + + _session.OnInventoryUpdate(); } internal void UpdateInventoryItems(InventoryDelta delta) @@ -129,9 +136,8 @@ internal void UpdateInventoryItems(InventoryDelta delta) InventoryItems.Remove(oldItem); } } - Update?.Invoke(this, EventArgs.Empty); - } - public event EventHandler Update; + _session.OnInventoryUpdate(); + } } } \ No newline at end of file diff --git a/POGOLib.Official/Pokemon/Map.cs b/POGOLib.Official/Pokemon/Map.cs index ffc5e7a6b..ccf663572 100644 --- a/POGOLib.Official/Pokemon/Map.cs +++ b/POGOLib.Official/Pokemon/Map.cs @@ -1,12 +1,11 @@ -using GeoCoordinatePortable; +using System; +using System.Collections.Generic; +using System.Linq; +using GeoCoordinatePortable; using Google.Protobuf.Collections; using POGOLib.Official.Net; using POGOProtos.Map; using POGOProtos.Map.Fort; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Pokemon { @@ -40,7 +39,7 @@ public RepeatedField Cells internal set { _cells = value; - Update?.Invoke(this, EventArgs.Empty); + _session.OnMapUpdate(); } } @@ -65,7 +64,5 @@ public List GetFortsSortedByDistance(Func filter = nul return sorted; } - - public event EventHandler Update; } } \ No newline at end of file diff --git a/POGOLib.Official/Pokemon/Player.cs b/POGOLib.Official/Pokemon/Player.cs index fc26f332c..3a9f500b4 100644 --- a/POGOLib.Official/Pokemon/Player.cs +++ b/POGOLib.Official/Pokemon/Player.cs @@ -1,20 +1,19 @@ -using GeoCoordinatePortable; +using System; +using System.Linq; +using GeoCoordinatePortable; +using POGOLib.Official.Net; using POGOProtos.Data; using POGOProtos.Data.Player; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Pokemon { public class Player { - internal Player(GeoCoordinate coordinate) + internal Player(Session session, GeoCoordinate coordinate) { Coordinate = coordinate; - Inventory = new Inventory(); - Inventory.Update += InventoryOnUpdate; + Inventory = new Inventory(session); + session.InventoryUpdate += InventoryOnUpdate; } /// @@ -42,8 +41,8 @@ internal Player(GeoCoordinate coordinate) /// Gets the of the beautiful object by PokémonGo. /// public PlayerStats Stats { get; private set; } - - public PlayerData Data { get; set; } + + public PlayerData Data { get; set; } /// /// Sets the of the . @@ -55,7 +54,7 @@ public void SetCoordinates(double latitude, double longitude, double altitude = { Coordinate = new GeoCoordinate(latitude, longitude, altitude); } - + /// /// Sets the of the . /// diff --git a/POGOLib.Official/Pokemon/Templates.cs b/POGOLib.Official/Pokemon/Templates.cs new file mode 100644 index 000000000..bdf36a039 --- /dev/null +++ b/POGOLib.Official/Pokemon/Templates.cs @@ -0,0 +1,70 @@ +namespace POGOLib.Official.Pokemon +{ + /* + public class Templates + { + private GetAssetDigestResponse _assetDigestResponse; + private DownloadItemTemplatesResponse _itemTemplatesResponse; + + public Templates(IDataCache templateDataCache) + { + _templateDataCache = templateDataCache; + _assetDigestResponse = LoadAssetDigest(); + _itemTemplatesResponse = LoadItemTemplates(); + } + + public RepeatedField AssetDigests => _assetDigestResponse?.Digest; + + public RepeatedField ItemTemplates + => _itemTemplatesResponse?.ItemTemplates; + + public string AssetDigestFile => "templates.asset-digests.dat"; + + public string ItemTemplatesFile => "templates.items.dat"; + + public void SetAssetDigests(GetAssetDigestResponse assetDigestResponse) + { + if (_assetDigestResponse == null || assetDigestResponse.TimestampMs > _assetDigestResponse.TimestampMs) + { + _assetDigestResponse = assetDigestResponse; + _templateDataCache.SaveTemplateDate(AssetDigestFile, _assetDigestResponse.ToByteString().ToByteArray()); + } + } + + public void SetItemTemplates(DownloadItemTemplatesResponse itemTemplatesResponse) + { + if (_itemTemplatesResponse == null || itemTemplatesResponse.TimestampMs > _itemTemplatesResponse.TimestampMs) + { + _itemTemplatesResponse = itemTemplatesResponse; + _templateDataCache.SaveTemplateDate(ItemTemplatesFile, _itemTemplatesResponse.ToByteString().ToByteArray()); + } + } + + private GetAssetDigestResponse LoadAssetDigest() + { + if (_templateDataCache.HasTemplateData(AssetDigestFile)) + { + var bytes = _templateDataCache.GetTemplateDataCache(AssetDigestFile); + if (bytes?.Any() ?? false) + { + return GetAssetDigestResponse.Parser.ParseFrom(bytes); + } + } + return null; + } + + private DownloadItemTemplatesResponse LoadItemTemplates() + { + if (_templateDataCache.HasTemplateData(ItemTemplatesFile)) + { + var bytes = _templateDataCache.GetTemplateDataCache(ItemTemplatesFile); + if (bytes?.Any() ?? false) + { + return DownloadItemTemplatesResponse.Parser.ParseFrom(bytes); + } + } + return null; + } + + }*/ +} \ No newline at end of file diff --git a/POGOLib.Official/Properties/AssemblyInfo.cs b/POGOLib.Official/Properties/AssemblyInfo.cs index ca8831978..e4c1bed18 100644 --- a/POGOLib.Official/Properties/AssemblyInfo.cs +++ b/POGOLib.Official/Properties/AssemblyInfo.cs @@ -26,5 +26,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +[assembly: AssemblyVersion("1.4.0.0")] +[assembly: AssemblyFileVersion("1.4.0.0")] diff --git a/POGOLib.Official/Util/Data/IDataCache.cs b/POGOLib.Official/Util/Data/IDataCache.cs index 8f343e1d4..5f29bcbed 100644 --- a/POGOLib.Official/Util/Data/IDataCache.cs +++ b/POGOLib.Official/Util/Data/IDataCache.cs @@ -1,9 +1,5 @@ using Google.Protobuf; using POGOProtos.Networking.Responses; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util.Data { diff --git a/POGOLib.Official/Util/Data/MemoryDataCache.cs b/POGOLib.Official/Util/Data/MemoryDataCache.cs index d1ae07d0b..5ed76def1 100644 --- a/POGOLib.Official/Util/Data/MemoryDataCache.cs +++ b/POGOLib.Official/Util/Data/MemoryDataCache.cs @@ -1,8 +1,5 @@ -using Google.Protobuf; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; +using Google.Protobuf; namespace POGOLib.Official.Util.Data { diff --git a/POGOLib.Official/Util/Device/DeviceInfoUtil.cs b/POGOLib.Official/Util/Device/DeviceInfoUtil.cs index 53f9dd64e..3eabee246 100644 --- a/POGOLib.Official/Util/Device/DeviceInfoUtil.cs +++ b/POGOLib.Official/Util/Device/DeviceInfoUtil.cs @@ -1,9 +1,5 @@ using POGOLib.Official.Extensions; using POGOLib.Official.Net; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; using static POGOProtos.Networking.Envelopes.Signature.Types; namespace POGOLib.Official.Util.Device diff --git a/POGOLib.Official/Util/Encryption/Legacy/NiaHashLegacy.cs b/POGOLib.Official/Util/Encryption/Legacy/NiaHashLegacy.cs index 74bdf9130..3bf076312 100644 --- a/POGOLib.Official/Util/Encryption/Legacy/NiaHashLegacy.cs +++ b/POGOLib.Official/Util/Encryption/Legacy/NiaHashLegacy.cs @@ -1,10 +1,7 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util.Encryption.Legacy -{ +{ /// /// This is the legacy NiaHash used by POGOLib. /// @@ -81,12 +78,12 @@ private static ulong Hash(byte[] input) // copy tail, pad with zeroes var tail = new byte[128]; var tailSize = len % 128; - Buffer.BlockCopy(input, (int)(len - tailSize), tail, 0, (int)tailSize); + Buffer.BlockCopy(input, (int) (len - tailSize), tail, 0, (int) tailSize); UInt128 hash; - hash = numChunks != 0 - ? hash_chunk(input, 128, 0) + hash = numChunks != 0 + ? hash_chunk(input, 128, 0) : hash_chunk(tail, tailSize, 0); hash += RoundMagic; diff --git a/POGOLib.Official/Util/Encryption/Legacy/PCryptLegacy.cs b/POGOLib.Official/Util/Encryption/Legacy/PCryptLegacy.cs index 1449a5566..ffcd8ec8a 100644 --- a/POGOLib.Official/Util/Encryption/Legacy/PCryptLegacy.cs +++ b/POGOLib.Official/Util/Encryption/Legacy/PCryptLegacy.cs @@ -1,8 +1,6 @@ using System; -using System.Collections.Generic; using System.Collections.ObjectModel; using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util.Encryption.Legacy { diff --git a/POGOLib.Official/Util/Encryption/Legacy/ShufflesLegacy.cs b/POGOLib.Official/Util/Encryption/Legacy/ShufflesLegacy.cs index 082e6ac79..0ac442527 100644 --- a/POGOLib.Official/Util/Encryption/Legacy/ShufflesLegacy.cs +++ b/POGOLib.Official/Util/Encryption/Legacy/ShufflesLegacy.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using POGOLib.Official.Util.Hash; namespace POGOLib.Official.Util.Encryption.Legacy { diff --git a/POGOLib.Official/Util/Encryption/PokeHash/PCryptPokeHash.cs b/POGOLib.Official/Util/Encryption/PokeHash/PCryptPokeHash.cs index 73b0a5384..d55d7f1e7 100644 --- a/POGOLib.Official/Util/Encryption/PokeHash/PCryptPokeHash.cs +++ b/POGOLib.Official/Util/Encryption/PokeHash/PCryptPokeHash.cs @@ -1,224 +1,102 @@ -using POGOLib.Official.Util.Encryption.Legacy; -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Threading.Tasks; +using System; namespace POGOLib.Official.Util.Encryption.PokeHash { /// /// This is the PCrypt used by POGOLib. It should always match the used PokeHash version. /// - /// Android version: 0.51.0 - /// IOS version: 1.21.0 + /// Android version: 0.59.1 + /// IOS version: 1.29.1 /// internal static class PCryptPokeHash { - private static byte Rot18(byte val, int bits) - { - return (byte)(((val << bits) | (val >> (8 - bits))) & 0xff); - } - - private static byte GenerateRand(ref uint rand) - { - rand = rand * 0x41c64e6d + 12345; - return (byte)((rand >> 16) & 0xff); - } - - private static byte[] Cipher8FromIv(byte[] iv) - { - var ret = new byte[256]; - for (var i = 0; i < 8; i++) - { - for (var j = 0; j < 32; j++) - { - ret[32 * i * j] = Rot18(iv[j], i); - } - } - return ret; - } - - private static byte[] Cipher8FromRand(ref uint rand) - { - var ret = new byte[256]; - for (var i = 0; i < 256; i++) - { - ret[i] = GenerateRand(ref rand); - } - return ret; - } - - private static byte MakeIntegrityByte(byte b) - { - return (byte)(b & 0xe3 | 0x10); - } - - public static byte[] Encrypt(byte[] input, uint ms) - { - var ct = new CipherText(input, ms); - var iv = Cipher8FromRand(ref ms); - - //encrypt - foreach (var bytes in ct.Content) - { - for (var j = 0; j < 256; j++) - { - bytes[j] ^= iv[j]; - } - - var temp2 = new uint[0x100 / 4]; - Buffer.BlockCopy(bytes, 0, temp2, 0, 0x100); - ShufflesLegacy.Shuffle2(temp2); - - Buffer.BlockCopy(temp2, 0, iv, 0, 0x100); - Buffer.BlockCopy(temp2, 0, bytes, 0, 0x100); - } - - return ct.GetBytes(ref ms); - } - - //this returns an empty buffer if error - public static byte[] Decrypt(byte[] input, out int length) - { - int version, len = input.Length; - if (len < 261) - { - length = 0; - return new byte[] { }; - } - - var modSize = len % 256; - switch (modSize) - { - case 32: - version = 1; - break; - case 33: - version = 2; - break; - case 5: - version = 3; - break; - default: - length = 0; return new byte[] { }; - } - - byte[] cipher8, output; - int outputLen; - switch (version) - { - case 1: - outputLen = len - 32; - output = new byte[outputLen]; - Buffer.BlockCopy(input, 32, output, 0, outputLen); - cipher8 = Cipher8FromIv(input); - break; - case 2: - outputLen = len - 33; - output = new byte[outputLen]; - Buffer.BlockCopy(input, 32, output, 0, outputLen); - cipher8 = Cipher8FromIv(input); - break; - default: - outputLen = len - 5; - output = new byte[outputLen]; - Buffer.BlockCopy(input, 4, output, 0, outputLen); - var tmp = new byte[4]; - Buffer.BlockCopy(input, 0, tmp, 0, 4); - Array.Reverse(tmp); - var ms = BitConverter.ToUInt32(tmp, 0); - cipher8 = Cipher8FromRand(ref ms); - if (input[len - 1] != MakeIntegrityByte(GenerateRand(ref ms))) { length = 0; return new byte[] { }; } - break; - } - - var outputcontent = new Collection(); - - //break into chunks of 256 - var roundedsize = (outputLen + 255) / 256; //round up - for (var i = 0; i < roundedsize; i++) - outputcontent.Add(new byte[256]); - for (var i = 0; i < outputLen; i++) - outputcontent[i / 256][i % 256] = output[i]; - - foreach (var bytes in outputcontent) - { - var temp2 = new uint[0x100 / 4]; - var temp3 = new uint[0x100 / 4]; - Buffer.BlockCopy(bytes, 0, temp2, 0, 0x100); - Buffer.BlockCopy(temp2, 0, temp3, 0, 0x100); - - if (version == 1) - ShufflesLegacy.Unshuffle(temp2); - else - ShufflesLegacy.Unshuffle2(temp2); - - Buffer.BlockCopy(temp2, 0, bytes, 0, 0x100); - for (var j = 0; j < 256; j++) - { - bytes[j] ^= cipher8[j]; - } - Buffer.BlockCopy(temp3, 0, cipher8, 0, 0x100); - } - - var ret = new byte[outputLen]; - for (var i = 0; i < outputcontent.Count; i++) - { - Buffer.BlockCopy(outputcontent[i], 0, ret, i * 256, 0x100); - } - length = outputLen - ret.Last(); - return ret; - } - - private class CipherText - { - private readonly byte[] _prefix; - private readonly int _totalsize; - - public readonly Collection Content; - - private static byte[] IntToBytes(int x) - { - return BitConverter.GetBytes(x); - } - - public CipherText(byte[] input, uint ms) - { - var inputlen = input.Length; - _prefix = new byte[32]; - - //allocate blocks of 256 bytes - Content = new Collection(); - var roundedsize = inputlen + (256 - (inputlen % 256)); - for (var i = 0; i < roundedsize / 256; i++) - Content.Add(new byte[256]); - _totalsize = roundedsize + 5; - - //first 32 bytes, pcrypt.c:68 - _prefix = IntToBytes((int)ms); - Array.Reverse(_prefix); - - //split input into 256 - for (var i = 0; i < inputlen; i++) Content[i / 256][i % 256] = input[i]; - - //pcrypt.c:75 - Content.Last()[Content.Last().Length - 1] = (byte)(256 - (input.Length % 256)); - } - - public byte[] GetBytes(ref uint ms) - { - var ret = new byte[_totalsize]; - Buffer.BlockCopy(_prefix, 0, ret, 0, _prefix.Length); - var offset = _prefix.Length; - foreach (var bytes in Content) - { - Buffer.BlockCopy(bytes, 0, ret, offset, bytes.Length); - offset += bytes.Length; - } - ret[ret.Length - 1] = MakeIntegrityByte(GenerateRand(ref ms)); - return ret; - } - } + public static byte[] KEY = new byte[] + { + (byte) 0x4F, (byte) 0xEB, (byte) 0x1C, (byte) 0xA5, (byte) 0xF6, (byte) 0x1A, (byte) 0x67, (byte) 0xCE, + (byte) 0x43, (byte) 0xF3, (byte) 0xF0, (byte) 0x0C, (byte) 0xB1, (byte) 0x23, (byte) 0x88, (byte) 0x35, + (byte) 0xE9, (byte) 0x8B, (byte) 0xE8, (byte) 0x39, (byte) 0xD8, (byte) 0x89, (byte) 0x8F, (byte) 0x5A, + (byte) 0x3B, (byte) 0x51, (byte) 0x2E, (byte) 0xA9, (byte) 0x47, (byte) 0x38, (byte) 0xC4, (byte) 0x14 + }; + + public static object _twoFish { get; private set; } + + public static byte[] MakeIv(Rand rand) + { + byte[] iv = new byte[TwoFish.BLOCK_SIZE]; + for (int i = 0; i < iv.Length; i++) + { + iv[i] = rand.Next(); + } + return iv; + } + + public static byte MakeIntegrityByte(Rand rand) + { + return 0x21; + } + + /** + * Encrypts the given signature + * + * @param input input data + * @param msSinceStart time since start + * @return encrypted signature + */ + public static byte[] Encrypt(byte[] input, uint msSinceStart) + { + try + { + object[] key = TwoFish.MakeKey(KEY); + + Rand rand = new Rand(msSinceStart); + byte[] iv = MakeIv(rand); + int blockCount = (input.Length + 256) / 256; + int outputSize = (blockCount * 256) + 5; + byte[] output = new byte[outputSize]; + + output[0] = (byte)(msSinceStart >> 24); + output[1] = (byte)(msSinceStart >> 16); + output[2] = (byte)(msSinceStart >> 8); + output[3] = (byte)msSinceStart; + + Array.Copy(input, 0, output, 4, input.Length); + output[outputSize - 2] = (byte)(256 - input.Length % 256); + + for (int offset = 0; offset < blockCount * 256; offset += TwoFish.BLOCK_SIZE) + { + for (int i = 0; i < TwoFish.BLOCK_SIZE; i++) + { + output[4 + offset + i] ^= iv[i]; + } + + byte[] block = TwoFish.blockEncrypt(output, offset + 4, key); + Array.Copy(block, 0, output, offset + 4, block.Length); + Array.Copy(output, 4 + offset, iv, 0, TwoFish.BLOCK_SIZE); + } + + output[outputSize - 1] = MakeIntegrityByte(rand); + return output; + } + catch (Exception) + { + return null; + } + } + + public class Rand + { + private long state; + + public Rand(long state) + { + this.state = state; + } + + public byte Next() + { + state = (state * 0x41C64E6D) + 0x3039; + return (byte)((state >> 16) & 0xFF); + } + } } } diff --git a/POGOLib.Official/Util/Encryption/PokeHash/TwoFish.cs b/POGOLib.Official/Util/Encryption/PokeHash/TwoFish.cs new file mode 100644 index 000000000..9d1086153 --- /dev/null +++ b/POGOLib.Official/Util/Encryption/PokeHash/TwoFish.cs @@ -0,0 +1,603 @@ +using System; + +namespace POGOLib.Official.Util.Encryption.PokeHash +{ + public static class TwoFish + { + public static int BLOCK_SIZE = 16; + private static int ROUNDS = 16; + + private static int INPUT_WHITEN = 0; + private static int OUTPUT_WHITEN = INPUT_WHITEN + BLOCK_SIZE / 4; + private static int ROUND_SUBKEYS = OUTPUT_WHITEN + BLOCK_SIZE / 4; + + private static int SK_STEP = 0x02020202; + private static int SK_BUMP = 0x01010101; + private static int SK_ROTL = 9; + + /** + * Fixed 8x8 permutation S-boxes + */ + private static byte[][] P = new byte[2][] + { + new byte[256]{ + (byte) 0xA9, (byte) 0x67, (byte) 0xB3, (byte) 0xE8, + (byte) 0x04, (byte) 0xFD, (byte) 0xA3, (byte) 0x76, + (byte) 0x9A, (byte) 0x92, (byte) 0x80, (byte) 0x78, + (byte) 0xE4, (byte) 0xDD, (byte) 0xD1, (byte) 0x38, + (byte) 0x0D, (byte) 0xC6, (byte) 0x35, (byte) 0x98, + (byte) 0x18, (byte) 0xF7, (byte) 0xEC, (byte) 0x6C, + (byte) 0x43, (byte) 0x75, (byte) 0x37, (byte) 0x26, + (byte) 0xFA, (byte) 0x13, (byte) 0x94, (byte) 0x48, + (byte) 0xF2, (byte) 0xD0, (byte) 0x8B, (byte) 0x30, + (byte) 0x84, (byte) 0x54, (byte) 0xDF, (byte) 0x23, + (byte) 0x19, (byte) 0x5B, (byte) 0x3D, (byte) 0x59, + (byte) 0xF3, (byte) 0xAE, (byte) 0xA2, (byte) 0x82, + (byte) 0x63, (byte) 0x01, (byte) 0x83, (byte) 0x2E, + (byte) 0xD9, (byte) 0x51, (byte) 0x9B, (byte) 0x7C, + (byte) 0xA6, (byte) 0xEB, (byte) 0xA5, (byte) 0xBE, + (byte) 0x16, (byte) 0x0C, (byte) 0xE3, (byte) 0x61, + (byte) 0xC0, (byte) 0x8C, (byte) 0x3A, (byte) 0xF5, + (byte) 0x73, (byte) 0x2C, (byte) 0x25, (byte) 0x0B, + (byte) 0xBB, (byte) 0x4E, (byte) 0x89, (byte) 0x6B, + (byte) 0x53, (byte) 0x6A, (byte) 0xB4, (byte) 0xF1, + (byte) 0xE1, (byte) 0xE6, (byte) 0xBD, (byte) 0x45, + (byte) 0xE2, (byte) 0xF4, (byte) 0xB6, (byte) 0x66, + (byte) 0xCC, (byte) 0x95, (byte) 0x03, (byte) 0x56, + (byte) 0xD4, (byte) 0x1C, (byte) 0x1E, (byte) 0xD7, + (byte) 0xFB, (byte) 0xC3, (byte) 0x8E, (byte) 0xB5, + (byte) 0xE9, (byte) 0xCF, (byte) 0xBF, (byte) 0xBA, + (byte) 0xEA, (byte) 0x77, (byte) 0x39, (byte) 0xAF, + (byte) 0x33, (byte) 0xC9, (byte) 0x62, (byte) 0x71, + (byte) 0x81, (byte) 0x79, (byte) 0x09, (byte) 0xAD, + (byte) 0x24, (byte) 0xCD, (byte) 0xF9, (byte) 0xD8, + (byte) 0xE5, (byte) 0xC5, (byte) 0xB9, (byte) 0x4D, + (byte) 0x44, (byte) 0x08, (byte) 0x86, (byte) 0xE7, + (byte) 0xA1, (byte) 0x1D, (byte) 0xAA, (byte) 0xED, + (byte) 0x06, (byte) 0x70, (byte) 0xB2, (byte) 0xD2, + (byte) 0x41, (byte) 0x7B, (byte) 0xA0, (byte) 0x11, + (byte) 0x31, (byte) 0xC2, (byte) 0x27, (byte) 0x90, + (byte) 0x20, (byte) 0xF6, (byte) 0x60, (byte) 0xFF, + (byte) 0x96, (byte) 0x5C, (byte) 0xB1, (byte) 0xAB, + (byte) 0x9E, (byte) 0x9C, (byte) 0x52, (byte) 0x1B, + (byte) 0x5F, (byte) 0x93, (byte) 0x0A, (byte) 0xEF, + (byte) 0x91, (byte) 0x85, (byte) 0x49, (byte) 0xEE, + (byte) 0x2D, (byte) 0x4F, (byte) 0x8F, (byte) 0x3B, + (byte) 0x47, (byte) 0x87, (byte) 0x6D, (byte) 0x46, + (byte) 0xD6, (byte) 0x3E, (byte) 0x69, (byte) 0x64, + (byte) 0x2A, (byte) 0xCE, (byte) 0xCB, (byte) 0x2F, + (byte) 0xFC, (byte) 0x97, (byte) 0x05, (byte) 0x7A, + (byte) 0xAC, (byte) 0x7F, (byte) 0xD5, (byte) 0x1A, + (byte) 0x4B, (byte) 0x0E, (byte) 0xA7, (byte) 0x5A, + (byte) 0x28, (byte) 0x14, (byte) 0x3F, (byte) 0x29, + (byte) 0x88, (byte) 0x3C, (byte) 0x4C, (byte) 0x02, + (byte) 0xB8, (byte) 0xDA, (byte) 0xB0, (byte) 0x17, + (byte) 0x55, (byte) 0x1F, (byte) 0x8A, (byte) 0x7D, + (byte) 0x57, (byte) 0xC7, (byte) 0x8D, (byte) 0x74, + (byte) 0xB7, (byte) 0xC4, (byte) 0x9F, (byte) 0x72, + (byte) 0x7E, (byte) 0x15, (byte) 0x22, (byte) 0x12, + (byte) 0x58, (byte) 0x07, (byte) 0x99, (byte) 0x34, + (byte) 0x6E, (byte) 0x50, (byte) 0xDE, (byte) 0x68, + (byte) 0x65, (byte) 0xBC, (byte) 0xDB, (byte) 0xF8, + (byte) 0xC8, (byte) 0xA8, (byte) 0x2B, (byte) 0x40, + (byte) 0xDC, (byte) 0xFE, (byte) 0x32, (byte) 0xA4, + (byte) 0xCA, (byte) 0x10, (byte) 0x21, (byte) 0xF0, + (byte) 0xD3, (byte) 0x5D, (byte) 0x0F, (byte) 0x00, + (byte) 0x6F, (byte) 0x9D, (byte) 0x36, (byte) 0x42, + (byte) 0x4A, (byte) 0x5E, (byte) 0xC1, (byte) 0xE0 + }, + new byte[256] { + (byte) 0x75, (byte) 0xF3, (byte) 0xC6, (byte) 0xF4, + (byte) 0xDB, (byte) 0x7B, (byte) 0xFB, (byte) 0xC8, + (byte) 0x4A, (byte) 0xD3, (byte) 0xE6, (byte) 0x6B, + (byte) 0x45, (byte) 0x7D, (byte) 0xE8, (byte) 0x4B, + (byte) 0xD6, (byte) 0x32, (byte) 0xD8, (byte) 0xFD, + (byte) 0x37, (byte) 0x71, (byte) 0xF1, (byte) 0xE1, + (byte) 0x30, (byte) 0x0F, (byte) 0xF8, (byte) 0x1B, + (byte) 0x87, (byte) 0xFA, (byte) 0x06, (byte) 0x3F, + (byte) 0x5E, (byte) 0xBA, (byte) 0xAE, (byte) 0x5B, + (byte) 0x8A, (byte) 0x00, (byte) 0xBC, (byte) 0x9D, + (byte) 0x6D, (byte) 0xC1, (byte) 0xB1, (byte) 0x0E, + (byte) 0x80, (byte) 0x5D, (byte) 0xD2, (byte) 0xD5, + (byte) 0xA0, (byte) 0x84, (byte) 0x07, (byte) 0x14, + (byte) 0xB5, (byte) 0x90, (byte) 0x2C, (byte) 0xA3, + (byte) 0xB2, (byte) 0x73, (byte) 0x4C, (byte) 0x54, + (byte) 0x92, (byte) 0x74, (byte) 0x36, (byte) 0x51, + (byte) 0x38, (byte) 0xB0, (byte) 0xBD, (byte) 0x5A, + (byte) 0xFC, (byte) 0x60, (byte) 0x62, (byte) 0x96, + (byte) 0x6C, (byte) 0x42, (byte) 0xF7, (byte) 0x10, + (byte) 0x7C, (byte) 0x28, (byte) 0x27, (byte) 0x8C, + (byte) 0x13, (byte) 0x95, (byte) 0x9C, (byte) 0xC7, + (byte) 0x24, (byte) 0x46, (byte) 0x3B, (byte) 0x70, + (byte) 0xCA, (byte) 0xE3, (byte) 0x85, (byte) 0xCB, + (byte) 0x11, (byte) 0xD0, (byte) 0x93, (byte) 0xB8, + (byte) 0xA6, (byte) 0x83, (byte) 0x20, (byte) 0xFF, + (byte) 0x9F, (byte) 0x77, (byte) 0xC3, (byte) 0xCC, + (byte) 0x03, (byte) 0x6F, (byte) 0x08, (byte) 0xBF, + (byte) 0x40, (byte) 0xE7, (byte) 0x2B, (byte) 0xE2, + (byte) 0x79, (byte) 0x0C, (byte) 0xAA, (byte) 0x82, + (byte) 0x41, (byte) 0x3A, (byte) 0xEA, (byte) 0xB9, + (byte) 0xE4, (byte) 0x9A, (byte) 0xA4, (byte) 0x97, + (byte) 0x7E, (byte) 0xDA, (byte) 0x7A, (byte) 0x17, + (byte) 0x66, (byte) 0x94, (byte) 0xA1, (byte) 0x1D, + (byte) 0x3D, (byte) 0xF0, (byte) 0xDE, (byte) 0xB3, + (byte) 0x0B, (byte) 0x72, (byte) 0xA7, (byte) 0x1C, + (byte) 0xEF, (byte) 0xD1, (byte) 0x53, (byte) 0x3E, + (byte) 0x8F, (byte) 0x33, (byte) 0x26, (byte) 0x5F, + (byte) 0xEC, (byte) 0x76, (byte) 0x2A, (byte) 0x49, + (byte) 0x81, (byte) 0x88, (byte) 0xEE, (byte) 0x21, + (byte) 0xC4, (byte) 0x1A, (byte) 0xEB, (byte) 0xD9, + (byte) 0xC5, (byte) 0x39, (byte) 0x99, (byte) 0xCD, + (byte) 0xAD, (byte) 0x31, (byte) 0x8B, (byte) 0x01, + (byte) 0x18, (byte) 0x23, (byte) 0xDD, (byte) 0x1F, + (byte) 0x4E, (byte) 0x2D, (byte) 0xF9, (byte) 0x48, + (byte) 0x4F, (byte) 0xF2, (byte) 0x65, (byte) 0x8E, + (byte) 0x78, (byte) 0x5C, (byte) 0x58, (byte) 0x19, + (byte) 0x8D, (byte) 0xE5, (byte) 0x98, (byte) 0x57, + (byte) 0x67, (byte) 0x7F, (byte) 0x05, (byte) 0x64, + (byte) 0xAF, (byte) 0x63, (byte) 0xB6, (byte) 0xFE, + (byte) 0xF5, (byte) 0xB7, (byte) 0x3C, (byte) 0xA5, + (byte) 0xCE, (byte) 0xE9, (byte) 0x68, (byte) 0x44, + (byte) 0xE0, (byte) 0x4D, (byte) 0x43, (byte) 0x69, + (byte) 0x29, (byte) 0x2E, (byte) 0xAC, (byte) 0x15, + (byte) 0x59, (byte) 0xA8, (byte) 0x0A, (byte) 0x9E, + (byte) 0x6E, (byte) 0x47, (byte) 0xDF, (byte) 0x34, + (byte) 0x35, (byte) 0x6A, (byte) 0xCF, (byte) 0xDC, + (byte) 0x22, (byte) 0xC9, (byte) 0xC0, (byte) 0x9B, + (byte) 0x89, (byte) 0xD4, (byte) 0xED, (byte) 0xAB, + (byte) 0x12, (byte) 0xA2, (byte) 0x0D, (byte) 0x52, + (byte) 0xBB, (byte) 0x02, (byte) 0x2F, (byte) 0xA9, + (byte) 0xD7, (byte) 0x61, (byte) 0x1E, (byte) 0xB4, + (byte) 0x50, (byte) 0x04, (byte) 0xF6, (byte) 0xC2, + (byte) 0x16, (byte) 0x25, (byte) 0x86, (byte) 0x56, + (byte) 0x55, (byte) 0x09, (byte) 0xBE, (byte) 0x91 + } + }; + + /** + * Define the fixed p0/p1 permutations used in keyed S-box lookup. + * By changing the following constant definitions, the S-boxes will + * automatically get changed in the Twofish engine. + */ + private static int P_00 = 1; + private static int P_01 = 0; + private static int P_02 = 0; + private static int P_03 = P_01 ^ 1; + private static int P_04 = 1; + + private static int P_10 = 0; + private static int P_11 = 0; + private static int P_12 = 1; + private static int P_13 = P_11 ^ 1; + private static int P_14 = 0; + + private static int P_20 = 1; + private static int P_21 = 1; + private static int P_22 = 0; + private static int P_23 = P_21 ^ 1; + private static int P_24 = 0; + + private static int P_30 = 0; + private static int P_31 = 1; + private static int P_32 = 1; + private static int P_33 = P_31 ^ 1; + private static int P_34 = 1; + + /** + * Primitive polynomial for GF(256) + */ + private static int GF256_FDBK_2 = 0x169 / 2; + private static int GF256_FDBK_4 = 0x169 / 4; + + /** + * MDS matrix + */ + private static int[][] MDS = new int[4][]; + + private static int RS_GF_FDBK = 0x14D; + + static TwoFish() + { + int[] m1 = new int[2]; + int[] mxArray = new int[2]; + int[] myArray = new int[2]; + int first; + int second = 0; + + for (int i = 0; i < MDS.Length; i++) + { + MDS[i] = new int[256]; + } + + for (first = 0; first < 256; first++) + { + second = P[0][first] & 0xFF; + m1[0] = second; + mxArray[0] = mxX(second) & 0xFF; + myArray[0] = mxY(second) & 0xFF; + + second = P[1][first] & 0xFF; + m1[1] = second; + mxArray[1] = mxX(second) & 0xFF; + myArray[1] = mxY(second) & 0xFF; + + MDS[0][first] = m1[P_00] + | mxArray[P_00] << 8 + | myArray[P_00] << 16 + | myArray[P_00] << 24; + MDS[1][first] = myArray[P_10] + | myArray[P_10] << 8 + | mxArray[P_10] << 16 + | m1[P_10] << 24; + MDS[2][first] = mxArray[P_20] + | myArray[P_20] << 8 + | m1[P_20] << 16 + | myArray[P_20] << 24; + MDS[3][first] = mxArray[P_30] + | m1[P_30] << 8 + | myArray[P_30] << 16 + | mxArray[P_30] << 24; + } + } + + private static int lfsr1(int x) + { + return (x >> 1) ^ ((x & 0x01) != 0 ? GF256_FDBK_2 : 0); + } + + private static int lfsr2(int x) + { + return (x >> 2) ^ ((x & 0x02) != 0 ? GF256_FDBK_2 : 0) ^ ((x & 0x01) != 0 ? GF256_FDBK_4 : 0); + } + + private static int mxX(int x) + { + return x ^ lfsr2(x); + } + + private static int mxY(int x) + { + return x ^ lfsr1(x) ^ lfsr2(x); + } + + /** + * Expand a user-supplied key material into a session key. + * + * @param k The 64/128/192/256-bit user-key to use. + * @return This cipher's round keys. + * @throws InvalidKeyException If the key is invalid. + */ + public static object[] MakeKey(byte[] k) + { + if (k == null) + throw new Exception("Empty key"); + int length = k.Length; + if (!(length == 8 || length == 16 || length == 24 || length == 32)) + throw new Exception("Incorrect key length"); + + int k64Cnt = length / 8; + int subkeyCnt = ROUND_SUBKEYS + 2 * ROUNDS; + int[] k32e = new int[4]; + int[] k32o = new int[4]; + int[] sBoxKey = new int[4]; + int i, j, offset = 0; + for (i = 0, j = k64Cnt - 1; i < 4 && offset < length; i++, j--) + { + k32e[i] = (k[offset++] & 0xFF) + | (k[offset++] & 0xFF) << 8 + | (k[offset++] & 0xFF) << 16 + | (k[offset++] & 0xFF) << 24; + k32o[i] = (k[offset++] & 0xFF) + | (k[offset++] & 0xFF) << 8 + | (k[offset++] & 0xFF) << 16 + | (k[offset++] & 0xFF) << 24; + sBoxKey[j] = rsMdsEncode(k32e[i], k32o[i]); + } + int q, A, B; + int[] subKeys = new int[subkeyCnt]; + for (i = q = 0; i < subkeyCnt / 2; i++, q += SK_STEP) + { + A = f32(k64Cnt, q, k32e); + B = f32(k64Cnt, q + SK_BUMP, k32o); + B = B << 8 | RightUShift(B, 24); + A += B; + subKeys[2 * i] = A; + A += B; + subKeys[2 * i + 1] = A << SK_ROTL | RightUShift(A, (32 - SK_ROTL)); + } + int k0 = sBoxKey[0]; + int k1 = sBoxKey[1]; + int k2 = sBoxKey[2]; + int k3 = sBoxKey[3]; + int b0, b1, b2, b3; + int[] sBox = new int[4 * 256]; + for (i = 0; i < 256; i++) + { + b0 = b1 = b2 = b3 = i; + + int val = k64Cnt & 3; + + if (val == 1) + { + sBox[2 * i] = MDS[0][(P[P_01][b0] & 0xFF) ^ _b0(k0)]; + sBox[2 * i + 1] = MDS[1][(P[P_11][b1] & 0xFF) ^ _b1(k0)]; + sBox[0x200 + 2 * i] = MDS[2][(P[P_21][b2] & 0xFF) ^ _b2(k0)]; + sBox[0x200 + 2 * i + 1] = MDS[3][(P[P_31][b3] & 0xFF) ^ _b3(k0)]; + } + switch (k64Cnt & 3) + { + case 1: + sBox[2 * i] = MDS[0][(P[P_01][b0] & 0xFF) ^ _b0(k0)]; + sBox[2 * i + 1] = MDS[1][(P[P_11][b1] & 0xFF) ^ _b1(k0)]; + sBox[0x200 + 2 * i] = MDS[2][(P[P_21][b2] & 0xFF) ^ _b2(k0)]; + sBox[0x200 + 2 * i + 1] = MDS[3][(P[P_31][b3] & 0xFF) ^ _b3(k0)]; + break; + case 0: + b0 = (P[P_04][b0] & 0xFF) ^ _b0(k3); + b1 = (P[P_14][b1] & 0xFF) ^ _b1(k3); + b2 = (P[P_24][b2] & 0xFF) ^ _b2(k3); + b3 = (P[P_34][b3] & 0xFF) ^ _b3(k3); + + + b0 = (P[P_03][b0] & 0xFF) ^ _b0(k2); + b1 = (P[P_13][b1] & 0xFF) ^ _b1(k2); + b2 = (P[P_23][b2] & 0xFF) ^ _b2(k2); + b3 = (P[P_33][b3] & 0xFF) ^ _b3(k2); + + + sBox[2 * i] = MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) ^ _b0(k1)] & 0xFF) ^ _b0(k0)]; + sBox[2 * i + 1] = MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) ^ _b1(k1)] & 0xFF) ^ _b1(k0)]; + sBox[0x200 + 2 * i] = MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) ^ _b2(k1)] & 0xFF) ^ _b2(k0)]; + sBox[0x200 + 2 * i + 1] = MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) ^ _b3(k1)] & 0xFF) ^ _b3(k0)]; + break; + case 3: + b0 = (P[P_03][b0] & 0xFF) ^ _b0(k2); + b1 = (P[P_13][b1] & 0xFF) ^ _b1(k2); + b2 = (P[P_23][b2] & 0xFF) ^ _b2(k2); + b3 = (P[P_33][b3] & 0xFF) ^ _b3(k2); + + sBox[2 * i] = MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) ^ _b0(k1)] & 0xFF) ^ _b0(k0)]; + sBox[2 * i + 1] = MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) ^ _b1(k1)] & 0xFF) ^ _b1(k0)]; + sBox[0x200 + 2 * i] = MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) ^ _b2(k1)] & 0xFF) ^ _b2(k0)]; + sBox[0x200 + 2 * i + 1] = MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) ^ _b3(k1)] & 0xFF) ^ _b3(k0)]; + break; + case 2: + sBox[2 * i] = MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) ^ _b0(k1)] & 0xFF) ^ _b0(k0)]; + sBox[2 * i + 1] = MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) ^ _b1(k1)] & 0xFF) ^ _b1(k0)]; + sBox[0x200 + 2 * i] = MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) ^ _b2(k1)] & 0xFF) ^ _b2(k0)]; + sBox[0x200 + 2 * i + 1] = MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) ^ _b3(k1)] & 0xFF) ^ _b3(k0)]; + break; + } + } + return new object[] { sBox, subKeys }; + } + + public static int RightUShift(int val, int shift) + { + return (int)((uint)val >> shift); + } + + /** + * Encrypt exactly one block of plaintext. + * + * @param in The plaintext. + * @param inOffset Index of in from which to start considering data. + * @param sessionKey The session key to use for encryption. + * @return The ciphertext generated from a plaintext using the session key. + */ + public static byte[] blockEncrypt(byte[] bArray, int inOffset, Object sessionKey) + { + Object[] sk = (Object[])sessionKey; + int[] sBox = (int[])sk[0]; + int[] sKey = (int[])sk[1]; + + int x0 = (bArray[inOffset++] & 0xFF) + | (bArray[inOffset++] & 0xFF) << 8 + | (bArray[inOffset++] & 0xFF) << 16 + | (bArray[inOffset++] & 0xFF) << 24; + int x1 = (bArray[inOffset++] & 0xFF) + | (bArray[inOffset++] & 0xFF) << 8 + | (bArray[inOffset++] & 0xFF) << 16 + | (bArray[inOffset++] & 0xFF) << 24; + int x2 = (bArray[inOffset++] & 0xFF) + | (bArray[inOffset++] & 0xFF) << 8 + | (bArray[inOffset++] & 0xFF) << 16 + | (bArray[inOffset++] & 0xFF) << 24; + int x3 = (bArray[inOffset++] & 0xFF) + | (bArray[inOffset++] & 0xFF) << 8 + | (bArray[inOffset++] & 0xFF) << 16 + | (bArray[inOffset++] & 0xFF) << 24; + + x0 ^= sKey[INPUT_WHITEN]; + x1 ^= sKey[INPUT_WHITEN + 1]; + x2 ^= sKey[INPUT_WHITEN + 2]; + x3 ^= sKey[INPUT_WHITEN + 3]; + + int t0, t1; + int k = ROUND_SUBKEYS; + for (int R = 0; R < ROUNDS; R += 2) + { + t0 = fe32(sBox, x0, 0); + t1 = fe32(sBox, x1, 3); + x2 ^= t0 + t1 + sKey[k++]; + x2 = RightUShift(x2, 1) | x2 << 31; + x3 = x3 << 1 | RightUShift(x3, 31); + x3 ^= t0 + 2 * t1 + sKey[k++]; + + t0 = fe32(sBox, x2, 0); + t1 = fe32(sBox, x3, 3); + x0 ^= t0 + t1 + sKey[k++]; + x0 = RightUShift(x0, 1) | x0 << 31; + x1 = x1 << 1 | RightUShift(x1, 31); + x1 ^= t0 + 2 * t1 + sKey[k++]; + } + x2 ^= sKey[OUTPUT_WHITEN]; + x3 ^= sKey[OUTPUT_WHITEN + 1]; + x0 ^= sKey[OUTPUT_WHITEN + 2]; + x1 ^= sKey[OUTPUT_WHITEN + 3]; + + return new byte[]{ + (byte) x2, (byte) RightUShift(x2, 8), (byte) RightUShift(x2, 16), (byte) RightUShift(x2, 24), + (byte) x3, (byte) RightUShift(x3, 8), (byte) RightUShift(x3, 16), (byte) RightUShift(x3, 24), + (byte) x0, (byte) RightUShift(x0, 8), (byte) RightUShift(x0, 16), (byte) RightUShift(x0, 24), + (byte) x1, (byte) RightUShift(x1, 8), (byte) RightUShift(x1, 16), (byte) RightUShift(x1, 24), + }; + } + + private static int _b0(int x) { return x & 0xFF; } + + private static int _b1(int x) { return RightUShift(x, 8) & 0xFF; } + + private static int _b2(int x) { return RightUShift(x, 16) & 0xFF; } + + private static int _b3(int x) { return RightUShift(x, 24) & 0xFF; } + + /** + * Use (12, 8) Reed-Solomon code over GF(256) to produce a key S-box + * 32-bit entity from two key material 32-bit entities. + * + * @param k0 1st 32-bit entity. + * @param k1 2nd 32-bit entity. + * @return Remainder polynomial generated using RS code + */ + private static int rsMdsEncode(int k0, int k1) + { + int r = k1; + for (int i = 0; i < 4; i++) + { + r = rsRem(r); + } + r ^= k0; + for (int i = 0; i < 4; i++) + { + r = rsRem(r); + } + return r; + } + + private static int rsRem(int x) + { + int b = RightUShift(x, 24) & 0xFF; + int g2 = ((b << 1) ^ ((b & 0x80) != 0 ? RS_GF_FDBK : 0)) & 0xFF; + int g3 = RightUShift(b, 1) ^ ((b & 0x01) != 0 ? RightUShift(RS_GF_FDBK, 1) : 0) ^ g2; + int result = (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b; + return result; + } + + private static int f32(int k64Cnt, int x, int[] k32) + { + int b0 = _b0(x); + int b1 = _b1(x); + int b2 = _b2(x); + int b3 = _b3(x); + int k0 = k32[0]; + int k1 = k32[1]; + int k2 = k32[2]; + int k3 = k32[3]; + + int result = 0; + switch (k64Cnt & 3) + { + case 1: + result = + MDS[0][(P[P_01][b0] & 0xFF) + ^ _b0(k0)] + ^ MDS[1][(P[P_11][b1] & 0xFF) + ^ _b1(k0)] + ^ MDS[2][(P[P_21][b2] & 0xFF) + ^ _b2(k0)] + ^ MDS[3][(P[P_31][b3] & 0xFF) + ^ _b3(k0)]; + break; + case 0: + b0 = (P[P_04][b0] & 0xFF) ^ _b0(k3); + b1 = (P[P_14][b1] & 0xFF) ^ _b1(k3); + b2 = (P[P_24][b2] & 0xFF) ^ _b2(k3); + b3 = (P[P_34][b3] & 0xFF) ^ _b3(k3); + + + b0 = (P[P_03][b0] & 0xFF) ^ _b0(k2); + b1 = (P[P_13][b1] & 0xFF) ^ _b1(k2); + b2 = (P[P_23][b2] & 0xFF) ^ _b2(k2); + b3 = (P[P_33][b3] & 0xFF) ^ _b3(k2); + + result = + MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) + ^ _b0(k1)] & 0xFF) + ^ _b0(k0)] + ^ MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) + ^ _b1(k1)] & 0xFF) ^ _b1(k0)] + ^ MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) + ^ _b2(k1)] & 0xFF) + ^ _b2(k0)] + ^ MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) + ^ _b3(k1)] & 0xFF) + ^ _b3(k0)]; + break; + case 3: + b0 = (P[P_03][b0] & 0xFF) ^ _b0(k2); + b1 = (P[P_13][b1] & 0xFF) ^ _b1(k2); + b2 = (P[P_23][b2] & 0xFF) ^ _b2(k2); + b3 = (P[P_33][b3] & 0xFF) ^ _b3(k2); + + result = + MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) + ^ _b0(k1)] & 0xFF) + ^ _b0(k0)] + ^ MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) + ^ _b1(k1)] & 0xFF) ^ _b1(k0)] + ^ MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) + ^ _b2(k1)] & 0xFF) + ^ _b2(k0)] + ^ MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) + ^ _b3(k1)] & 0xFF) + ^ _b3(k0)]; + break; + case 2: + result = + MDS[0][(P[P_01][(P[P_02][b0] & 0xFF) + ^ _b0(k1)] & 0xFF) + ^ _b0(k0)] + ^ MDS[1][(P[P_11][(P[P_12][b1] & 0xFF) + ^ _b1(k1)] & 0xFF) ^ _b1(k0)] + ^ MDS[2][(P[P_21][(P[P_22][b2] & 0xFF) + ^ _b2(k1)] & 0xFF) + ^ _b2(k0)] + ^ MDS[3][(P[P_31][(P[P_32][b3] & 0xFF) + ^ _b3(k1)] & 0xFF) + ^ _b3(k0)]; + break; + } + return result; + } + + private static int fe32(int[] sBox, int x, int r) + { + return sBox[2 * b(x, r)] + ^ sBox[2 * b(x, r + 1) + 1] + ^ sBox[0x200 + 2 * b(x, r + 2)] + ^ sBox[0x200 + 2 * b(x, r + 3) + 1]; + } + + private static int b(int x, int n) + { + int result = 0; + switch (n % 4) + { + case 0: + result = _b0(x); + break; + case 1: + result = _b1(x); + break; + case 2: + result = _b2(x); + break; + case 3: + result = _b3(x); + break; + } + return result; + } + } +} \ No newline at end of file diff --git a/POGOLib.Official/Util/Hash/HashData.cs b/POGOLib.Official/Util/Hash/HashData.cs index 238b88b11..035aed96b 100644 --- a/POGOLib.Official/Util/Hash/HashData.cs +++ b/POGOLib.Official/Util/Hash/HashData.cs @@ -1,10 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace POGOLib.Official.Util.Hash +namespace POGOLib.Official.Util.Hash { public class HashData { diff --git a/POGOLib.Official/Util/Hash/HashVersionMismatchException.cs b/POGOLib.Official/Util/Hash/HashVersionMismatchException.cs index f704e9ce6..8f17ad63c 100644 --- a/POGOLib.Official/Util/Hash/HashVersionMismatchException.cs +++ b/POGOLib.Official/Util/Hash/HashVersionMismatchException.cs @@ -1,8 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; namespace POGOLib.Official.Util.Hash { diff --git a/POGOLib.Official/Util/Hash/IHasher.cs b/POGOLib.Official/Util/Hash/IHasher.cs index f22bf16eb..5810de98f 100644 --- a/POGOLib.Official/Util/Hash/IHasher.cs +++ b/POGOLib.Official/Util/Hash/IHasher.cs @@ -1,9 +1,6 @@ -using POGOProtos.Networking.Envelopes; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; +using System; using System.Threading.Tasks; +using POGOProtos.Networking.Envelopes; namespace POGOLib.Official.Util.Hash { diff --git a/POGOLib.Official/Util/Hash/LegacyHasher.cs b/POGOLib.Official/Util/Hash/LegacyHasher.cs index 9eb156c73..cb5cf9207 100644 --- a/POGOLib.Official/Util/Hash/LegacyHasher.cs +++ b/POGOLib.Official/Util/Hash/LegacyHasher.cs @@ -1,10 +1,8 @@ -using POGOLib.Official.Util.Encryption.Legacy; -using POGOProtos.Networking.Envelopes; -using System; -using System.Collections.Generic; +using System; using System.Linq; -using System.Text; using System.Threading.Tasks; +using POGOLib.Official.Util.Encryption.Legacy; +using POGOProtos.Networking.Envelopes; namespace POGOLib.Official.Util.Hash { diff --git a/POGOLib.Official/Util/Hash/PokeHash/PokeHashAuthKey.cs b/POGOLib.Official/Util/Hash/PokeHash/PokeHashAuthKey.cs index 9262a2d08..15607dd28 100644 --- a/POGOLib.Official/Util/Hash/PokeHash/PokeHashAuthKey.cs +++ b/POGOLib.Official/Util/Hash/PokeHash/PokeHashAuthKey.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util.Hash.PokeHash { diff --git a/POGOLib.Official/Util/Hash/PokeHash/PokeHashRequest.cs b/POGOLib.Official/Util/Hash/PokeHash/PokeHashRequest.cs index 3a25814d7..ca3629bab 100644 --- a/POGOLib.Official/Util/Hash/PokeHash/PokeHashRequest.cs +++ b/POGOLib.Official/Util/Hash/PokeHash/PokeHashRequest.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; namespace POGOLib.Official.Util.Hash.PokeHash { diff --git a/POGOLib.Official/Util/Hash/PokeHash/PokeHashResponse.cs b/POGOLib.Official/Util/Hash/PokeHash/PokeHashResponse.cs index 724a200f0..d58097d72 100644 --- a/POGOLib.Official/Util/Hash/PokeHash/PokeHashResponse.cs +++ b/POGOLib.Official/Util/Hash/PokeHash/PokeHashResponse.cs @@ -1,7 +1,4 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; namespace POGOLib.Official.Util.Hash.PokeHash { diff --git a/POGOLib.Official/Util/Hash/PokeHashHasher.cs b/POGOLib.Official/Util/Hash/PokeHashHasher.cs index 602d9ea5a..c5786e437 100644 --- a/POGOLib.Official/Util/Hash/PokeHashHasher.cs +++ b/POGOLib.Official/Util/Hash/PokeHashHasher.cs @@ -21,26 +21,26 @@ namespace POGOLib.Official.Util.Hash /// to buy an API key, go to this url. /// https://talk.pogodev.org/d/51-api-hashing-service-by-pokefarmer /// - /// Android version: 0.51.0 - /// IOS version: 1.21.0 + /// Android version: 0.59.1 + /// IOS version: 1.29.1 /// public class PokeHashHasher : IHasher { private const string PokeHashUrl = "https://pokehash.buddyauth.com/"; - private const string PokeHashEndpoint = "api/v127_4/hash"; + private const string PokeHashEndpoint = "api/v129_1/hash"; private readonly List _authKeys; private readonly HttpClient _httpClient; private readonly Semaphore _keySelection; - + /// /// Initializes the . /// /// The PokeHash authkey obtained from https://talk.pogodev.org/d/51-api-hashing-service-by-pokefarmer. - public PokeHashHasher(string authKey) : this(new[] { authKey }) + public PokeHashHasher(string authKey) : this(new []{ authKey }) { } @@ -55,7 +55,7 @@ public PokeHashHasher(string[] authKeys) throw new ArgumentException($"{nameof(authKeys)} may not be empty."); _authKeys = new List(); - + // We don't want any duplicate keys. foreach (var authKey in authKeys) { @@ -79,9 +79,9 @@ public PokeHashHasher(string[] authKeys) _keySelection = new Semaphore(1, 1); } - public Version PokemonVersion { get; } = new Version("0.57.4"); + public Version PokemonVersion { get; } = new Version("0.59.1"); - public long Unknown25 { get; } = -816976800928766045; + public long Unknown25 { get; } = -3226782243204485589; public async Task GetHashDataAsync(RequestEnvelope requestEnvelope, Signature signature, byte[] locationBytes, byte[][] requestsBytes, byte[] serializedTicket) { @@ -97,11 +97,11 @@ public async Task GetHashDataAsync(RequestEnvelope requestEnvelope, Si }; var requestContent = new StringContent(JsonConvert.SerializeObject(requestData), Encoding.UTF8, "application/json"); - + using (var response = await PerformRequest(requestContent)) { var responseContent = await response.Content.ReadAsStringAsync(); - + string message; switch (response.StatusCode) @@ -114,7 +114,7 @@ public async Task GetHashDataAsync(RequestEnvelope requestEnvelope, Si LocationAuthHash = responseData.LocationAuthHash, LocationHash = responseData.LocationHash, RequestHashes = responseData.RequestHashes - .Select(x => (ulong)x) + .Select(x => (ulong) x) .ToArray() }; @@ -125,12 +125,12 @@ public async Task GetHashDataAsync(RequestEnvelope requestEnvelope, Si case HttpStatusCode.BadRequest: message = $"Bad request sent to the hashing server! {responseContent}"; break; - + case HttpStatusCode.Unauthorized: message = "The auth key supplied for PokeHash was invalid."; break; - - case (HttpStatusCode)429: + + case (HttpStatusCode) 429: message = $"Your request has been limited. {responseContent}"; break; @@ -146,7 +146,7 @@ public async Task GetHashDataAsync(RequestEnvelope requestEnvelope, Si throw new Exception(message); } } - + private Task PerformRequest(HttpContent requestContent) { return Task.Run(async () => @@ -159,15 +159,15 @@ private Task PerformRequest(HttpContent requestContent) { _keySelection.WaitOne(); - //Logger.Warn(">>> Entering key selection."); - +// Logger.Warn(">>> Entering key selection."); + var availableKeys = _authKeys.Where(x => x.Requests < x.MaxRequestCount).ToArray(); if (availableKeys.Length > 0) { authKey = availableKeys.First(); authKey.Requests += 1; - //Logger.Warn("Found available auth key."); +// Logger.Warn("Found available auth key."); // If the auth key has not been initialized yet, we need to have control a bit longer // to configure it properly. @@ -176,16 +176,15 @@ private Task PerformRequest(HttpContent requestContent) } else { - Logger.Warn("No available auth keys found."); +// Logger.Warn("No available auth keys found."); authKey = _authKeys .OrderBy(x => x.RatePeriodEnd) .First(); - var sleepTime = (int)Math.Ceiling(authKey.RatePeriodEnd.Subtract(DateTime.UtcNow).TotalMilliseconds); - - Logger.Warn($"Key selection is sleeping for {sleepTime}ms."); + var sleepTime = (int) Math.Ceiling(authKey.RatePeriodEnd.Subtract(DateTime.UtcNow).TotalMilliseconds); +// Logger.Warn($"Key selection is sleeping for {sleepTime}ms."); PokehashSleeping?.Invoke(this, sleepTime); await Task.Delay(sleepTime); @@ -195,23 +194,23 @@ private Task PerformRequest(HttpContent requestContent) // We have to receive the new rate period end. extendedSelection = true; - Logger.Warn("Key selection is done sleeping."); +// Logger.Warn("Key selection is done with sleeping."); } } finally { if (!extendedSelection) { - //Logger.Warn("<<< Exiting key selection."); +// Logger.Warn("<<< Exiting key selection."); _keySelection.Release(); } else { - Logger.Warn("=== Holding key selection."); +// Logger.Warn("=== Holding key selection."); } } - + requestContent.Headers.Add("X-AuthToken", authKey.AuthKey); HttpResponseMessage response = null; @@ -227,45 +226,43 @@ private Task PerformRequest(HttpContent requestContent) // Handle response try { - int maxRequestCount = 150; - int secs = 60; - int remaining = maxRequestCount; - - IEnumerable requestCountHeader; - if (response.Headers.TryGetValues("X-MaxRequestCount", out requestCountHeader)) + // Parse headers + int maxRequestCount; + int rateRequestsRemaining; + int ratePeriodEndSeconds; + + IEnumerable maxRequestsValue; + IEnumerable requestsRemainingValue; + IEnumerable ratePeriodEndValue; + + if (response.Headers.TryGetValues("X-MaxRequestCount", out maxRequestsValue) && + response.Headers.TryGetValues("X-RateRequestsRemaining", out requestsRemainingValue) && + response.Headers.TryGetValues("X-RatePeriodEnd", out ratePeriodEndValue)) { - int.TryParse(requestCountHeader.FirstOrDefault() ?? "1", out maxRequestCount); - } - - IEnumerable ratePeriodEndHeader; - if (response.Headers.TryGetValues("X-RatePeriodEnd", out ratePeriodEndHeader)) - { - int.TryParse(ratePeriodEndHeader.FirstOrDefault() ?? "1", out secs); - - //Logger.Warn($"Resets: {TimeUtil.GetDateTimeFromSeconds(secs)}"); + if (!int.TryParse(maxRequestsValue.First(), out maxRequestCount) || + !int.TryParse(requestsRemainingValue.First(), out rateRequestsRemaining) || + !int.TryParse(ratePeriodEndValue.FirstOrDefault(), out ratePeriodEndSeconds)) + { + throw new Exception("Failed parsing pokehash response header values."); + } } - - IEnumerable rateRequestsRemainingHeader; - if (response.Headers.TryGetValues("X-RateRequestsRemaining", out rateRequestsRemainingHeader)) + else { - int.TryParse(rateRequestsRemainingHeader.FirstOrDefault() ?? "1", out remaining); - - //Logger.Warn($"Remaining / Max: {remaining} / {authKey.MaxRequestCount}"); - //Logger.Warn($"Requests / ShouldBe: {authKey.Requests} / {authKey.MaxRequestCount - remaining}"); + throw new Exception("Failed parsing pokehash response headers."); } // Use parsed headers if (!authKey.IsInitialized) { authKey.MaxRequestCount = maxRequestCount; - authKey.Requests = authKey.MaxRequestCount - remaining; + authKey.Requests = authKey.MaxRequestCount - rateRequestsRemaining; authKey.IsInitialized = true; } - - var ratePeriodEnd = TimeUtil.GetDateTimeFromSeconds(secs); + + var ratePeriodEnd = TimeUtil.GetDateTimeFromSeconds(ratePeriodEndSeconds); if (ratePeriodEnd > authKey.RatePeriodEnd) { - //Logger.Warn($"[AuthKey: {authKey.AuthKey}] {authKey.RatePeriodEnd} increased to {ratePeriodEnd}."); +// Logger.Warn($"[AuthKey: {authKey.AuthKey}] {authKey.RatePeriodEnd} increased to {ratePeriodEnd}."); authKey.RatePeriodEnd = ratePeriodEnd; } @@ -276,7 +273,7 @@ private Task PerformRequest(HttpContent requestContent) { if (extendedSelection) { - //Logger.Warn("<<< Exiting extended key selection."); +// Logger.Warn("<<< Exiting extended key selection."); _keySelection.Release(); } diff --git a/POGOLib.Official/Util/LocationUtil.cs b/POGOLib.Official/Util/LocationUtil.cs index 265f20dad..18b9f38c6 100644 --- a/POGOLib.Official/Util/LocationUtil.cs +++ b/POGOLib.Official/Util/LocationUtil.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util { diff --git a/POGOLib.Official/Util/MapUtil.cs b/POGOLib.Official/Util/MapUtil.cs index b0a7ab462..a03e8bce9 100644 --- a/POGOLib.Official/Util/MapUtil.cs +++ b/POGOLib.Official/Util/MapUtil.cs @@ -1,8 +1,5 @@ -using Google.Common.Geometry; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; +using System.Collections.Generic; +using Google.Common.Geometry; namespace POGOLib.Official.Util { diff --git a/POGOLib.Official/Util/TimeUtil.cs b/POGOLib.Official/Util/TimeUtil.cs index 8d56c42e8..ac022d5f0 100644 --- a/POGOLib.Official/Util/TimeUtil.cs +++ b/POGOLib.Official/Util/TimeUtil.cs @@ -1,7 +1,4 @@ using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; namespace POGOLib.Official.Util { diff --git a/POGOLib.Official/project.json b/POGOLib.Official/project.json index 8ca3fa60e..05fc57fa3 100644 --- a/POGOLib.Official/project.json +++ b/POGOLib.Official/project.json @@ -1,10 +1,10 @@ -{ +{ "supports": {}, "dependencies": { "GeoCoordinate.NetStandard1": "1.0.0", "Google.Protobuf": "3.2.0", "Newtonsoft.Json": "9.0.1", - "POGOProtos.NetStandard1": "2.7.0", + "POGOProtos.NetStandard1": "2.8.0", "S2Geometry": "1.0.3" }, "frameworks": { diff --git a/PokemonGo-UWP/Assets/Icons/ic_ivpercentage.png b/PokemonGo-UWP/Assets/Icons/ic_ivpercentage.png index 3a5699caf..4aa4ac218 100644 Binary files a/PokemonGo-UWP/Assets/Icons/ic_ivpercentage.png and b/PokemonGo-UWP/Assets/Icons/ic_ivpercentage.png differ diff --git a/PokemonGo-UWP/Assets/Icons/ic_level.png b/PokemonGo-UWP/Assets/Icons/ic_level.png index 78d2cb673..97febfb48 100644 Binary files a/PokemonGo-UWP/Assets/Icons/ic_level.png and b/PokemonGo-UWP/Assets/Icons/ic_level.png differ diff --git a/PokemonGo-UWP/Utils/GameClient.cs b/PokemonGo-UWP/Utils/GameClient.cs index 9cae5df29..cee85a6e4 100644 --- a/PokemonGo-UWP/Utils/GameClient.cs +++ b/PokemonGo-UWP/Utils/GameClient.cs @@ -51,6 +51,7 @@ using Template10.Services.NavigationService; using POGOProtos.Data.Battle; using PokemonGo_UWP.Exceptions; +using POGOLib.Official.Net.Captcha; namespace PokemonGo_UWP.Utils { @@ -393,9 +394,9 @@ private async static Task CreateSession(Geoposition pos) if (_session != null) { _session.AccessTokenUpdated -= SessionOnAccessTokenUpdated; - _session.Player.Inventory.Update -= InventoryOnUpdate; - _session.Map.Update -= MapOnUpdate; - _session.RpcClient.CheckChallengeReceived -= SessionOnCheckChallengeReceived; + _session.InventoryUpdate -= InventoryOnUpdate; + _session.MapUpdate -= MapOnUpdate; + _session.CaptchaReceived -= SessionOnCaptchaReceived; _session.RpcClient.HatchedEggsReceived -= SessionOnHatchedEggsReceived; _session.RpcClient.CheckAwardedBadgesReceived -= SessionOnCheckAwardedBadgesReceived; } @@ -445,9 +446,9 @@ private async static Task CreateSession(Geoposition pos) } _session.AccessTokenUpdated += SessionOnAccessTokenUpdated; - _session.Player.Inventory.Update += InventoryOnUpdate; - _session.Map.Update += MapOnUpdate; - _session.RpcClient.CheckChallengeReceived += SessionOnCheckChallengeReceived; + _session.InventoryUpdate += InventoryOnUpdate; + _session.MapUpdate += MapOnUpdate; + _session.CaptchaReceived += SessionOnCaptchaReceived; _session.RpcClient.HatchedEggsReceived += SessionOnHatchedEggsReceived; _session.RpcClient.CheckAwardedBadgesReceived += SessionOnCheckAwardedBadgesReceived; @@ -522,25 +523,20 @@ private static void SessionOnAccessTokenUpdated(object sender, EventArgs eventAr Logger.Info("Saved access token to file."); } - private static async void SessionOnCheckChallengeReceived(object sender, CheckChallengeResponse e) + private static async void SessionOnCaptchaReceived(object sender, CaptchaEventArgs e) { - if (e.ShowChallenge && !String.IsNullOrWhiteSpace(e.ChallengeUrl) && e.ChallengeUrl.Length > 5) - { - // Captcha is shown in checkChallengeResponse.ChallengeUrl - Logger.Warn($"ChallengeURL: {e.ChallengeUrl}"); - // breakpoint here to manually resolve Captcha in a browser - // after that set token to str variable from browser (see screenshot) - Logger.Warn("Pause"); + var session = (Session)sender; + + Logger.Warn($"Captcha received: {e.CaptchaUrl}"); - //GOTO THE REQUIRED PAGE - if (BootStrapper.Current.NavigationService.CurrentPageType != typeof(ChallengePage)) + //GOTO THE REQUIRED PAGE + if (BootStrapper.Current.NavigationService.CurrentPageType != typeof(ChallengePage)) + { + await DispatcherHelper.RunInDispatcherAndAwait(() => { - await DispatcherHelper.RunInDispatcherAndAwait(() => - { - // We are not in UI thread probably, so run this via dispatcher - BootStrapper.Current.NavigationService.Navigate(typeof(ChallengePage), e.ChallengeUrl); - }); - } + // We are not in UI thread probably, so run this via dispatcher + BootStrapper.Current.NavigationService.Navigate(typeof(ChallengePage), e.CaptchaUrl); + }); } } @@ -554,18 +550,20 @@ private static void SessionOnCheckAwardedBadgesReceived(object sender, CheckAwar OnAwardedBadgesReceived?.Invoke(sender, e); } - private static void InventoryOnUpdate(object sender, EventArgs eventArgs) + private static void InventoryOnUpdate(object sender, EventArgs e) { - Inventory inventory = sender as Inventory; + Session session = sender as Session; + Inventory inventory = session.Player.Inventory; UpdateLocalInventory(inventory); Logger.Info("Inventory was updated."); } - private async static void MapOnUpdate(object sender, EventArgs eventArgs) + private async static void MapOnUpdate(object sender, EventArgs e) { if (_isSessionEnabled) { - Map map = sender as Map; + Session session = sender as Session; + Map map = session.Map; await UpdateMapObjects(map); Logger.Info("Map was updated."); } @@ -1656,9 +1654,16 @@ public static async Task UseCaptureItem(ulong encounterI SpawnPointId = spawnpointId }.ToByteString() }); - var useItemCaptureResponse = UseItemCaptureResponse.Parser.ParseFrom(response); - return useItemCaptureResponse; + try + { + var useItemCaptureResponse = UseItemCaptureResponse.Parser.ParseFrom(response); + return useItemCaptureResponse; + } + catch (Exception) + { + return new UseItemCaptureResponse() { Success = false }; + } } /// @@ -1680,9 +1685,16 @@ public static async Task UseItemEncounter(ulong encoun SpawnPointGuid = spawnpointId }.ToByteString() }); - var useItemEncounterResponse = UseItemEncounterResponse.Parser.ParseFrom(response); - return useItemEncounterResponse; + try + { + var useItemEncounterResponse = UseItemEncounterResponse.Parser.ParseFrom(response); + return useItemEncounterResponse; + } + catch (Exception) + { + return new UseItemEncounterResponse() { Status = UseItemEncounterResponse.Types.Status.AlreadyCompleted }; + } } #endregion @@ -2021,9 +2033,16 @@ public static async Task UseIncense(ItemId item) IncenseType = item }.ToByteString() }); - var useIncenseResponse = UseIncenseResponse.Parser.ParseFrom(response); - return useIncenseResponse; + try + { + var useIncenseResponse = UseIncenseResponse.Parser.ParseFrom(response); + return useIncenseResponse; + } + catch (Exception) + { + return new UseIncenseResponse() { Result = UseIncenseResponse.Types.Result.Unknown }; + } } /// @@ -2041,9 +2060,16 @@ public static async Task UseXpBoost(ItemId item) ItemId = item }.ToByteString() }); - var useItemXpBoostResponse = UseItemXpBoostResponse.Parser.ParseFrom(response); - return useItemXpBoostResponse; + try + { + var useItemXpBoostResponse = UseItemXpBoostResponse.Parser.ParseFrom(response); + return useItemXpBoostResponse; + } + catch (Exception) + { + return new UseItemXpBoostResponse() { Result = UseItemXpBoostResponse.Types.Result.Unset }; + } } public static async Task UseItemRevive(ItemId item, ulong pokemonId) @@ -2057,9 +2083,16 @@ public static async Task UseItemRevive(ItemId item, ulong ItemId = item }.ToByteString() }); - var useItemReviveResponse = UseItemReviveResponse.Parser.ParseFrom(response); - return useItemReviveResponse; + try + { + var useItemReviveResponse = UseItemReviveResponse.Parser.ParseFrom(response); + return useItemReviveResponse; + } + catch (Exception) + { + return new UseItemReviveResponse() { Result = UseItemReviveResponse.Types.Result.Unset }; + } } public static async Task UseItemPotion(ItemId item, ulong pokemonId) @@ -2073,9 +2106,16 @@ public static async Task UseItemPotion(ItemId item, ulong ItemId = item }.ToByteString() }); - var useItemPotionResponse = UseItemPotionResponse.Parser.ParseFrom(response); - return useItemPotionResponse; + try + { + var useItemPotionResponse = UseItemPotionResponse.Parser.ParseFrom(response); + return useItemPotionResponse; + } + catch(Exception) + { + return new UseItemPotionResponse() { Result = UseItemPotionResponse.Types.Result.Unset }; + } } /// @@ -2121,9 +2161,16 @@ public static async Task UseEggIncubator(EggIncubat PokemonId = egg.Id }.ToByteString() }); - var useItemEggIncubatorResponse = UseItemEggIncubatorResponse.Parser.ParseFrom(response); - return useItemEggIncubatorResponse; + try + { + var useItemEggIncubatorResponse = UseItemEggIncubatorResponse.Parser.ParseFrom(response); + return useItemEggIncubatorResponse; + } + catch (Exception) + { + return new UseItemEggIncubatorResponse() { Result = UseItemEggIncubatorResponse.Types.Result.Unset }; + } } /// diff --git a/PokemonGo-UWP/ViewModels/ItemsInventoryPageViewModel.cs b/PokemonGo-UWP/ViewModels/ItemsInventoryPageViewModel.cs index cde52037e..730dd7725 100644 --- a/PokemonGo-UWP/ViewModels/ItemsInventoryPageViewModel.cs +++ b/PokemonGo-UWP/ViewModels/ItemsInventoryPageViewModel.cs @@ -313,7 +313,9 @@ private void AskAndUsePotion(ItemDataWrapper item) { PokemonInventory = new ObservableCollection(GameClient.PokemonsInventory .Select(pokemonData => new PokemonDataWrapper(pokemonData)) - .Where(pokemonData => pokemonData.Stamina != pokemonData.StaminaMax)); + .Where(pokemonData => pokemonData.Stamina != pokemonData.StaminaMax && // Not completely healthy + pokemonData.Stamina != 0 && // Not fainted + String.IsNullOrEmpty(pokemonData.DeployedFortId))); // Not deployed to fort CurrentUseItem = item; @@ -327,7 +329,9 @@ private void AskAndUseRevive(ItemDataWrapper item) { PokemonInventory = new ObservableCollection(GameClient.PokemonsInventory .Select(pokemonData => new PokemonDataWrapper(pokemonData)) - .Where(pokemonData => pokemonData.Stamina ==0)); + //.Where(pokemonData => pokemonData.Stamina == 0)); + .Where(pokemonData => pokemonData.Stamina == 0 && // Fainted + String.IsNullOrEmpty(pokemonData.DeployedFortId))); // Not deployed to fort CurrentUseItem = item; diff --git a/PokemonGo-UWP/Views/EnterGymPage.xaml b/PokemonGo-UWP/Views/EnterGymPage.xaml index c05ada0a9..0e19bd26c 100644 --- a/PokemonGo-UWP/Views/EnterGymPage.xaml +++ b/PokemonGo-UWP/Views/EnterGymPage.xaml @@ -503,7 +503,7 @@ Width="60" Height="60" Margin="8,0,0,0" Command="{Binding AutoSelectCommand}" Style="{ThemeResource ImageButtonStyle}"> - +