Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merge in (and fix) KeyPhact's U6 implementation #182

Open
wants to merge 48 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
632b7df
Rewrite of google/ptc auth in regard to refresh/access token and addes
tejado Aug 6, 2016
9b31550
Update proto
wchill Aug 6, 2016
5bdd8af
Merge pull request #1 from keyphact/fix-proto
keyphact Aug 6, 2016
916a51f
Update Signature.proto
globeriz Aug 6, 2016
972b892
Update Signature_pb2.py
globeriz Aug 6, 2016
b583558
Update Signature_pb2.py
globeriz Aug 6, 2016
1dbf5a1
Update utilities.py
cheesynoob Aug 6, 2016
dbd0634
iOSActivity should be Activity
globeriz Aug 6, 2016
d3d84df
Merge pull request #3 from keyphact/cheesynoob-patch-1
cheesynoob Aug 6, 2016
9938ae0
Update Signature.proto
globeriz Aug 6, 2016
f46e4bc
Update Signature_pb2.py
globeriz Aug 6, 2016
b7845a2
Fixed mistake in generateRequests
cheesynoob Aug 6, 2016
a5dcbde
Merge pull request #4 from keyphact/cheesynoob-patch-2
KevinCollmer Aug 6, 2016
0eacfe6
Merge pull request #2 from keyphact/fix-proto-1
classyjakey Aug 6, 2016
2c2b780
Quick fix to utilities functions
TalSk Aug 6, 2016
8b7a003
Merge pull request #5 from keyphact/tal-patch1
JosiahWhite Aug 6, 2016
3ecea0d
Added wrapper for signature encryption
tejado Aug 6, 2016
917bff0
Merge pull request #6 from tejado/master
keyphact Aug 7, 2016
91a700b
changed request altitude to int value
cheesynoob Aug 7, 2016
fd462be
Merge pull request #8 from keyphact/cheesynoob-patch-1
keyphact Aug 7, 2016
e206f4a
d2h fix and options for pokecli
dmadisetti Aug 7, 2016
b59cb1d
Merge res
dmadisetti Aug 7, 2016
64d539d
It works now
dmadisetti Aug 7, 2016
10d7cde
Works now
dmadisetti Aug 7, 2016
9c070a7
Merge pull request #9 from dmadisetti/patch-1
wchill Aug 7, 2016
75eba6b
Use six for chr() call for python3 compat, pep8 fixes
pbdeuchler Aug 7, 2016
249d3be
Merge pull request #11 from catchemallio/python3
wchill Aug 7, 2016
6b50289
Fix hex decoding on Python 3
Noctem Aug 7, 2016
06eaef1
Merge pull request #23 from Noctem/hexdecode
wchill Aug 7, 2016
d809601
Merge in Keys and Conflict res
dmadisetti Aug 7, 2016
691aff2
Added scripts\accept-tos.py (#30)
scottstamp Aug 7, 2016
23be9dd
correctly return the new access_token after refresh
rbignon Aug 8, 2016
e489594
Add a sane API timeout.
edevil Aug 8, 2016
8b1dca3
Fix index error on API endpoint (#63)
wchill Aug 9, 2016
5b7c7c5
Merge pull request #54 from edevil/api_timeout
keyphact Aug 9, 2016
a2755eb
Merge pull request #49 from rbignon/master
keyphact Aug 9, 2016
5e83cad
Allow using a proxy.
tschroeder-zendesk Aug 2, 2016
842e80c
Merge proxy support
Noctem Aug 9, 2016
39ea20d
Merge pull request #66 from Noctem/proxies
globeriz Aug 9, 2016
915cfa8
Add auth service as an optional parameter
OurFlagIsMined Aug 9, 2016
dc1b222
Added future as a requirement
pengstrom Aug 9, 2016
51ceb8a
Merge pull request #69 from OurFlagIsMined/patch-1
globeriz Aug 9, 2016
ef58322
Merge pull request #70 from pengstrom/pengstrom/add-future-requirement
globeriz Aug 9, 2016
68575cc
Now reusing sessions (#73)
cheesynoob Aug 9, 2016
b4bf0e0
Fixed missing import (#74)
cheesynoob Aug 9, 2016
7a0666f
Uses auth_info as seed when auth_ticket is unavailable (#74)
cheesynoob Aug 9, 2016
60096cc
Fix proxies with reused sessions (#77)
Noctem Aug 10, 2016
d10968f
Merge branch 'master' of https://github.com/keyphact/pgoapi
dmadisetti Aug 11, 2016
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions pgoapi/pgoapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,8 @@ def __init__(self, provider = None, oauth2_refresh_token = None, username = None
self._position_lng = position_lng
self._position_alt = position_alt

self._signature_lib = None

def set_logger(self, logger = None):
self.log = logger or logging.getLogger(__name__)

Expand Down Expand Up @@ -106,6 +108,12 @@ def create_request(self):
request = PGoApiRequest(self, self._position_lat, self._position_lng, self._position_alt)
return request

def activate_signature(self, lib_path):
self._signature_lib = lib_path

def get_signature_lib(self):
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

return self._signature_lib

def __getattr__(self, func):
def function(**kwargs):
request = self.create_request()
Expand Down Expand Up @@ -195,6 +203,10 @@ def call(self):
return NotLoggedInException()

request = RpcApi(self._auth_provider)

lib_path = self.__parent__.get_signature_lib()
if lib_path is not None:
request.activate_signature(lib_path)

self.log.info('Execution of RPC')
response = None
Expand Down
73 changes: 68 additions & 5 deletions pgoapi/rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,9 @@

from __future__ import absolute_import

import os
import re
import time
import base64
import random
import logging
Expand All @@ -36,18 +38,23 @@

from importlib import import_module


import ctypes

from pgoapi.protobuf_to_dict import protobuf_to_dict
from pgoapi.exceptions import NotLoggedInException, ServerBusyOrOfflineException, ServerSideRequestThrottlingException, ServerSideAccessForbiddenException, UnexpectedResponseException, AuthTokenExpiredException, ServerApiEndpointRedirectException
from pgoapi.utilities import to_camel_case, get_time, get_format_time_diff
from pgoapi.utilities import to_camel_case, get_time, get_format_time_diff, Rand48, long_to_bytes, generateLocation1, generateLocation2, generateRequestHash, f2i

from . import protos
from POGOProtos.Networking.Envelopes_pb2 import RequestEnvelope
from POGOProtos.Networking.Envelopes_pb2 import ResponseEnvelope
from POGOProtos.Networking.Requests_pb2 import RequestType
import Signature_pb2

class RpcApi:

RPC_ID = 0
START_TIME = 0

def __init__(self, auth_provider):

Expand All @@ -59,10 +66,25 @@ def __init__(self, auth_provider):

self._auth_provider = auth_provider

""" mystic unknown6 - revolved by PokemonGoDev """
self._signature_gen = False
self._signature_lib = None

if RpcApi.START_TIME == 0:
RpcApi.START_TIME = get_time(ms = True)

if RpcApi.RPC_ID == 0:
RpcApi.RPC_ID = int(random.random() * 10 ** 18)
self.log.debug('Generated new random RPC Request id: %s', RpcApi.RPC_ID)

def activate_signature(self, lib_path):
try:
ctypes.cdll.LoadLibrary(lib_path)
self._signature_gen = True
self._signature_lib = lib_path
except:
raise

def get_rpc_id(self):
RpcApi.RPC_ID += 1
self.log.debug("Incremented RPC Request ID: %s", RpcApi.RPC_ID)
Expand Down Expand Up @@ -146,7 +168,7 @@ def check_authentication(self, response_dict):
else:
self.log.debug('Received Session Ticket valid for %02d:%02d:%02d hours (%s < %s)', h, m, s, now_ms, auth_ticket['expire_timestamp_ms'])

def _build_main_request2(self, subrequests, player_position = None):
def _build_main_request(self, subrequests, player_position = None):
self.log.debug('Generating main RPC request...')

request = RequestEnvelope()
Expand All @@ -155,11 +177,38 @@ def _build_main_request2(self, subrequests, player_position = None):

if player_position is not None:
request.latitude, request.longitude, request.altitude = player_position

request.altitude = 0.63

""" generate sub requests before signature generation """
request = self._build_sub_requests(request, subrequests)

ticket = self._auth_provider.get_ticket()
if ticket:
self.log.debug('Found Session Ticket - using this instead of oauth token')
request.auth_ticket.expire_timestamp_ms, request.auth_ticket.start, request.auth_ticket.end = ticket

if self._signature_gen:
ticket_serialized = request.auth_ticket.SerializeToString()

sig = Signature_pb2.Signature()

sig.location_hash1 = generateLocation1(ticket_serialized, request.latitude, request.longitude, request.altitude)
sig.location_hash2 = generateLocation2(request.latitude, request.longitude, request.altitude)

for req in request.requests:
hash = generateRequestHash(ticket_serialized, req.SerializeToString())
sig.request_hash.append( hash )

sig.unk22 = os.urandom(32)
sig.timestamp = get_time(ms=True)
sig.timestamp_since_start = get_time(ms=True) - RpcApi.START_TIME

signature_proto = sig.SerializeToString()

u6 = request.unknown6.add()
u6.request_type = 6
u6.unknown2.unknown1 = self._generate_signature(signature_proto, self._signature_lib)
else:
self.log.debug('No Session Ticket found - using OAUTH Access Token')
request.auth_info.provider = self._auth_provider.get_name()
Expand All @@ -169,13 +218,27 @@ def _build_main_request2(self, subrequests, player_position = None):
# unknown stuff
request.unknown12 = 989

request = self._build_sub_requests(request, subrequests)

self.log.debug('Generated protobuf request: \n\r%s', request )

return request

def _build_main_request(self, subrequests, player_position = None):
def _generate_signature(self, signature_plain, lib_path = "encrypt.so"):
lib = ctypes.cdll.LoadLibrary(lib_path)
lib.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.c_size_t, ctypes.POINTER(ctypes.c_ubyte), ctypes.POINTER(ctypes.c_size_t)]
lib.restype = ctypes.c_int

iv = os.urandom(32)

output_size = ctypes.c_size_t()

ret = lib.encrypt(signature_plain, len(signature_plain), iv, 32, None, ctypes.byref(output_size))
output = (ctypes.c_ubyte * output_size.value)()
ret = lib.encrypt(signature_plain, len(signature_plain), iv, 32, ctypes.byref(output), ctypes.byref(output_size))

signature = b''.join(map(chr, output))
return signature

def _build_main_request_orig(self, subrequests, player_position = None):
self.log.debug('Generating main RPC request...')

request = RequestEnvelope()
Expand Down
42 changes: 23 additions & 19 deletions pgoapi/utilities.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,9 @@
import re
import time
import struct
import logging
import ctypes
import xxhash
import logging

from json import JSONEncoder
from binascii import unhexlify
Expand Down Expand Up @@ -159,21 +160,24 @@ def long_to_bytes (val, endianness='big'):
return s


def generateLocation1(authticket, lat, lng, alt): #u10
firstHash = xxhash.xxh32(bytearray(authticket), seed=0x1B845238).intdigest() #Hashing the auth ticket using static seed 0x1B845238
locationBytes = bytearray([lat,lng,alt]) #Lat, long and alt as a double
locationHash = xxhash.xxh32(locationBytes, seed=firstHash).intdigest() #hash of location using the hashed auth ticket as seed
return locationHash

def generateLocation2(lat, lng, alt): #u20
locationBytes = bytearray([lat,lng,alt])
locationHash = xxhash.xxh32(locationBytes, seed=0x1B845238).intdigest() #Hash of location using static seed 0x1B845238
return locationHash

def generateRequests(authticket, requests): #u24
firstHash = xxhash.xxh64(bytearray(authticket), seed=0x1B845238).intdigest() #Hashing the auth ticket using static seed 0x1B845238
hashList = [] #Leaving as a list for now
for req in requests:
hashList.add(xxhash.xxh64(bytearray(req), seed=firstHash).intdigest()) #Hash each request with the hashed auth ticket as seed
hashArray = array(I, hashList) #Convert the list to an array
return hashArray
def generateLocation1(authticket, lat, lng, alt):
firstHash = xxhash.xxh32(authticket, seed=0x1B845238).intdigest()
locationBytes = d2h(lat) + d2h(lng) + d2h(alt)
if not alt:
alt = "\x00\x00\x00\x00\x00\x00\x00\x00"
return xxhash.xxh32(locationBytes, seed=firstHash).intdigest()

def generateLocation2(lat, lng, alt):
locationBytes = d2h(lat) + d2h(lng) + d2h(alt)
if not alt:
alt = "\x00\x00\x00\x00\x00\x00\x00\x00"
return xxhash.xxh32(locationBytes, seed=0x1B845238).intdigest() #Hash of location using static seed 0x1B845238


def generateRequestHash(authticket, request):
firstHash = xxhash.xxh64(authticket, seed=0x1B845238).intdigest()
return xxhash.xxh64(request, seed=firstHash).intdigest()


def d2h(f):
return hex(struct.unpack('<Q', struct.pack('<d', f))[0])[2:-1].decode("hex")
24 changes: 9 additions & 15 deletions pokecli.py
Original file line number Diff line number Diff line change
Expand Up @@ -116,24 +116,18 @@ def main():
# set player position on the earth
api.set_position(*position)

if not api.login(config.auth_service, config.username, config.password, app_simulation = True):
return
# new authentication initialitation
api.set_authentication(provider = config.auth_service, username = config.username, password = config.password)

# get player profile call (single command example)
# ----------------------
response_dict = api.get_player()
print('Response dictionary (get_player): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict)))
# provide the path for your encrypt dll
api.activate_signature("encrypt.dll")

# sleep due to server-side throttling
time.sleep(0.2)
# print get maps object
cell_ids = util.get_cell_ids(position[0], position[1])
timestamps = [0,] * len(cell_ids)
response_dict = api.get_map_objects(latitude =position[0], longitude = position[1], since_timestamp_ms = timestamps, cell_id = cell_ids)
print('Response dictionary (get_player): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict)))

# get player profile + inventory call (thread-safe/chaining example)
# ----------------------
req = api.create_request()
req.get_player()
req.get_inventory()
response_dict = req.call()
print('Response dictionary (get_player + get_inventory): \n\r{}'.format(pprint.PrettyPrinter(indent=4).pformat(response_dict)))

if __name__ == '__main__':
main()
1 change: 1 addition & 0 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ requests==2.10.0
s2sphere==0.2.4
gpsoauth==0.3.0
six
xxhash