Skip to content
This repository has been archived by the owner on Dec 15, 2018. It is now read-only.

Commit

Permalink
Merge pull request #144 from Noctem/revert
Browse files Browse the repository at this point in the history
Revert some changes
  • Loading branch information
keyphact authored Nov 8, 2016
2 parents 3a02e74 + ea6840d commit 21470ea
Show file tree
Hide file tree
Showing 11 changed files with 149 additions and 69 deletions.
15 changes: 14 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ __pycache__/

# C extensions
*.so
*.dylib
*.dll

# Distribution / packaging
.Python
Expand Down Expand Up @@ -89,4 +91,15 @@ ENV/
.ropeproject

# Personal load details
config.json
config.json
/.idea

# macOS junk
*.DS_Store
.AppleDouble
.LSOverride

# Windows Junk
Thumbs.db
ehthumbs.db
Desktop.ini
Binary file added pgoapi/lib/libniantichash-freebsd-64.so
Binary file not shown.
Binary file added pgoapi/lib/libniantichash-linux-x86-64.so
Binary file not shown.
Binary file added pgoapi/lib/libniantichash-macos-64.dylib
Binary file not shown.
Binary file added pgoapi/lib/niantichash32.dll
Binary file not shown.
Binary file added pgoapi/lib/niantichash64.dll
Binary file not shown.
25 changes: 19 additions & 6 deletions pgoapi/pgoapi.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
from pgoapi.rpc_api import RpcApi
from pgoapi.auth_ptc import AuthPtc
from pgoapi.auth_google import AuthGoogle
from pgoapi.utilities import parse_api_endpoint
from pgoapi.utilities import parse_api_endpoint, get_hash_lib_path
from pgoapi.exceptions import AuthException, NotLoggedInException, ServerBusyOrOfflineException, NoPlayerPositionSetException, EmptySubrequestChainException, AuthTokenExpiredException, ServerApiEndpointRedirectException, UnexpectedResponseException

from . import protos
Expand All @@ -60,6 +60,7 @@ def __init__(self, provider=None, oauth2_refresh_token=None, username=None, pass
self._position_alt = position_alt

self._signature_lib = None
self._hash_lib = None

self._session = requests.session()
self._session.headers.update({'User-Agent': 'Niantic App'})
Expand Down Expand Up @@ -126,12 +127,22 @@ def create_request(self):
self._position_alt, self.device_info)
return request

def activate_signature(self, lib_path):
self._signature_lib = lib_path
def activate_signature(self, signature_lib_path=None, hash_lib_path=None):
if signature_lib_path: self.set_signature_lib(signature_lib_path)
if hash_lib_path: self.set_hash_lib(hash_lib_path)

def set_signature_lib(self, signature_lib_path):
self._signature_lib = signature_lib_path

def set_hash_lib(self, hash_lib_path):
self._hash_lib = hash_lib_path

def get_signature_lib(self):
return self._signature_lib

def get_hash_lib(self):
return self._hash_lib

def __getattr__(self, func):
def function(**kwargs):
request = self.create_request()
Expand Down Expand Up @@ -227,9 +238,11 @@ def call(self):
request = RpcApi(self._auth_provider, self.device_info)
request._session = self.__parent__._session

lib_path = self.__parent__.get_signature_lib()
if lib_path is not None:
request.activate_signature(lib_path)
signature_lib_path = self.__parent__.get_signature_lib()
hash_lib_path = self.__parent__.get_hash_lib()
if not hash_lib_path:
hash_lib_path = get_hash_lib_path()
request.activate_signature(signature_lib_path, hash_lib_path)

self.log.info('Execution of RPC')
response = None
Expand Down
43 changes: 19 additions & 24 deletions pgoapi/rpc_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,17 +34,16 @@
import requests
import subprocess
import six
import ctypes

from google.protobuf import message

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, Rand48, long_to_bytes, generate_location_hash_by_seed, generate_location_hash, generate_request_hash, f2i
from pgoapi.utilities import to_camel_case, get_time, get_format_time_diff, Rand48, long_to_bytes, f2i, \
HashGenerator

