source.repr (12485B)
1b'DECRYPT\x00\x00\x00\x00\x00\x00\x00#!/usr/bin/env python3\nfrom Crypto.Cipher import AES\nfrom challenge_secrets import AES_KEY, APP_KEY\nfrom crcmod.predefined import mkPredefinedCrcFun\nfrom flask import Flask, Response, flash, g, redirect, render_template_string, request, session\nimport io\nimport logging\nimport logging.handlers\nimport os\nimport signal\nimport struct\nimport sys\n\nVERSION_NUM = 150\nVERSION_STR = "1.5.0"\nUPDATE_FILENAME = "bulb-update.py"\nLOG_FILENAME = "bulb.log"\n\nTEMPLATE = """\n<!DOCTYPE html>\n<head>\n<title>Bulb</title>\n</head>\n<body>\n <div style="text-align: center;">\n <h2>Smart Lightbulb</h2>\n <img src="static/bulb-{{current}}.png" style="width: 150px;height: 174px">\n <form>\n <button type="submit" name="value" value="{{next}}" formmethod="post" style="border: outset;">Turn {{next}}</button>\n <button type="submit" name="value" value="color" formmethod="post" style="border: outset;">Change color</button>\n </form>\n </div>\n <div>\n <form style="position: absolute;bottom: 10px">\n <button type="submit" formaction="/update" style="border: none;">Update</button>\n </form>\n <span style="position: absolute;bottom: 10px;right: 10px">Version: {{version}}</span>\n </div>\n</body>\n"""\n\nUPDATE_TEMPLATE = """\n<!DOCTYPE html>\n<head>\n<title>Update</title>\n<style>\ninput[type="submit"], input::file-selector-button {\n background: hsl(210, 98%, 80%);\n border: none;\n border-radius: 5px;\n padding: 0.85em 2.5em;\n\tcursor: pointer;\n margin-top: 0.85em;\n margin-bottom: 0.85em;\n}\n.flash {\n background: rgba(200, 50, 50, 0.5);\n border: black solid;\n display: inline-block;\n border-radius: 5px;\n padding: 10px;\n}\n</style>\n</head>\n<body>\n <div style="text-align: center;">\n <h2>Firmware Update</h2>\n <p>Current version: {{version}}</p>\n {%with messages = get_flashed_messages()%}\n {%if messages%}\n {%for message in messages%}\n <div class="flash">{{message}}</div><br>\n {%endfor%}\n {%endif%}\n {%endwith%}\n <form method=post enctype=multipart/form-data>\n <label for="file">Select update file</label>\n <input type="file" name="file">\n <br>\n <input type="submit" value="Start Update">\n </form>\n <br>\n <p style="max-width: 500px;margin: auto;">If you have any problems with the latest version please <a href="/logs">Download</a> debug files and send them to cusomer support.\n </div>\n</body>\n"""\n\nlog_file = logging.handlers.WatchedFileHandler(LOG_FILENAME)\nlog_stdout = logging.StreamHandler(sys.stdout)\nlogging.basicConfig(handlers=[log_stdout, log_file])\nlogger_switch = logging.getLogger("switch")\nlogger_switch.setLevel(logging.DEBUG)\nlogger_update = logging.getLogger("update")\nlogger_update.setLevel(logging.DEBUG)\n\napp = Flask(__name__)\napp.config["SEND_FILE_MAX_AGE_DEFAULT"] = 300\napp.secret_key = APP_KEY\n\n\n@app.route("/", methods=["GET", "POST"])\ndef index():\n if request.method == "POST":\n match request.form.get("value"):\n case "on":\n session["switch"] = True\n logger_switch.info("Switch On")\n case "off":\n session["switch"] = False\n logger_switch.info("Switch Off")\n case "color":\n session["color"] = not session.get("color")\n session["switch"] = True\n logger_switch.info("Switch Color")\n case _:\n session["switch"] = True\n pass\n return redirect("/")\n match session.get("switch", False), session.get("color", False):\n case [True, True]:\n c_val, n_val = "color", "off"\n case [True, False]:\n c_val, n_val = "on", "off"\n case [False, _]:\n c_val, n_val = "off", "on"\n return render_template_string(TEMPLATE, current=c_val, next=n_val, version=VERSION_STR)\n\n\ncrc16 = mkPredefinedCrcFun("x-25")\n\n\ndef do_update(file):\n data = file.read()\n if len(data) != 8272:\n logger_update.info("Update file has wrong length")\n return False\n magic, version, signed, vendor, product, iv, data = struct.unpack("<8sI?3x16s16s16s8208s", data)\n if magic != b"Update\\x00\\x00":\n logger_update.info("Got wrong magic value %s", magic)\n return False\n if version <= VERSION_NUM:\n logger_update.info("Disallowed downgrade from current version %d to %d", VERSION_NUM, version)\n return False\n logger_update.info("Valid update header for %s %s to version %d", vendor, product, version)\n logger_update.info("Decrypting update content")\n cipher = AES.new(AES_KEY, AES.MODE_CBC, iv)\n data = cipher.decrypt(data)\n logger_update.debug("Decrypted content: %s", data)\n magic, firmware, check_crc = struct.unpack("<8s6x8192sH", data)\n if magic != b"DECRYPT\\x00":\n logger_update.info("Wrong magic on update content %s", magic)\n return False\n calc_crc = crc16(data[:-2])\n if calc_crc != check_crc:\n logger_update.info("CRC check failed. given: %d, calculated: %d", check_crc, calc_crc)\n return False\n if signed:\n # we don\'t need this as data is encrypted\n return False\n logger_update.info("All checks passed, writing new firmware file")\n open(UPDATE_FILENAME, "wb").write(firmware.strip(b"\\x00"))\n os.chmod(UPDATE_FILENAME, 0o777)\n logger_update.info("Firmware has been written")\n return True\n\n\n@app.route("/update", methods=["GET", "POST"])\ndef update():\n if request.method == "POST":\n if "file" not in request.files:\n flash("Something went wrong")\n elif request.files["file"].filename == "":\n flash("No file selected")\n elif do_update(request.files["file"]):\n logger_update.info("Update was successful. Rebooting...")\n if "SERVER_SOFTWARE" in os.environ:\n # running under gunicorn\n os.kill(os.getppid(), signal.SIGTERM)\n else:\n os.kill(os.getpid(), signal.SIGINT)\n flash("Update successful")\n return """\n <!DOCTYPE html>\n <meta http-equiv="refresh" content="3">\n Update running please wait...\n """\n else:\n flash("Invalid update file")\n return redirect("/update")\n return render_template_string(UPDATE_TEMPLATE, version=VERSION_STR)\n\n\n@app.route("/logs")\ndef logs():\n try:\n logs = open(LOG_FILENAME, "rb").read()\n os.unlink(LOG_FILENAME)\n logger_switch.info("Log file downloaded, removing old log")\n except FileNotFoundError:\n logs = b""\n return Response(logs, mimetype="text/plain", headers={"Content-disposition": "attachment"})\n\n\nif __name__ == "__main__":\n app.run(debug=False)\n\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\xde\t'