mirror of
https://github.com/flobz/psa_car_controller.git
synced 2026-08-22 01:16:14 +00:00
add strong authentification
This commit is contained in:
+30
-2
@@ -19,6 +19,7 @@ from typing import List
|
||||
import psa_connectedcar as psac
|
||||
from Trip import Trip
|
||||
from ecomix import Ecomix
|
||||
from otp.Otp import load_otp, new_otp_session, save_otp
|
||||
from psa_connectedcar import ApiClient
|
||||
from psa_connectedcar.rest import ApiException
|
||||
from MyLogger import logger
|
||||
@@ -167,6 +168,7 @@ class MyPSACC:
|
||||
}
|
||||
self.remote_token_last_update = None
|
||||
self._record_enabled = False
|
||||
self.otp = None
|
||||
self.weather_api = weather_api
|
||||
|
||||
def refresh_token(self):
|
||||
@@ -218,6 +220,28 @@ class MyPSACC:
|
||||
self.get_vehicles()
|
||||
return list(self.vehicles_list.keys())
|
||||
|
||||
def load_otp(self):
|
||||
otp_session = load_otp()
|
||||
if otp_session is None:
|
||||
self.get_sms_otp_code()
|
||||
otp_session = new_otp_session()
|
||||
return otp_session
|
||||
|
||||
def get_sms_otp_code(self):
|
||||
res = self.manager.post(
|
||||
"https://api.groupe-psa.com/applications/cvs/v4/mobile/smsCode?client_id=" + self.client_id,
|
||||
headers={
|
||||
"Connection": "Keep-Alive",
|
||||
"User-Agent": "okhttp/4.8.0",
|
||||
"x-introspect-realm": "clientsB2CPeugeot"
|
||||
})
|
||||
return res
|
||||
|
||||
def getOtpCode(self):
|
||||
otp_code = self.otp.getOtpCode()
|
||||
save_otp(self.otp)
|
||||
return otp_code
|
||||
|
||||
def get_remote_access_token(self, password):
|
||||
res = self.manager.post(remote_url + self.client_id,
|
||||
json={"grant_type": "password", "password": password},
|
||||
@@ -242,9 +266,12 @@ class MyPSACC:
|
||||
self.remote_access_token = data["access_token"]
|
||||
self.remote_refresh_token = data["refresh_token"]
|
||||
self.remote_token_last_update = datetime.now()
|
||||
return data["access_token"], data["refresh_token"]
|
||||
return res
|
||||
else:
|
||||
logger.error(f"can't refresh_remote_token: {data}")
|
||||
logger.error(f"can't refresh_remote_token: {data}\n Create a new one")
|
||||
self.remote_token_last_update = datetime.now()
|
||||
otp_code = self.getOtpCode()
|
||||
self.get_remote_access_token(otp_code)
|
||||
|
||||
def on_mqtt_connect(self, client, userdata, rc, a):
|
||||
try:
|
||||
@@ -293,6 +320,7 @@ class MyPSACC:
|
||||
logger.error(traceback.format_exc())
|
||||
|
||||
def start_mqtt(self):
|
||||
self.otp = self.load_otp()
|
||||
self.refresh_remote_token()
|
||||
self.mqtt_client = mqtt.Client(clean_session=True, protocol=mqtt.MQTTv311)
|
||||
self.mqtt_client.tls_set_context()
|
||||
|
||||
BIN
Binary file not shown.
+85
-76
@@ -1,15 +1,16 @@
|
||||
#!/usr/bin/env python3
|
||||
import json
|
||||
import os
|
||||
import shutil
|
||||
import xml.etree.ElementTree as ET
|
||||
import base64
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
|
||||
import requests
|
||||
from cryptography.hazmat.primitives import serialization
|
||||
from cryptography.hazmat.primitives.serialization import pkcs12
|
||||
|
||||
from ChargeControl import ChargeControl, ChargeControls
|
||||
from MyPSACC import MyPSACC
|
||||
from sys import argv
|
||||
import tarfile
|
||||
import sys
|
||||
import traceback
|
||||
import re
|
||||
|
||||
|
||||
@@ -40,91 +41,99 @@ def find_preferences_xml():
|
||||
return None
|
||||
|
||||
|
||||
|
||||
def save_key_to_pem(pfx_data, pfx_password):
|
||||
private_key, certificate, additional_certificates = pkcs12.load_key_and_certificates(pfx_data, bytes.fromhex(pfx_password))
|
||||
with open("public.pem", "wb") as f:
|
||||
f.write(certificate.public_bytes(encoding=serialization.Encoding.PEM))
|
||||
|
||||
with open("private.pem", "wb") as f:
|
||||
f.write(private_key.private_bytes(encoding=serialization.Encoding.PEM,
|
||||
format=serialization.PrivateFormat.TraditionalOpenSSL,
|
||||
encryption_algorithm=serialization.NoEncryption()))
|
||||
|
||||
|
||||
current_dir = os.getcwd()
|
||||
script_dir = dir_path = os.path.dirname(os.path.realpath(__file__))
|
||||
if sys.version_info < (3, 6):
|
||||
raise RuntimeError("This application requres Python 3.6+")
|
||||
|
||||
if argv[1].endswith(".apk"):
|
||||
from androguard.core.bytecodes.apk import APK
|
||||
a = APK(argv[1])
|
||||
package_name = "com.psa.mym.mypeugeot"
|
||||
resources = a.get_android_resources() # .get_strings_resources()
|
||||
client_id = resources.get_string(package_name, "PSA_API_CLIENT_ID_PROD")[1]
|
||||
client_secret = resources.get_string(package_name, "PSA_API_CLIENT_SECRET_PROD")[1]
|
||||
remote_refresh_token = None
|
||||
customer_id = None
|
||||
|
||||
else:
|
||||
if len(argv) > 2:
|
||||
password = argv[2]
|
||||
else:
|
||||
password = ""
|
||||
res = os.system("java --version")
|
||||
if res != 0:
|
||||
print("You need to install java on your computer : https://www.java.com/fr/download/")
|
||||
exit(1)
|
||||
os.system(f"java -jar {script_dir}/abe-all.jar unpack {argv[1]} backup.tar {password}")
|
||||
my_tar = tarfile.open('backup.tar')
|
||||
my_tar.extractall()
|
||||
dir = find_app_path()
|
||||
os.chdir(dir + "/sp")
|
||||
# get client id/secret
|
||||
psa_pref = find_preferences_xml()
|
||||
root = ET.parse(psa_pref).getroot()
|
||||
client_secret = getxmlvalue(root, "CEA_CLIENT_SECRET")
|
||||
client_id = getxmlvalue(root, "CEA_CLIENT_ID")
|
||||
#get customer id
|
||||
remote_info_file = "BASIC_AUTH_CVS.xml"
|
||||
root = ET.parse(remote_info_file).getroot()
|
||||
customer_id_enc = getxmlvalue(root, "CRYPTED_CUSTOMER_ID")
|
||||
customer_id = base64.b64decode(customer_id_enc).decode('utf-8')
|
||||
#get remote token
|
||||
try:
|
||||
root = ET.parse("HUTokenManager.xml").getroot()
|
||||
except FileNotFoundError:
|
||||
traceback.print_exc()
|
||||
print("Do a remote request (start preconditioning for example) to generate a remote refresh token, "
|
||||
"make a new backup and relaunch this script")
|
||||
exit(1)
|
||||
remote_enc = root[0].text
|
||||
remote_dec = json.loads(base64.b64decode(remote_enc))
|
||||
try:
|
||||
if "refresh_token" in remote_dec:
|
||||
remote_refresh_token = remote_dec["refresh_token"]
|
||||
# Mypeugeot >= 1.26
|
||||
else:
|
||||
remote_refresh_token = next(iter(remote_dec.values()))["refresh_token"]
|
||||
except:
|
||||
traceback.print_exc()
|
||||
print(remote_dec)
|
||||
|
||||
if not argv[1].endswith(".apk"):
|
||||
print("No apk given")
|
||||
exit(1)
|
||||
print("APK loading...")
|
||||
a = APK(argv[1])
|
||||
package_name = a.get_package()
|
||||
resources = a.get_android_resources() # .get_strings_resources()
|
||||
client_id = resources.get_string(package_name, "PSA_API_CLIENT_ID_PROD")[1]
|
||||
client_secret = resources.get_string(package_name, "PSA_API_CLIENT_SECRET_PROD")[1]
|
||||
HOST_BRANDID_PROD = resources.get_string(package_name, "HOST_BRANDID_PROD")[1]
|
||||
pfx_cert = a.get_file("assets/MWPMYMA1.pfx")
|
||||
remote_refresh_token = None
|
||||
print("APK loaded !")
|
||||
|
||||
|
||||
client_email = input("mypeugeot email: ")
|
||||
client_paswword = input("mypeugeot password: ")
|
||||
client_realm = input("What is the car api realm : clientsB2CPeugeot, clientsB2CCitroen, clientsB2CDS, clientsB2COpel, clientsB2CVauxhall\n")
|
||||
client_password = input("mypeugeot password: ")
|
||||
client_realm = input("What is the car api realm : clientsB2CPeugeot, clientsB2CCitroen, clientsB2CDS, clientsB2COpel, "
|
||||
"clientsB2CVauxhall\n")
|
||||
country_code = input("What is your country code ? (ex: FR, GB, DE, ES...)\n")
|
||||
|
||||
## Customer id
|
||||
site_code="AP_"+country_code+"_ESP"
|
||||
res=requests.post(HOST_BRANDID_PROD+"/GetAccessToken",
|
||||
headers={
|
||||
"Connection": "Keep-Alive",
|
||||
"Content-Type": "application/json",
|
||||
"Host": "id-dcr.peugeot.com",
|
||||
"User-Agent": "okhttp/2.3.0"
|
||||
},
|
||||
params={"jsonRequest": json.dumps({"siteCode": site_code, "culture": "fr-FR", "action": "authenticate",
|
||||
"fields": {"USR_EMAIL": {"value": client_email},
|
||||
"USR_PASSWORD": {"value": client_password}}})
|
||||
}
|
||||
)
|
||||
|
||||
token = res.json()["accessToken"]
|
||||
save_key_to_pem(pfx_cert,"")
|
||||
|
||||
res2 = requests.post("https://mw-ap-m2c.mym.awsmpsa.com/api/v1/user?culture=fr_FR&width=1080&cgu=1611656517&v=1.27.0",
|
||||
data=json.dumps({"site_code":site_code, "ticket":token}),
|
||||
headers={
|
||||
"Connection": "Keep-Alive",
|
||||
"Content-Type": "application/json;charset=UTF-8",
|
||||
"Source-Agent": "App-Android",
|
||||
"Token": token,
|
||||
"User-Agent": "okhttp/4.8.0",
|
||||
"Version": "1.27.0"
|
||||
},
|
||||
cert=("public.pem","private.pem"),
|
||||
)
|
||||
res_dict = res2.json()["success"]
|
||||
customer_id = res_dict["dealers"]["apv"]["data"]["Brand"] + "-" + res_dict["id"]
|
||||
|
||||
|
||||
# Psacc
|
||||
|
||||
psacc = MyPSACC(None, client_id, client_secret, remote_refresh_token, customer_id, client_realm)
|
||||
psacc.connect(client_email, client_paswword)
|
||||
psacc.connect(client_email, client_password)
|
||||
|
||||
os.chdir(current_dir)
|
||||
psacc.save_config(name="test.json")
|
||||
res = psacc.get_vehicles()
|
||||
print(f"\nYour vehicles: {res}")
|
||||
|
||||
## Charge control
|
||||
charge_controls = ChargeControls()
|
||||
for vin, vehicle in res.items():
|
||||
chc = ChargeControl(None, vin, 100, [0, 0])
|
||||
charge_controls.list[vin] = chc
|
||||
charge_controls.save_config(name="charge_config1.json")
|
||||
|
||||
try:
|
||||
os.remove("private.pem")
|
||||
os.remove("public.pem")
|
||||
except:
|
||||
print("Error when deleting temp files")
|
||||
|
||||
if not argv[1].endswith(".apk"):
|
||||
try:
|
||||
os.remove("backup.tar")
|
||||
shutil.rmtree('apps')
|
||||
except:
|
||||
print("Error when deleting temp files")
|
||||
charge_controls = ChargeControls()
|
||||
for vin,vehicle in res.items():
|
||||
chc = ChargeControl(None,vin,100,[0,0])
|
||||
charge_controls.list[vin] = chc
|
||||
charge_controls.save_config(name="charge_config1.json")
|
||||
|
||||
|
||||
|
||||
print("Success !!!")
|
||||
+305
@@ -0,0 +1,305 @@
|
||||
import hashlib
|
||||
import random
|
||||
import traceback
|
||||
|
||||
import requests
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from math import ceil
|
||||
|
||||
import Crypto
|
||||
|
||||
from Crypto.PublicKey import RSA
|
||||
|
||||
from collections import defaultdict
|
||||
from xml.etree import cElementTree as ET
|
||||
|
||||
from otp import oaep
|
||||
from otp.load import IWData
|
||||
import pickle
|
||||
from MyLogger import logger
|
||||
proxies = None
|
||||
|
||||
|
||||
def etree_to_dict(t):
|
||||
d = {t.tag: {} if t.attrib else None}
|
||||
children = list(t)
|
||||
if children:
|
||||
dd = defaultdict(list)
|
||||
for dc in map(etree_to_dict, children):
|
||||
for k, v in dc.items():
|
||||
dd[k].append(v)
|
||||
d = {t.tag: {k: v[0] if len(v) == 1 else v for k, v in dd.items()}}
|
||||
if t.attrib:
|
||||
d[t.tag].update(('@' + k, v) for k, v in t.attrib.items())
|
||||
if t.text:
|
||||
text = t.text.strip()
|
||||
if children or t.attrib:
|
||||
if text:
|
||||
d[t.tag]['#text'] = text
|
||||
else:
|
||||
d[t.tag] = text
|
||||
return d
|
||||
|
||||
|
||||
base36 = ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v",
|
||||
"w", "x", "y", "z", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9"]
|
||||
|
||||
|
||||
def numberToBase36(n):
|
||||
b = 36
|
||||
if n == 0:
|
||||
return [0]
|
||||
digits = ""
|
||||
while n:
|
||||
digits += base36[int(n % b)]
|
||||
n //= b
|
||||
return digits
|
||||
|
||||
|
||||
class Otp:
|
||||
OTP_TWICE = 10
|
||||
OK=0
|
||||
kPub = "11"
|
||||
exponent = int(kPub, 16)
|
||||
ACTIVATE_MODE = "activate"
|
||||
OTP_MODE = "otp"
|
||||
MS_MODE = "ms"
|
||||
iw_host = "https://otp.mpsa.com"
|
||||
def __init__(self, inweboAccessId):
|
||||
self.Kiw = None
|
||||
self.pinmode = None
|
||||
self.Kfact = None
|
||||
self.Kiw = None
|
||||
self.pinmode = None
|
||||
self.needsync = None
|
||||
self.serviceid = None
|
||||
self.alias = None
|
||||
self.iwalea = random.randbytes(16).hex()
|
||||
self.device_id = random.randbytes(8).hex()
|
||||
self.codepin = None
|
||||
self.challenge = ""
|
||||
self.action = ""
|
||||
self.s_id = None
|
||||
self.version = "0.2.11"
|
||||
self.isMac = True
|
||||
self.data = IWData(self)
|
||||
self.cipher = None
|
||||
self.macid = inweboAccessId
|
||||
self.smsCode = None
|
||||
self.mode = Otp.ACTIVATE_MODE
|
||||
self.defi = 0
|
||||
self.otp_count = 0
|
||||
|
||||
def init(self, Kfact=None, Kiw=None, pinmode=None):
|
||||
self.Kfact = Kfact
|
||||
self.pinmode = pinmode
|
||||
self.Kiw = self.decode_oeap(Kiw, self.Kfact)
|
||||
key = RSA.construct((int(self.Kiw, 16), Otp.exponent))
|
||||
self.cipher = oaep.new(key, hashAlgo=Crypto.Hash.SHA256)
|
||||
|
||||
def getSerial(self):
|
||||
return self.device_id + "/_/" + self.iwalea
|
||||
|
||||
def generateKMA(self, codepin):
|
||||
serial = self.getSerial()
|
||||
kma_str = codepin + ";" + serial
|
||||
kma = hashlib.sha256(kma_str.encode("utf-8")).hexdigest()[:32]
|
||||
return kma
|
||||
|
||||
def getR(self):
|
||||
if self.action == "upgrade":
|
||||
iw = self.data.iwK1
|
||||
# not correctly implemented
|
||||
else:
|
||||
iw = self.data.iwK0
|
||||
if self.action == "synchro":
|
||||
R2 = self.challenge + ";" + iw + ";" + self.codepin
|
||||
else:
|
||||
R2 = self.challenge + ";" + iw + ";"
|
||||
|
||||
R0 = self.challenge + ";" + iw + ";" + self.getSerial()
|
||||
R1 = self.challenge + ";" + iw + ";" + self.data.iwK1
|
||||
logger.debug(f"{R0}\n{R1}\n{R2}")
|
||||
return {"R0": hashlib.sha256(R0.encode("utf-8")).hexdigest(),
|
||||
"R1": hashlib.sha256(R1.encode("utf-8")).hexdigest(),
|
||||
"R2": hashlib.sha256(R2.encode("utf-8")).hexdigest()}
|
||||
|
||||
def decode_oeap(self, enc, key):
|
||||
modulus = int(key, 16)
|
||||
key = RSA.construct((modulus, Otp.exponent))
|
||||
cipher = oaep.new(key, hashAlgo=Crypto.Hash.SHA256)
|
||||
block_size = 128
|
||||
dec_string = ""
|
||||
enc_b = bytes.fromhex(enc)
|
||||
nb_block = ceil(len(enc_b) / block_size)
|
||||
|
||||
for x in range(0, nb_block):
|
||||
if x == nb_block - 1:
|
||||
max = len(enc_b)
|
||||
else:
|
||||
max = (1 + x) * 128
|
||||
min = x * 128
|
||||
ciphertext = cipher.decrypt(enc_b[min:max])
|
||||
dec_string += ciphertext.hex()
|
||||
logger.debug(dec_string)
|
||||
return dec_string
|
||||
|
||||
def request(self, param, setup=False):
|
||||
raw_xml = requests.get(
|
||||
f"{self.iw_host}/iwws/MAC",
|
||||
headers={
|
||||
"Connection": "Keep-Alive",
|
||||
"Host": "otp.mpsa.com",
|
||||
"User-Agent": "Dalvik/2.1.0 (Linux; U; Android 8.0.0; Android SDK built for x86_64 Build/OSR1.180418.004)"
|
||||
},
|
||||
params=param,
|
||||
proxies=proxies,
|
||||
verify=False
|
||||
).text
|
||||
try:
|
||||
raw_xml = raw_xml[raw_xml.index("?>") + 2:]
|
||||
if setup:
|
||||
return etree_to_dict(ET.XML(raw_xml))["ActionSetup"]
|
||||
else:
|
||||
return etree_to_dict(ET.XML(raw_xml))["ActionFinalize"]
|
||||
|
||||
except:
|
||||
logger.debug(raw_xml)
|
||||
raise Exception("Bad return from server")
|
||||
|
||||
def activation_start(self):
|
||||
param = {"action": "ActionSetup", "mode": self.mode, "id": self.data.iwid, "lastsync": self.data.iwTsync,
|
||||
"version": "Generator-1.0/0.2.11", "macid": self.macid}
|
||||
if self.mode == Otp.OTP_MODE:
|
||||
param.update({"sid": self.data.iwsecid})
|
||||
if self.mode == Otp.ACTIVATE_MODE:
|
||||
param.update({"code": self.smsCode})
|
||||
|
||||
xml = self.request(param, setup=True)
|
||||
if xml["err"] == "OK":
|
||||
if self.mode == Otp.ACTIVATE_MODE:
|
||||
xml_filtred = {key: xml[key] for key in ["Kiw", "Kfact", "pinmode"]}
|
||||
self.init(**xml_filtred)
|
||||
elif self.mode == Otp.OTP_MODE:
|
||||
self.challenge = xml["challenge"]
|
||||
return True
|
||||
else:
|
||||
return False
|
||||
|
||||
def activation_finalyze(self, random_bytes=None):
|
||||
|
||||
R = self.getR()
|
||||
params = {"action": "ActionFinalize", "mode": self.mode, "id": self.data.iwid, "lastsync": self.data.iwTsync,
|
||||
"version": "Generator-1.0/0.2.11",
|
||||
"lang": "fr", "ack": "", "macid": self.macid}
|
||||
if self.mode == Otp.OTP_MODE:
|
||||
params.update({"keytype": '0', "sid": self.data.iwsecid})
|
||||
|
||||
elif self.mode == Otp.ACTIVATE_MODE:
|
||||
kma_crypt = self.cipher.encrypt(bytes.fromhex(self.generateKMA(self.codepin))).hex()
|
||||
pin_crypt = self.cipher.encrypt(self.codepin.encode("utf-8")).hex()
|
||||
params.update({"serial": self.getSerial(), "code": self.smsCode,
|
||||
"Kma": kma_crypt, "pin": pin_crypt, "name": "Android SDK built for x86_64 / UNKNOWN", })
|
||||
|
||||
params.update(R)
|
||||
xml = self.request(params)
|
||||
|
||||
self.data.synchro(xml, self.generateKMA(self.codepin))
|
||||
|
||||
if self.mode == Otp.OTP_MODE:
|
||||
self.defi = str(xml["defi"])
|
||||
if "J" in xml:
|
||||
logger.debug("Need another otp request")
|
||||
return Otp.OTP_TWICE
|
||||
else:
|
||||
return Otp.OK
|
||||
|
||||
if "ms_n" not in xml or xml["ms_n"] == 0:
|
||||
logger.debug("no ms_n request needed")
|
||||
return Otp.OK
|
||||
|
||||
if int(xml["ms_n"]) > 1:
|
||||
raise NotImplementedError
|
||||
else:
|
||||
ms_n = "0"
|
||||
|
||||
self.challenge = xml["challenge"]
|
||||
self.action = "synchro"
|
||||
res = self.decode_oeap(xml["ms_key"], self.Kfact)
|
||||
temp_key = RSA.construct((int(res, 16), self.exponent))
|
||||
temp_cipher = oaep.new(temp_key, hashAlgo=Crypto.Hash.SHA256)
|
||||
if random_bytes is None:
|
||||
random_bytes = random.randbytes(16)
|
||||
KpubEncode = temp_cipher.encrypt(random_bytes)
|
||||
|
||||
aes_cipher = AES.new(bytes.fromhex(self.generateKMA(self.codepin)), AES.MODE_ECB)
|
||||
encodeAesFromHex = aes_cipher.encrypt(random_bytes).hex()
|
||||
self.data.iwsecval = encodeAesFromHex
|
||||
self.data.iwsecid = xml["s_id"]
|
||||
self.data.iwsecn = 1
|
||||
|
||||
req_param = {"action": "ActionFinalize", "mode": Otp.MS_MODE, "ms_id" + ms_n: xml["ms_id"],
|
||||
"ms_val" + ms_n: KpubEncode.hex(), "macid": self.macid}
|
||||
req_param.update({"id": self.data.iwid, "lastsync": self.data.iwTsync, "ms_n": 1})
|
||||
req_param.update(self.getR())
|
||||
xml = self.request(req_param)
|
||||
self.data.synchro(xml, self.generateKMA(self.codepin))
|
||||
return Otp.OK
|
||||
|
||||
def _getOtpCode(self):
|
||||
password = self.data.iwK1 + ":" + self.defi + ":" + self.data.iwsecval
|
||||
res = bytes(hashlib.sha256(password.encode("utf-8")).digest())
|
||||
nb = ((int.from_bytes(res[:4], byteorder="big") & 0xfffffff) * 1024) + (
|
||||
int.from_bytes(res[4:8], byteorder="big") & 1023)
|
||||
otp = numberToBase36(nb)
|
||||
return otp
|
||||
|
||||
def getOtpCode(self):
|
||||
self.mode = Otp.OTP_MODE
|
||||
self.activation_start()
|
||||
res = self.activation_finalyze()
|
||||
if res == Otp.OTP_TWICE:
|
||||
self.mode = Otp.OTP_MODE
|
||||
self.activation_start()
|
||||
self.activation_finalyze()
|
||||
otp_code = self._getOtpCode()
|
||||
logger.debug(f"otp code: {otp_code}")
|
||||
return otp_code
|
||||
|
||||
def __getstate__(self):
|
||||
odict = self.__dict__.copy()
|
||||
del odict['cipher'] # don't pickle this
|
||||
return odict
|
||||
|
||||
def __setstate__(self, dict):
|
||||
self.__dict__.update(dict)
|
||||
if self.Kiw is not None:
|
||||
key = RSA.construct((int(self.Kiw, 16), Otp.exponent))
|
||||
self.cipher = oaep.new(key, hashAlgo=Crypto.Hash.SHA256)
|
||||
|
||||
|
||||
def encode_oeap(text, key):
|
||||
cipher = oaep.new(bytes.fromhex(key), hashAlgo=Crypto.Hash.SHA256)
|
||||
return cipher.encrypt(text)
|
||||
|
||||
def save_otp(obj):
|
||||
with open("otp.bin", 'wb') as output:
|
||||
pickle.dump(obj, output)
|
||||
|
||||
def load_otp():
|
||||
try:
|
||||
with open("otp.bin", 'rb') as input:
|
||||
return pickle.load(input)
|
||||
except:
|
||||
logger.debug(traceback.format_exc())
|
||||
return None
|
||||
|
||||
def new_otp_session():
|
||||
otp = Otp("bb8e981582b0f31353108fb020bead1c")
|
||||
otp.smsCode = input("What is the code you just received by SMS ?")
|
||||
otp.codepin = input("What is your app pin code ?")
|
||||
otp.activation_start()
|
||||
otp.activation_finalyze()
|
||||
save_otp(otp)
|
||||
return otp
|
||||
@@ -0,0 +1,41 @@
|
||||
from locale import atoi
|
||||
|
||||
|
||||
class Tokenizer(object):
|
||||
def __init__(self, str, delimiter="&&"):
|
||||
self.s:str = str
|
||||
self.delimiter = delimiter
|
||||
self.currentIndex = 0
|
||||
|
||||
def nextToken(self):
|
||||
if self.currentIndex >= len(self.s):
|
||||
return ""
|
||||
indexOf = self.currentIndex+self.s[self.currentIndex:].index(self.delimiter)
|
||||
if indexOf == -1:
|
||||
substring = self.s[self.currentIndex:]
|
||||
self.currentIndex = self.s.length()
|
||||
return substring
|
||||
|
||||
substring2 = self.s[self.currentIndex:indexOf]
|
||||
self.currentIndex = indexOf + len(self.delimiter)
|
||||
#print(f"{substring2} index:{self.currentIndex}")
|
||||
return substring2
|
||||
|
||||
def nextTokenI(self):
|
||||
token = self.nextToken()
|
||||
if token == "":
|
||||
return 0
|
||||
else:
|
||||
return int(token, 16)
|
||||
|
||||
def hasMoreTokens(self):
|
||||
return self.currentIndex < len(self.s)
|
||||
|
||||
# a="0.2.11&&&&&&0&&0&&0&&9f13ba238fbabba08e85d93638e98ef5e48682a9d3e5bc325c3dd6fac8199a6ce09e9b4f373aa6a75a905c3d690f6e3335d1e8e5b748ecec3020a794149033f6ada6896db6d73b8d43b8365bbe15b9ac66f49d4e684a3628f1e9f3deda0c4e24aba771946e6085b92c5ad312477152acf8db01e6aea4b409d5ac1a05c2fd4e95&&0&&&&&&&&&&&&0&&0&&0&&0&&0&&0&&0&&&&&&&&0&&0&&0&&0&&0&&2.0.0&&http://m.inwebo.com/&&"
|
||||
# t=Tokenizer(a)
|
||||
# self = t
|
||||
# t.nextToken()
|
||||
# t.nextToken()
|
||||
# t.currentIndex
|
||||
# t.nextTokenI()
|
||||
# atoi("")
|
||||
+175
@@ -0,0 +1,175 @@
|
||||
import hashlib
|
||||
from locale import atoi
|
||||
from time import time
|
||||
|
||||
from Crypto.Cipher import AES
|
||||
|
||||
from otp.Tokenizer import Tokenizer
|
||||
|
||||
default_token="0.2.11&&&&&&0&&0&&0&&9f13ba238fbabba08e85d93638e98ef5e48682a9d3e5bc325c3dd6fac8199a6ce09e9b4f373aa6a75a905c3d690f6e3335d1e8e5b748ecec3020a794149033f6ada6896db6d73b8d43b8365bbe15b9ac66f49d4e684a3628f1e9f3deda0c4e24aba771946e6085b92c5ad312477152acf8db01e6aea4b409d5ac1a05c2fd4e95&&0&&&&&&&&&&&&0&&0&&0&&0&&0&&0&&0&&&&&&&&0&&0&&0&&0&&0&&2.0.0&&http://m.inwebo.com/&&"
|
||||
default_version="529"
|
||||
|
||||
def filterLoad(string:str):
|
||||
return string.replace("&", "&")
|
||||
|
||||
class IWData(object):
|
||||
def __init__(self,IW):
|
||||
self.IW=IW
|
||||
self.tokenizer = Tokenizer(default_token)
|
||||
self.tokenizer.nextToken()
|
||||
self.load1xx(int(default_version), self.tokenizer)
|
||||
|
||||
def load1xx(self, j, tokenizer):
|
||||
self.iwid = tokenizer.nextToken()
|
||||
self.iwalea = tokenizer.nextToken()
|
||||
self.iwblocked = tokenizer.nextTokenI()
|
||||
if j >= 519:
|
||||
self.iwhasnopin = tokenizer.nextTokenI()
|
||||
self.iwTsync = tokenizer.nextTokenI()
|
||||
self.kfact = tokenizer.nextToken()
|
||||
if self.IW.isMac:
|
||||
self.iwconnected = tokenizer.nextTokenI()
|
||||
self.iwserver = tokenizer.nextToken()
|
||||
self.iwJ = tokenizer.nextToken()
|
||||
self.iwK = tokenizer.nextToken()
|
||||
self.iwK0 = tokenizer.nextToken()
|
||||
self.iwK1 = tokenizer.nextToken()
|
||||
self.iwTref = tokenizer.nextTokenI()
|
||||
self.iwcancelpin = tokenizer.nextTokenI()
|
||||
self.iwnboka = tokenizer.nextTokenI()
|
||||
self.iwlastt1 = tokenizer.nextTokenI()
|
||||
self.iwlastt2 = tokenizer.nextTokenI()
|
||||
self.iwlastt3 = tokenizer.nextTokenI()
|
||||
self.iwlastbp = tokenizer.nextTokenI()
|
||||
self.iwstackrand = tokenizer.nextToken()
|
||||
self.iwstack = tokenizer.nextToken()
|
||||
self.iwH = tokenizer.nextToken()
|
||||
nextTokenI = tokenizer.nextTokenI()
|
||||
self.iwsrvn = nextTokenI
|
||||
self.iwsrvid = [None]*(nextTokenI)
|
||||
self.iwsrvname = [None]*(nextTokenI)
|
||||
self.iwsrvlogo = [None]*(nextTokenI)
|
||||
self.iwsrvurl = [None]*(nextTokenI)
|
||||
self.iwsrvonlineotp = [None]*(nextTokenI)
|
||||
if self.IW.isMac:
|
||||
self.iwsrvconnected = [None]*(self.iwsrvn)
|
||||
j2 = self.iwsrvn
|
||||
self.iwsrvsecure = [None]*(j2)
|
||||
self.iwsrvksc = [None]*(j2)
|
||||
i = 0
|
||||
while i < self.iwsrvn:
|
||||
self.iwsrvid[i] = tokenizer.nextToken()
|
||||
self.iwsrvname[i] = filterLoad(tokenizer.nextToken())
|
||||
self.iwsrvlogo[i] = filterLoad(tokenizer.nextToken())
|
||||
if self.IW.isMac:
|
||||
self.iwsrvconnected[i] = tokenizer.nextTokenI()
|
||||
if j>515:
|
||||
i2 = 1
|
||||
elif j ==515:
|
||||
i2=0
|
||||
else:
|
||||
i2=-1
|
||||
if i2 < 0 or self.IW.isMac:
|
||||
self.iwsrvurl[i] = ""
|
||||
else:
|
||||
self.iwsrvurl[i] = filterLoad(tokenizer.nextToken())
|
||||
if j < 520 or self.IW.isMac:
|
||||
self.iwsrvonlineotp[i] = 0
|
||||
else:
|
||||
self.iwsrvonlineotp[i] = tokenizer.nextTokenI()
|
||||
self.iwsrvsecure[i] = tokenizer.nextToken()
|
||||
if i2 < 0 or not self.IW.isMac:
|
||||
self.iwsrvksc[i] = ""
|
||||
else:
|
||||
self.iwsrvksc[i] = tokenizer.nextToken()
|
||||
i += 1
|
||||
nextTokenI2 = tokenizer.nextTokenI()
|
||||
self.iwsecn = nextTokenI2
|
||||
self.iwsecid = [None]*(nextTokenI2)
|
||||
self.iwsecval = [None]*(nextTokenI2)
|
||||
i3 = 0
|
||||
while ((i3)) < self.iwsecn:
|
||||
self.iwsecid[i3] = tokenizer.nextToken()
|
||||
self.iwsecval[i3] = tokenizer.nextToken()
|
||||
i3 += 1
|
||||
self.iwmsgn = tokenizer.nextTokenI()
|
||||
self.iwmsgtime = tokenizer.nextTokenI()
|
||||
self.iwmsgid =""
|
||||
self.iwmsgtitle =""
|
||||
self.iwmsgcontent = ""
|
||||
self.iwmsgack = ""
|
||||
i4 = 0
|
||||
while i4 < self.iwmsgn:
|
||||
self.iwmsgid+= tokenizer.nextToken()
|
||||
self.iwmsgtitle+= filterLoad(tokenizer.nextToken())
|
||||
self.iwmsgcontent+=filterLoad(tokenizer.nextToken())
|
||||
self.iwmsgack += tokenizer.nextTokenI()
|
||||
i4 += 1
|
||||
self.iwmajorversion = tokenizer.nextTokenI()
|
||||
self.iwnewversion = filterLoad(tokenizer.nextToken())
|
||||
self.iwnewversionurl = filterLoad(tokenizer.nextToken())
|
||||
self.mustupgrade = False
|
||||
self.datatouch = 0
|
||||
|
||||
def synchro(self, ixml:dict, key):
|
||||
aes_cipher = AES.new(bytes.fromhex(key), AES.MODE_ECB)
|
||||
""" generated source for method synchro """
|
||||
value = ixml.get("id")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwid = value
|
||||
value = ixml.get("server")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwserver = value
|
||||
value = ixml.get("K0")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwK0 = aes_cipher.decrypt(bytes.fromhex(value)).hex()
|
||||
value = ixml.get("K1")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwK1 = aes_cipher.decrypt(bytes.fromhex(value)).hex()
|
||||
value = ixml.get("dK1")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwK1 = hashlib.sha256(("" + self.iwK1 + ";" + value).encode("utf-8")).hexdigest()[0:32]
|
||||
value = ixml.get("J")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwJ = value
|
||||
self.iwTref = int(time())
|
||||
self.IW.otpRetryService = -1
|
||||
value = ixml.get("K")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwK = value
|
||||
value = ixml.get("H")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwH = aes_cipher.decrypt(bytes.fromhex(value))
|
||||
value = ixml.get("connected")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwconnected = atoi(value) + int(time())
|
||||
value = ixml.get("s_n")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwcancelpin = 0
|
||||
self.iwnboka = 0
|
||||
self.iwlastt1 = 0
|
||||
self.iwlastt2 = 0
|
||||
self.iwlastt3 = 0
|
||||
self.iwlastbp = 0
|
||||
self.iwstackrand = ""
|
||||
self.iwstack = ""
|
||||
self.iwTsync = ixml.get("Tsync")
|
||||
self.iwsrvn = ixml.get("s_n")
|
||||
self.iwsrvid = ixml.get("s_id")
|
||||
self.iwsrvname = ixml.get("s_name")
|
||||
self.iwsrvlogo = ixml.get("s_icon")
|
||||
self.iwsrvconnected = ixml.get("s_connected")
|
||||
self.iwsrvksc = ixml.get("s_ksc")
|
||||
self.iwsrvsecure = ixml.get("s_secure")
|
||||
self.iwsrvurl = ixml.get("s_url")
|
||||
self.iwsrvonlineotp = ixml.get("s_onlineotp")
|
||||
self.IW.synchroJustDone = 1
|
||||
value = ixml.get("m_n")
|
||||
if value is not None and len(value) > 0:
|
||||
self.iwmsgtime = int(time())
|
||||
self.iwmsgn = ixml.get("m_n")
|
||||
self.iwmsgid = ixml.get("m_id")
|
||||
self.iwmsgtitle = ixml.get("m_title")
|
||||
self.iwmsgcontent = ixml.get("m_content")
|
||||
self.iwmsgack = ixml.get("m_ack")
|
||||
self.datatouch = 1
|
||||
@@ -12,6 +12,7 @@ flask
|
||||
dash_bootstrap_components
|
||||
geojson
|
||||
reverse_geocode
|
||||
androguard
|
||||
|
||||
#swagger req
|
||||
certifi >= 14.05.14
|
||||
|
||||
Reference in New Issue
Block a user