diff --git a/Dockerfile b/Dockerfile index 54fdce1..521d307 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,4 +1,4 @@ -FROM phusion/baseimage:0.11 +FROM phusion/baseimage:master RUN apt-get update && apt-get install -y sudo iproute2 iputils-ping diff --git a/docker/bin/stop b/docker/bin/stop index a51a01f..355829b 100755 --- a/docker/bin/stop +++ b/docker/bin/stop @@ -1,3 +1,2 @@ #!/bin/bash -cd /usr/bin/tuya-convert -./stop_flash.sh +pkill -SIGINT -f start_flash.sh diff --git a/install_prereq.sh b/install_prereq.sh index a9dce81..1a0be8c 100755 --- a/install_prereq.sh +++ b/install_prereq.sh @@ -5,6 +5,6 @@ set -e sudo apt-get update sudo apt-get install -y git iw dnsmasq hostapd screen curl build-essential python3-pip python3-setuptools python3-wheel python3-dev mosquitto haveged net-tools libssl-dev -sudo -H python3 -m pip install paho-mqtt tornado git+https://github.com/drbild/sslpsk.git@use-byte-string-for-identity-hints pycryptodomex +sudo -H python3 -m pip install --upgrade paho-mqtt tornado git+https://github.com/drbild/sslpsk.git@use-byte-string-for-identity-hints pycryptodomex echo "Ready to start upgrade" diff --git a/scripts/fake-registration-server.py b/scripts/fake-registration-server.py index 9023b98..74417a1 100755 --- a/scripts/fake-registration-server.py +++ b/scripts/fake-registration-server.py @@ -27,8 +27,8 @@ def exit_cleanly(signal, frame): from Cryptodome.Cipher import AES pad = lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16) unpad = lambda s: s[:-ord(s[len(s) - 1:])] -encrypt = lambda msg, key: AES.new(key.encode(), AES.MODE_ECB).encrypt(pad(msg).encode()) -decrypt = lambda msg, key: unpad(AES.new(key.encode(), AES.MODE_ECB).decrypt(msg.encode())) +encrypt = lambda msg, key: AES.new(key, AES.MODE_ECB).encrypt(pad(msg).encode()) +decrypt = lambda msg, key: unpad(AES.new(key, AES.MODE_ECB).decrypt(msg)).decode() from base64 import b64encode import hashlib @@ -84,7 +84,7 @@ def reply(self, result=None, encrypted=False): 't': ts, 'success': True } answer = jsonstr(answer) - payload = b64encode(encrypt(answer, options.secKey)).decode() + payload = b64encode(encrypt(answer, options.secKey.encode())).decode() signature = "result=%s||t=%d||%s" % (payload, ts, options.secKey) signature = hashlib.md5(signature.encode()).hexdigest()[8:24] answer = { @@ -115,7 +115,7 @@ def post(self): print(self.request.headers) if payload: try: - decrypted_payload = decrypt(binascii.unhexlify(payload), options.secKey).decode() + decrypted_payload = decrypt(binascii.unhexlify(payload), options.secKey.encode()) if decrypted_payload[0] != "{": raise ValueError("payload is not JSON") print("payload", decrypted_payload) diff --git a/scripts/mq_pub_15.py b/scripts/mq_pub_15.py index 3617169..ccb4311 100755 --- a/scripts/mq_pub_15.py +++ b/scripts/mq_pub_15.py @@ -21,15 +21,15 @@ from Cryptodome.Cipher import AES pad = lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16) unpad = lambda s: s[:-ord(s[len(s) - 1:])] -encrypt = lambda msg, key: AES.new(key.encode(), AES.MODE_ECB).encrypt(pad(msg).encode()) -decrypt = lambda msg, key: unpad(AES.new(key.encode(), AES.MODE_ECB).decrypt(msg.encode())) +encrypt = lambda msg, key: AES.new(key, AES.MODE_ECB).encrypt(pad(msg).encode()) +decrypt = lambda msg, key: unpad(AES.new(key, AES.MODE_ECB).decrypt(msg)).decode() def iot_dec(message, local_key): - message_clear = decrypt(base64.b64decode(message[19:]), local_key) + message_clear = decrypt(base64.b64decode(message[19:]), local_key.encode()) print (message_clear) return message_clear def iot_enc(message, local_key, protocol): - messge_enc = encrypt(message, local_key) + messge_enc = encrypt(message, local_key.encode()) if protocol == "2.1": messge_enc = base64.b64encode(messge_enc) signature = b'data=' + messge_enc + b'||pv=' + protocol.encode() + b'||' + local_key.encode() diff --git a/scripts/psk-frontend.py b/scripts/psk-frontend.py index 586f616..68a4099 100755 --- a/scripts/psk-frontend.py +++ b/scripts/psk-frontend.py @@ -71,6 +71,8 @@ def new_client(self, s1): print("could not establish sslpsk socket:", e) if "NO_SHARED_CIPHER" in e.reason or "WRONG_VERSION_NUMBER" in e.reason or "WRONG_SSL_VERSION" in e.reason: print("don't panic this is probably just your phone!") + except Exception as e: + print(e) def data_ready_cb(self, s): if s == self.server_sock: _s, frm = s.accept() diff --git a/scripts/tuya-discovery.py b/scripts/tuya-discovery.py index 9d0981c..1a9feaa 100755 --- a/scripts/tuya-discovery.py +++ b/scripts/tuya-discovery.py @@ -12,28 +12,39 @@ from Cryptodome.Cipher import AES pad = lambda s: s + (16 - len(s) % 16) * chr(16 - len(s) % 16) unpad = lambda s: s[:-ord(s[len(s) - 1:])] -encrypt = lambda msg, key: AES.new(key.encode(), AES.MODE_ECB).encrypt(pad(msg).encode()) -decrypt = lambda msg, key: unpad(AES.new(key.encode(), AES.MODE_ECB).decrypt(msg.encode())) +encrypt = lambda msg, key: AES.new(key, AES.MODE_ECB).encrypt(pad(msg).encode()) +decrypt = lambda msg, key: unpad(AES.new(key, AES.MODE_ECB).decrypt(msg)).decode() from hashlib import md5 udpkey = md5(b"yGAdlopoPVldABfn").digest() decrypt_udp = lambda msg: decrypt(msg, udpkey) +devices_seen = set() + class TuyaDiscovery(asyncio.DatagramProtocol): def datagram_received(self, data, addr): + # ignore devices we've already seen + if data in devices_seen: + return + devices_seen.add(data) # remove message frame data = data[20:-8] # decrypt if encrypted try: data = decrypt_udp(data) except: - pass + data = data.decode() + print(addr[0], data) # parse json try: data = json.loads(data) + # there is a typo present only in Tuya SDKs for non-ESP devices ("ablilty") + # it is spelled correctly in the Tuya SDK for the ESP ("ability") + # we can use this as a clue to report unsupported devices + if "ablilty" in data: + print("WARNING: it appears this device does not use an ESP82xx and therefore cannot install ESP based firmware") except: pass - print(addr[0], data) def main(): loop = asyncio.get_event_loop() diff --git a/start_flash.sh b/start_flash.sh index 313024f..e889e4c 100755 --- a/start_flash.sh +++ b/start_flash.sh @@ -4,6 +4,7 @@ normal=$(tput sgr0) . ./config.txt setup () { + echo "tuya-convert $(git describe --tags)" pushd scripts >/dev/null || exit . ./setup_checks.sh screen_minor=$(screen --version | cut -d . -f 2)