from . import protos
from POGOProtos.Networking.Envelopes.RequestEnvelope_pb2 import RequestEnvelope
Expand All @@ -68,6 +67,7 @@ def __init__(self, auth_provider, device_info):
# mystical unknown6 - resolved by PokemonGoDev
self._signal_agglom_gen = False
self._signature_lib = None
self._hash_engine = None

if RpcApi.START_TIME == 0:
RpcApi.START_TIME = get_time(ms=True)
Expand All @@ -79,10 +79,11 @@ def __init__(self, auth_provider, device_info):

self.device_info = device_info

def activate_signature(self, lib_path):
def activate_signature(self, signature_lib_path, hash_lib_path):
try:
self._signal_agglom_gen = True
self._signature_lib = ctypes.cdll.LoadLibrary(lib_path)
self._signature_lib = ctypes.cdll.LoadLibrary(signature_lib_path)
self._hash_engine = HashGenerator(hash_lib_path)
except:
raise

Expand Down Expand Up @@ -208,11 +209,11 @@ def _build_main_request(self, subrequests, player_position=None):
if self._signal_agglom_gen:
sig = SignalAgglomUpdates()

sig.location_hash_by_token_seed = generate_location_hash_by_seed(ticket_serialized, request.latitude, request.longitude, request.accuracy)
sig.location_hash = generate_location_hash(request.latitude, request.longitude, request.accuracy)
sig.location_hash_by_token_seed = self._hash_engine.generate_location_hash_by_seed(ticket_serialized, request.latitude, request.longitude, request.accuracy)
sig.location_hash = self._hash_engine.generate_location_hash(request.latitude, request.longitude, request.accuracy)

for req in request.requests:
hash = generate_request_hash(ticket_serialized, req.SerializeToString())
hash = self._hash_engine.generate_request_hash(ticket_serialized, req.SerializeToString())
sig.request_hashes.append(hash)

sig.field22 = self.session_hash
Expand Down Expand Up @@ -275,7 +276,7 @@ def _build_main_request(self, subrequests, player_position=None):
sen.gravity_z = random.triangular(-1, .7, -0.8)
sen.status = 3

sig.field25 = 7363665268261373700
sig.field25 = 10038237239822475814

if self.device_info:
for key in self.device_info:
Expand All @@ -288,7 +289,7 @@ def _build_main_request(self, subrequests, player_position=None):
signal_agglom_proto = sig.SerializeToString()

sig_request = SendEncryptedSignatureRequest()
sig_request.encrypted_signature = self._generate_signature(signal_agglom_proto)
sig_request.encrypted_signature = self._generate_signature(signal_agglom_proto, sig.timestamp_ms_since_start)
plat = request.platform_requests.add()
plat.type = 6
plat.request_message = sig_request.SerializeToString()
Expand All @@ -299,20 +300,14 @@ def _build_main_request(self, subrequests, player_position=None):

return request

def _generate_signature(self, signature_plain, lib_path="encrypt.so"):
if self._signature_lib is None:
self.activate_signature(lib_path)
self._signature_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)]
def _generate_signature(self, signature_plain, iv):
self._signature_lib.argtypes = [ctypes.c_char_p, ctypes.c_size_t, ctypes.c_char_p, ctypes.POINTER(ctypes.POINTER(ctypes.c_ubyte))]
self._signature_lib.restype = ctypes.c_int

iv = os.urandom(32)

output_size = ctypes.c_size_t()

self._signature_lib.encrypt(signature_plain, len(signature_plain), iv, 32, None, ctypes.byref(output_size))
output = (ctypes.c_ubyte * output_size.value)()
self._signature_lib.encrypt(signature_plain, len(signature_plain), iv, 32, ctypes.byref(output), ctypes.byref(output_size))
signature = b''.join(list(map(lambda x: six.int2byte(x), output)))
rounded_size = len(signature_plain) + (256 - (len(signature_plain) % 256));
total_size = rounded_size + 5;
output = ctypes.POINTER(ctypes.c_ubyte * total_size)()
output_size = self._signature_lib.encrypt(signature_plain, len(signature_plain), iv, ctypes.byref(output))
signature = b''.join(list(map(lambda x: six.int2byte(x), output.contents)))
return signature

