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

Fix "Bad Authentication" and APK downloading #145

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Changes from all commits
Commits
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
56 changes: 41 additions & 15 deletions gpapi/googleplay.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@
from cryptography.hazmat.primitives.asymmetric import padding

import requests
import ssl

from urllib3.poolmanager import PoolManager
from urllib3.util import ssl_

from . import googleplay_pb2, config, utils

Expand Down Expand Up @@ -39,6 +43,27 @@
CONTENT_TYPE_PROTO = "application/x-protobuf"


class SSLContext(ssl.SSLContext):
def set_alpn_protocols(self, protocols):
"""
ALPN headers cause Google to return 403 Bad Authentication.
"""
pass

class AuthHTTPAdapter(requests.adapters.HTTPAdapter):
def init_poolmanager(self, *args, **kwargs):
"""
Secure settings from ssl.create_default_context(), but without
ssl.OP_NO_TICKET which causes Google to return 403 Bad
Authentication.
"""
context = SSLContext()
context.set_ciphers(ssl_.DEFAULT_CIPHERS)
context.verify_mode = ssl.CERT_REQUIRED
context.options &= ~ssl_.OP_NO_TICKET
self.poolmanager = PoolManager(*args, ssl_context=context, **kwargs)


class LoginError(Exception):
def __init__(self, value):
self.value = value
Expand Down Expand Up @@ -79,6 +104,8 @@ def __init__(self, locale="en_US", timezone="UTC", device_codename="bacon",
self.deviceBuilder = config.DeviceBuilder(device_codename)
self.setLocale(locale)
self.setTimezone(timezone)
self.session = requests.session()
self.session.mount('https://', AuthHTTPAdapter())

def setLocale(self, locale):
self.deviceBuilder.setLocale(locale)
Expand Down Expand Up @@ -157,7 +184,7 @@ def checkin(self, email, ac2dmToken):
request = self.deviceBuilder.getAndroidCheckinRequest()

stringRequest = request.SerializeToString()
res = requests.post(CHECKIN_URL, data=stringRequest,
res = self.session.post(CHECKIN_URL, data=stringRequest,
headers=headers, verify=ssl_verify,
proxies=self.proxies_config)
response = googleplay_pb2.AndroidCheckinResponse()
Expand All @@ -170,7 +197,7 @@ def checkin(self, email, ac2dmToken):
request.accountCookie.append("[" + email + "]")
request.accountCookie.append(ac2dmToken)
stringRequest = request.SerializeToString()
requests.post(CHECKIN_URL,
self.session.post(CHECKIN_URL,
data=stringRequest,
headers=headers,
verify=ssl_verify,
Expand All @@ -186,7 +213,7 @@ def uploadDeviceConfig(self):
upload.deviceConfiguration.CopyFrom(self.deviceBuilder.getDeviceConfig())
headers = self.getHeaders(upload_fields=True)
stringRequest = upload.SerializeToString()
response = requests.post(UPLOAD_URL, data=stringRequest,
response = self.session.post(UPLOAD_URL, data=stringRequest,
headers=headers,
verify=ssl_verify,
timeout=60,
Expand Down Expand Up @@ -219,7 +246,7 @@ def login(self, email=None, password=None, gsfId=None, authSubToken=None):
params['callerPkg'] = 'com.google.android.gms'
headers = self.deviceBuilder.getAuthHeaders(self.gsfId)
headers['app'] = 'com.google.android.gsm'
response = requests.post(AUTH_URL, data=params, verify=ssl_verify,
response = self.session.post(AUTH_URL, data=params, verify=ssl_verify,
proxies=self.proxies_config)
data = response.text.split()
params = {}
Expand Down Expand Up @@ -257,7 +284,7 @@ def getAuthSubToken(self, email, passwd):
requestParams['app'] = 'com.android.vending'
headers = self.deviceBuilder.getAuthHeaders(self.gsfId)
headers['app'] = 'com.android.vending'
response = requests.post(AUTH_URL,
response = self.session.post(AUTH_URL,
data=requestParams,
verify=ssl_verify,
headers=headers,
Expand Down Expand Up @@ -290,7 +317,7 @@ def getSecondRoundToken(self, first_token, params):
params.pop('EncryptedPasswd')
headers = self.deviceBuilder.getAuthHeaders(self.gsfId)
headers['app'] = 'com.android.vending'
response = requests.post(AUTH_URL,
response = self.session.post(AUTH_URL,
data=params,
headers=headers,
verify=ssl_verify,
Expand All @@ -316,15 +343,15 @@ def executeRequestApi2(self, path, post_data=None, content_type=CONTENT_TYPE_URL
headers["Content-Type"] = content_type

if post_data is not None:
response = requests.post(path,
response = self.session.post(path,
data=str(post_data),
headers=headers,
params=params,
verify=ssl_verify,
timeout=60,
proxies=self.proxies_config)
else:
response = requests.get(path,
response = self.session.get(path,
headers=headers,
params=params,
verify=ssl_verify,
Expand Down Expand Up @@ -497,7 +524,7 @@ def reviews(self, packageName, filterByDevice=False, sort=2,

def _deliver_data(self, url, cookies):
headers = self.getHeaders()
response = requests.get(url, headers=headers,
response = self.session.get(url, headers=headers,
cookies=cookies, verify=ssl_verify,
stream=True, timeout=60,
proxies=self.proxies_config)
Expand Down Expand Up @@ -541,7 +568,7 @@ def delivery(self, packageName, versionCode=None, offerType=1,
headers = self.getHeaders()
if downloadToken is not None:
params['dtok'] = downloadToken
response = requests.get(DELIVERY_URL, headers=headers,
response = self.session.get(DELIVERY_URL, headers=headers,
params=params, verify=ssl_verify,
timeout=60,
proxies=self.proxies_config)
Expand Down Expand Up @@ -613,8 +640,7 @@ def download(self, packageName, versionCode=None, offerType=1, expansion_files=F
params = {'ot': str(offerType),
'doc': packageName,
'vc': str(versionCode)}
self.log(packageName)
response = requests.post(PURCHASE_URL, headers=headers,
response = self.session.post(PURCHASE_URL, headers=headers,
params=params, verify=ssl_verify,
timeout=60,
proxies=self.proxies_config)
Expand All @@ -634,7 +660,7 @@ def log(self, docid):
log_request.timestamp = timestamp

string_request = log_request.SerializeToString()
response = requests.post(LOG_URL,
response = self.session.post(LOG_URL,
data=string_request,
headers=self.getHeaders(),
verify=ssl_verify,
Expand All @@ -645,7 +671,7 @@ def log(self, docid):
raise RequestError(response.commands.displayErrorMessage)

def toc(self):
response = requests.get(TOC_URL,
response = self.session.get(TOC_URL,
headers=self.getHeaders(),
verify=ssl_verify,
timeout=60,
Expand All @@ -664,7 +690,7 @@ def acceptTos(self, tosToken):
"tost": tosToken,
"toscme": "false"
}
response = requests.get(ACCEPT_TOS_URL,
response = self.session.get(ACCEPT_TOS_URL,
headers=self.getHeaders(),
params=params,
verify=ssl_verify,
Expand Down