def _build_sub_requests(self, mainrequest, subrequest_list):
Expand Down
128 changes: 93 additions & 35 deletions pgoapi/utilities.py
Original file line number Diff line number Diff line change
@@ -1,34 +1,32 @@
"""
pgoapi - Pokemon Go API
Copyright (c) 2016 tjado <https://github.com/tejado>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
OR OTHER DEALINGS IN THE SOFTWARE.
Author: tjado <https://github.com/tejado>
"""

import re
import time
import struct
import ctypes
import xxhash
import logging
import os
import sys
import platform

from json import JSONEncoder
from binascii import unhexlify
Expand All @@ -40,10 +38,9 @@

log = logging.getLogger(__name__)

HASH_SEED = 0x61656632 # static hash seed from app
HASH_SEED = 0x61247FBF # static hash seed from app
EARTH_RADIUS = 6371000 # radius of Earth in meters


def f2i(float):
return struct.unpack('<Q', struct.pack('<d', float))[0]

Expand All @@ -55,6 +52,10 @@ def f2h(float):
def h2f(hex):
return struct.unpack('<d', struct.pack('<Q', int(hex,16)))[0]

def d2h(f):
hex_str = f2h(f)[2:].replace('L','')
hex_str = ("0" * (len(hex_str) % 2)) + hex_str
return unhexlify(hex_str)

def to_camel_case(value):
return ''.join(word.capitalize() if word else '_' for word in value.split('_'))
Expand Down Expand Up @@ -142,14 +143,10 @@ def long_to_bytes(val, endianness='big'):
"""
Use :ref:`string formatting` and :func:`~binascii.unhexlify` to
convert ``val``, a :func:`long`, to a byte :func:`str`.
:param long val: The value to pack
:param str endianness: The endianness of the result. ``'big'`` for
big-endian, ``'little'`` for little-endian.
If you want byte- and word-ordering to differ, you're on your own.
Using :ref:`string formatting` lets us use Python's C innards.
"""

Expand All @@ -172,27 +169,88 @@ def long_to_bytes(val, endianness='big'):

return s


def generate_location_hash_by_seed(authticket, lat, lng, acc=5):
first_hash = xxhash.xxh32(authticket, seed=HASH_SEED).intdigest()
location_bytes = d2h(lat) + d2h(lng) + d2h(acc)
loc_hash = xxhash.xxh32(location_bytes, seed=first_hash).intdigest()
return ctypes.c_int32(loc_hash).value


def generate_location_hash(lat, lng, acc=5):
location_bytes = d2h(lat) + d2h(lng) + d2h(acc)
loc_hash = xxhash.xxh32(location_bytes, seed=HASH_SEED).intdigest()
return ctypes.c_int32(loc_hash).value


def generate_request_hash(authticket, request):
first_hash = xxhash.xxh64(authticket, seed=HASH_SEED).intdigest()
req_hash = xxhash.xxh64(request, seed=first_hash).intdigest()
return ctypes.c_int64(req_hash).value


def d2h(f):
hex_str = f2h(f)[2:].replace('L','')
hex_str = ("0" * (len(hex_str) % 2)) + hex_str
return unhexlify(hex_str)
def get_hash_lib_path():
# win32 doesn't mean necessarily 32 bits
hash_lib = None
if sys.platform == "win32" or sys.platform == "cygwin":
if platform.architecture()[0] == '64bit':
hash_lib = "niantichash64.dll"
else:
hash_lib = "niantichash32.dll"
elif sys.platform == "darwin":
hash_lib = "libniantichash-macos-64.dylib"
elif os.uname()[4].startswith("arm") and platform.architecture()[0] == '32bit':
hash_lib = "libniantichash-linux-arm-32.so"
elif os.uname()[4].startswith("aarch64") and platform.architecture()[0] == '64bit':
hash_lib = "libniantichash-linux-arm-64.so"
elif sys.platform.startswith('linux'):
if "centos" in platform.platform():
if platform.architecture()[0] == '64bit':
hash_lib = "libniantichash-centos-x86-64.so"
else:
hash_lib = "libniantichash-linux-x86-32.so"
else:
if platform.architecture()[0] == '64bit':
hash_lib = "libniantichash-linux-x86-64.so"
else:
hash_lib = "libniantichash-linux-x86-32.so"
elif sys.platform.startswith('freebsd'):
hash_lib = "libniantichash-freebsd-64.so"
else:
err = "Unexpected/unsupported platform '{}'".format(sys.platform)
log.error(err)
raise Exception(err)

hash_lib_path = os.path.join(os.path.dirname(__file__), "lib", hash_lib)

if not os.path.isfile(hash_lib_path):
err = "Could not find {} hashing library {}".format(sys.platform, hash_lib_path)
log.error(err)
raise Exception(err)

return hash_lib_path


class HashGenerator:
def __init__(self, library_path):
self._hash_lib = ctypes.cdll.LoadLibrary(library_path)
self._hash_lib.compute_hash.argtypes = (ctypes.POINTER(ctypes.c_ubyte), ctypes.c_uint32)
self._hash_lib.compute_hash.restype = ctypes.c_uint64

def generate_location_hash_by_seed(self, authticket, lat, lng, acc=5):
first_hash = self.hash32(authticket, seed=HASH_SEED)
location_bytes = d2h(lat) + d2h(lng) + d2h(acc)
loc_hash = self.hash32(location_bytes, seed=first_hash)
return ctypes.c_int32(loc_hash).value

def generate_location_hash(self, lat, lng, acc=5):
location_bytes = d2h(lat) + d2h(lng) + d2h(acc)
loc_hash = self.hash32(location_bytes, seed=HASH_SEED)
return ctypes.c_int32(loc_hash).value

def generate_request_hash(self, authticket, request):
first_hash = self.hash64salt32(authticket, seed=HASH_SEED)
req_hash = self.hash64salt64(request, seed=first_hash)
return ctypes.c_int64(req_hash).value

def hash64salt32(self, buf, seed):
buf = struct.pack(">I", seed) + buf
return self.calcHash(buf)

def hash64salt64(self, buf, seed):
buf = struct.pack(">Q", seed) + buf
return self.calcHash(buf)

def hash32(self, buf, seed):
buf = struct.pack(">I", seed) + buf
hash64 = self.calcHash(buf)
signedhash64 = ctypes.c_int64(hash64)
return ctypes.c_uint(signedhash64.value).value ^ ctypes.c_uint(signedhash64.value >> 32).value

def calcHash(self, buf):
buf = list(bytearray(buf))
num_bytes = len(buf)
array_type = ctypes.c_ubyte * num_bytes

data = self._hash_lib.compute_hash(array_type(*buf), ctypes.c_uint32(num_bytes));
return ctypes.c_uint64(data).value
4 changes: 2 additions & 2 deletions pokecli.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,8 +125,8 @@ def main():
else:
api.set_authentication(provider = config.auth_service, username = config.username, password = config.password)

# provide the path for your encrypt dll
api.activate_signature("encrypt.dll")
# provide the path of your pcrypt library
api.set_signature_lib('/usr/local/lib/libpcrypt.so')

# print get maps object
cell_ids = util.get_cell_ids(position[0], position[1])
Expand Down
3 changes: 2 additions & 1 deletion setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,4 +18,5 @@
download_url = "https://github.com/tejado/pgoapi/releases",
packages = find_packages(),
install_requires = reqs,
)
package_data={'pgoapi': ['lib/*']}
)

0 comments on commit 21470ea

Please sign in to comment.