{"id":619,"date":"2021-07-12T14:44:42","date_gmt":"2021-07-12T12:44:42","guid":{"rendered":"https:\/\/www.iot-embedded.de\/iot-2021\/?p=619"},"modified":"2021-07-13T02:58:37","modified_gmt":"2021-07-13T00:58:37","slug":"signierte-und-verschluesselte-ota-updates-via-tls","status":"publish","type":"post","link":"https:\/\/www.iot-embedded.de\/iot-2021\/beverage-monitoring\/signierte-und-verschluesselte-ota-updates-via-tls\/","title":{"rendered":"Signierte und verschl\u00fcsselte OTA Updates via TLS"},"content":{"rendered":"\n<p>In diesem Blogbeitrag besch\u00e4ftigen wir uns mit dem verschl\u00fcsselten \u00dcbertragen von Update Dateien. Dies ist w\u00fcnschenswert, da in den momentanen Dateien Informationen wie Anmeldedaten f\u00fcr das lokale Netzwerk, als auch Adressen von weiterem Server beinhaltet ist (Update Server, sp\u00e4ter auch MQTT Server). Um dies zu erreichen, brauchen wir:<\/p>\n\n\n\n<ul><li>Einen TLS f\u00e4higen HTTP Server als Update Server: Hierf\u00fcr nutzen wir NodeJS, eine andere M\u00f6glichkeit w\u00e4re bspw. ein nginx Webserver, usw.<\/li><li>Wieder ein paar Zertifikate und Keys: Hier k\u00f6nnen wir vorerst Zertifikate und Keys aus dem MQTT Blogbeitrag nutzen. Wie diese erstellt werden, wird in dem anderen Beitrag beschrieben.<\/li><li>Angepasste Logik auf dem Mikrocontroller, welche \u00fcberpr\u00fcft, ob der Update Server ein valides Zertifikat hat und mit diesem nur verschl\u00fcsselt, kommuniziert: Um eine sichere Anfrage zu stellen, m\u00fcssen wir die Verschl\u00fcsselung nicht selbst implementieren, aber es bedarf dennoch einiger Erweiterungen, um beispielsweise das Zertifikat der CA (Certificate Authority) dem WiFi Client zu \u00fcbergeben. Zus\u00e4tzlich zum signierte OTA Update m\u00fcssen wir vor dem Flashen:<ul><li>Ein LittleFS Dateisystem mit dem Zertifikat der CA erstellen<\/li><li>Das Dateisystem auf den ESP laden<\/li><\/ul>Zur Laufzeit:<ul><li>Die Zertifikate aus dem Dateisystem laden<\/li><li>Die lokale Zeit setzen<\/li><li>Einen WiFiClient erstellen welcher HTTPS unterst\u00fctzt<\/li><li>Eine update Anfrage stellen<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>Zu Beginn k\u00fcmmern wir uns schnell um einen Update Server welcher TLS unterst\u00fctzt. Wir haben uns vorerst f\u00fcr einen Server basierend auf NodeJS in Kombination mit ExpressJS entschieden. Andere Sprachen und Frameworks, oder etablierte HTTP Server wie nginx, oder der Apache HTTP Server w\u00e4ren auch m\u00f6glich. Der Vorteil von NodeJS ist das simple Aufsetzen und hoffentlich das einfache verpacken in einen Container.<\/p>\n\n\n\n<p>Hierf\u00fcr brauchen wir zum einen, <code>NodeJS<\/code> als auch den Node Packet Manager <code>npm<\/code>. Wir erstellen einen neuen Ordner <code>update_server_tls<\/code> in welchem wir das Projekt mit <code>npm init<\/code> initialisieren und geben <code>npm<\/code> ein paar Infos \u00fcber unser Projekt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ mkdir update_server_tls\n$ npm init\nThis utility will walk you through creating a package.json file.\nIt only covers the most common items, and tries to guess sensible defaults.\n\nSee `npm help init` for definitive documentation on these fields\nand exactly what they do.\n\nUse `npm install &lt;pkg&gt;` afterwards to install a package and\nsave it as a dependency in the package.json file.\n\nPress ^C at any time to quit.\npackage name: (update_server_tls)\nversion: (1.0.0)\ndescription:\nentry point: (index.js) server.js\ntest command:\ngit repository:\nkeywords:\nauthor:\nlicense: (ISC)\nAbout to write to ...\/update_server_tls\/package.json:\n\n{\n  \"name\": \"update_server_tls\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"server.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\"\n}\n\nIs this OK? (yes)<\/code><\/pre>\n\n\n\n<p>Nun erstellen wir die <code>server.js<\/code> Datei in welchem der Code f\u00fcr unseren Update Server liegen wird. Zu Beginn importieren wir ben\u00f6tigte Module. Diese m\u00fcssen sp\u00e4ter noch installiert werden. Im weiteren Code erstellen wir zum einen den https Server, welche das Zertifikat der CA, sowie Server Key und Zertifikat einliest. Key und Zertifikat lade ich aus dem Ordner in welchem die Dateien aus dem MQTT Blogbeitrag liegen. Des Weiteren m\u00fcssen wir noch die Logik angeben, welche auf die GET Anfrage des ESPs reagiert. Hier wird dann die gew\u00fcnschte Update Datei gelesen und zur\u00fcckgegeben.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ touch server.js\n$ cat server.js<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code>const fs = require('fs');\nconst https = require('https');\nconst express = require('express');\nconst path = require('path');\n\nconst app = express();\n\nvar PORT = 8080;\n  \n\/\/ Without middleware\napp.get('\/firmware.bin.signed', function(_req, res, next){\n    var options = {\n        root: path.join(__dirname)\n    };\n      \n    var fileName = 'firmware.bin.signed';\n\n    res.sendFile(fileName, options, function (err) {\n        if (err) {\n            next(err);\n        } else {\n            console.log('Sent:', fileName);\n        }\n    });\n});\n  \nhttps\n  .createServer(\n    {\n      \/\/ ...\n      cert: fs.readFileSync('..\/..\/tls-mqtt\/server\/server.crt'),\n      key: fs.readFileSync('..\/..\/tls-mqtt\/server\/server.key'),\n      \/\/ ...\n    },\n    app\n  )\n  .listen(PORT);<\/code><\/pre>\n\n\n\n<p>Nun installieren wir ben\u00f6tigte Module, und erstellen noch ein Script innerhalb der \u2018package.json\u2019 Datei (<code>\"start\": \"node server.js\"<\/code>) um dann mit <code>npm start<\/code> den Update Server starten zu k\u00f6nnen.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ npm install fs\n$ npm install https\n$ npm install express\n$ npm install path\n$ cat package.json\n{\n  \"name\": \"update_server_tls\",\n  \"version\": \"1.0.0\",\n  \"description\": \"\",\n  \"main\": \"server.js\",\n  \"scripts\": {\n    \"test\": \"echo \\\"Error: no test specified\\\" &amp;&amp; exit 1\",\n    \"start\": \"node server.js\"\n  },\n  \"author\": \"\",\n  \"license\": \"ISC\",\n  \"dependencies\": {\n    \"express\": \"^4.17.1\",\n    \"fs\": \"^0.0.1-security\",\n    \"https\": \"^1.0.0\",\n    \"path\": \"^0.12.7\"\n  }\n}<\/code><\/pre>\n\n\n\n<p>Damit der Server auch die Datei auf Anfrage senden kann muss der Pfad zur Datei stimmen. Momentan erwartet der Server die Datei unter dem Pfad \u2019.\/firmware.bin.signed`. Damit diese auch von hier geladen werden kann sollten wir sie dort platzieren. Ich nutze wieder das Blink Beispiel, welches wir im Beitrag zu signierten Updates schon signiert haben. Dieses kopiere ich vom Build Ordner in den Ordner, welcher den Update Server beinhaltet.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ # Gehe in das Projekt Verzeichnis mit dem Blink Code\n$ cd ${BLINK_EXAMPLE}\n$ # Baue das Artefakt und nutze unser Makefile aus dem Blogbeitrag, um das Artefakt zu signieren\n$ make \n$ # Kopieren des Artefaktes in den Update Server Ordner\n$ cp ${BLINK_EXAMPLE}\/.pio\/build\/nodemcuv2\/firmware.bin.signed ${UPDATE_SERVER_TLS}\/firmware.bin.signed\n$ cd ${UPDATE_SERVER_TLS}\n$ ls\nfirmware.bin.signed\nnode_modules\npackage-lock.json\npackage.json\nserver.js<\/code><\/pre>\n\n\n\n<p>Um den Update Server zu testen k\u00f6nnen wir in einmal ausf\u00fchren und mit <code>curl<\/code> anfragen. Wir starten den Server mit <code>npm start<\/code> und in einem anderen Terminal rufen wir <code>curl<\/code> auf.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cd ${UPDATE_SERVER_TLS}\n$ npm start\n---\n$ cd ${UPDATE_SERVER_TLS}\n$ # Wir nutzen nun https\n$ curl https:\/\/192.168.178.123:8080 --cacert ..\/..\/tls-mqtt\/ca.crt &gt; test.bin.signed\n$ diff firmware.bin.signed  test.bin.signed\n$ # Versuchen wir \u00fcber http anzufragen\n$ curl http:\/\/192.168.178.123:8080 --cacert ..\/..\/tls-mqtt\/ca.crt &gt; should-be-empty.txt\n$ cat should-be-empty.txt\n$ du -b should-be-empty.txt\n0       should-be-empty.txt<\/code><\/pre>\n\n\n\n<p>Damit haben wir nun ein funktionierender Update Server, welcher uns die signierte Update Datei nur \u00fcber HTTPS gibt.<\/p>\n\n\n\n<p>Nun m\u00fcssen wir den OTA Update Code auf dem ESP ab\u00e4ndern, als Vorlage k\u00f6nnen wir den Code aus dem signierten OTA Update Beispiel nehmen. Daher erstellen wir ein neues PlatformIO Projekt f\u00fcr das nodemcuv2 Board. In dieses Projekt kopieren wir den Code des alten Projektes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ mkdir -p ota_project_signing_secure\n$ cd ota_project_signing_secure\n$ pio init --board nodemcuv2\n$ cp ${OTA_PROJECT_SIGNING}\/src\/main.cpp .\/src\/main.cpp\n$ cat src\/main.cpp\n\/**\n   httpUpdate.ino\n\n    Created on: 27.11.2015\n\n*\/\n\n#include &lt;Arduino.h&gt;\n\n#include &lt;ESP8266WiFi.h&gt;\n#include &lt;ESP8266WiFiMulti.h&gt;\n\n#include &lt;ESP8266HTTPClient.h&gt;\n#include &lt;ESP8266httpUpdate.h&gt;\n\n#include \"secret.h\"\n\n#define MANUAL_SIGNING 1\n\nconst char pubkey&#091;] PROGMEM = R\"EOF(\n-----BEGIN PUBLIC KEY-----\nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArSf4W99aciiCoDH427w5\nFE95jL7R\/2tC4pYOyZWq3yTVl0Kq6y33L9GgLS6kCILLBi0KSGykOQX3kEzOnZa2\nnesjLZXwTxWeRlq1f0OcRMXXNjbVg+kDepKoinW3ch1fD29sLpzUgtNwIt7fAahe\neZGIsytNMnLPRUf0mjKbWe9xgyT88EETPLzsJ9Lw+CJUBxxenmzh5XbU8H\/VwUJq\nKjd2ta8jnK6htBPPMvdYpTpCqE+QY4Tp8VmKv2hnCrb8XlIyEfD5y+y5qrIF4Bg1\nvRKQD82QmNZon2ASuqPUz45ZQwVqTQSt8Pg4QI7sViO5LTmJuqAAQecHVEZ8ae3J\nBwIDAQAB\n-----END PUBLIC KEY-----\n)EOF\";\n#if MANUAL_SIGNING\nBearSSL::PublicKey *signPubKey = nullptr;\nBearSSL::HashSHA256 *hash;\nBearSSL::SigningVerifier *sign;\n#endif\n\n\nESP8266WiFiMulti WiFiMulti;\n\nvoid setup() {\n\n  Serial.begin(115200);\n  \/\/ Serial.setDebugOutput(true);\n\n  Serial.println();\n  Serial.println();\n  Serial.println();\n\n  for (uint8_t t = 4; t &gt; 0; t--) {\n    Serial.printf(\"&#091;SETUP] WAIT %d...\\n\", t);\n    Serial.flush();\n    delay(1000);\n  }\n\n  WiFi.mode(WIFI_STA);\n  WiFiMulti.addAP(APSSID, APPSK);\n\n  #if MANUAL_SIGNING\n  signPubKey = new BearSSL::PublicKey(pubkey);\n  hash = new BearSSL::HashSHA256();\n  sign = new BearSSL::SigningVerifier(signPubKey);\n  #endif\n}\n\nvoid update_started() {\n  Serial.println(\"CALLBACK:  HTTP update process started\");\n}\n\nvoid update_finished() {\n  Serial.println(\"CALLBACK:  HTTP update process finished\");\n}\n\nvoid update_progress(int cur, int total) {\n  Serial.printf(\"CALLBACK:  HTTP update process at %d of %d bytes...\\n\", cur, total);\n}\n\nvoid update_error(int err) {\n  Serial.printf(\"CALLBACK:  HTTP update fatal error code %d\\n\", err);\n}\n\n\nvoid loop() {\n  \/\/ wait for WiFi connection\n  if ((WiFiMulti.run() == WL_CONNECTED)) {\n\n    WiFiClient client;\n\n    Serial.print(\"Local WiFi\");\n    Serial.println(WiFi.localIP());\n\n    #if MANUAL_SIGNING\n    \/\/ Ensure all updates are signed appropriately.  W\/o this call, all will be accepted.\n    Update.installSignature(hash, sign);\n    #endif\n\n\n    \/\/ The line below is optional. It can be used to blink the LED on the board during flashing\n    \/\/ The LED will be on during download of one buffer of data from the network. The LED will\n    \/\/ be off during writing that buffer to flash\n    \/\/ On a good connection the LED should flash regularly. On a bad connection the LED will be\n    \/\/ on much longer than it will be off. Other pins than LED_BUILTIN may be used. The second\n    \/\/ value is used to put the LED on. If the LED is on with HIGH, that value should be passed\n    ESPhttpUpdate.setLedPin(LED_BUILTIN, LOW);\n\n    \/\/ Add optional callback notifiers\n    ESPhttpUpdate.onStart(update_started);\n    ESPhttpUpdate.onEnd(update_finished);\n    ESPhttpUpdate.onProgress(update_progress);\n    ESPhttpUpdate.onError(update_error);\n\n    t_httpUpdate_return ret = ESPhttpUpdate.update(client, \"192.168.178.123\", 8080, \"firmware.bin.signed\");\n\n    switch (ret) {\n      case HTTP_UPDATE_FAILED:\n        Serial.printf(\"HTTP_UPDATE_FAILD Error (%d): %s\\n\", ESPhttpUpdate.getLastError(), ESPhttpUpdate.getLastErrorString().c_str());\n        break;\n\n      case HTTP_UPDATE_NO_UPDATES:\n        Serial.println(\"HTTP_UPDATE_NO_UPDATES\");\n        break;\n\n      case HTTP_UPDATE_OK:\n        Serial.println(\"HTTP_UPDATE_OK\");\n        break;\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p>Wir wiederholen nochmal was wir erreichen m\u00fcssen, um solche verschl\u00fcsselten Updates auch auf Seiten des ESPs zu unterst\u00fctzen:<\/p>\n\n\n\n<ul><li>Vor dem Flashen:<ul><li>Ein LittleFS Dateisystem mit dem Zertifikat der CA erstellen<\/li><li>Das Dateisystem auf den ESP laden<\/li><\/ul><\/li><li>Zur Laufzeit:<ul><li>Die Zertifikate aus dem Dateisystem laden<\/li><li>Die lokale Zeit setzen<\/li><li>Einen WiFiClient erstellen welcher HTTPS unterst\u00fctzt<\/li><li>Eine update Anfrage stellen<\/li><\/ul><\/li><\/ul>\n\n\n\n<p>Um ein LittleFS Dateisystem zu erstellen, m\u00fcssen wir innerhalb des root Verzeichnisses des PlatformIO Projektes einen <code>data<\/code> Ordner erstellen und diesen mit den gew\u00fcnschten Inhalten des Dateisystems f\u00fcllen. Weiter m\u00fcssen wir klar darstellen, dass wir ein LittleFS Dateisystem erstellen m\u00f6chten.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ mkdir data\n$ echo \"This is a file that should be available on the ESP\" &gt; data\/hello.txt\n# Achtung doppelte &gt;&gt;, da nur ein &gt; die Datei \u00fcberschreiben w\u00fcrde\n$ echo \"board_build.filesystem = littlefs\" &gt;&gt; platform.ini\n$ cat platfrom.ini\n; PlatformIO Project Configuration File\n;\n;   Build options: build flags, source filter\n;   Upload options: custom upload port, speed and extra flags\n;   Library options: dependencies, extra library storages\n;   Advanced options: extra scripting\n;\n; Please visit documentation for the other options and examples\n; https:\/\/docs.platformio.org\/page\/projectconf.html\n\n&#091;env:nodemcuv2]\nplatform = espressif8266\nboard = nodemcuv2\nframework = arduino\nboard_build.filesystem = littlefs<\/code><\/pre>\n\n\n\n<p>Nun m\u00fcssen wir allerdings auch unser Zertifikat in das Dateisystem kopieren und nicht nur irgendeine Textdatei. Hierf\u00fcr erstellen wir aus dem Zertifikat der CA welches wir aus dem TLS-MQTT Beitrag haben und wandeln dieses in das DER Format um. Diese neue Datei legen wir dann in das <code>data<\/code> Verzeichnis.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ tree\n.\n\u251c\u2500\u2500 data\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 hello.txt\n\u251c\u2500\u2500 include\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 README\n\u251c\u2500\u2500 lib\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 README\n\u251c\u2500\u2500 platformio.ini\n\u251c\u2500\u2500 src\n\u2502&nbsp;&nbsp; \u2514\u2500\u2500 main.cpp\n\u2514\u2500\u2500 test\n    \u2514\u2500\u2500 README\n\n5 directories, 6 files\n$ cp ..\/..\/tls-mqtt\/ca.crt .\/data\/ca.crt\n$ cd data\n$ openssl x509 -outform der -in ca.crt -out our_ca_cert.der\n$ # Der Code erwartet sp\u00e4ter ein Verzeichnis vieler Zertifikate Namen 'certs.ar`\n$ ar q certs.ar our_ca_cert.der\n$ # Die Dateien, die wir nicht mehr ben\u00f6tigen, k\u00f6nnen wir l\u00f6schen.\n$ rm ca.crt our_ca_cert.der<\/code><\/pre>\n\n\n\n<p>Mit <code>pio run --target uploadfs<\/code> k\u00f6nnen wir sp\u00e4ter das Dateisystem auf den ESP flashen. Jedoch wollen wir zuvor ersteinmal die fehlende Logik dem ESP Code hinzuf\u00fcgen.<\/p>\n\n\n\n<p>Nun m\u00f6chten wir das Zertifikat vom Dateisystem laden und damit die relevanten Objekte f\u00fcllen. Hierf\u00fcr erweitern wir das Setup. Dies bedarf auch neuer Imports sowie Deklaration eines Zertifikatespeicher. Im Setup stellen wir dann sicher, dass das Dateisystem gemounted und verf\u00fcgbar ist mit <code>LittleFS.begin()<\/code>, danach laden wir die <code>certs.ar<\/code> Datei in den Zertifikatespeicher.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>...\n#if MANUAL_SIGNING\nBearSSL::PublicKey *signPubKey = nullptr;\nBearSSL::HashSHA256 *hash;\nBearSSL::SigningVerifier *sign;\n#endif\n\n#include &lt;time.h&gt;\n\n+ #include &lt;FS.h&gt;\n+ #include &lt;LittleFS.h&gt;\n+ \n+ #include &lt;CertStoreBearSSL.h&gt;\n+ BearSSL::CertStore certStore;\n\nESP8266WiFiMulti WiFiMulti;\n\nvoid setup() {\n...\n  #if MANUAL_SIGNING\n  signPubKey = new BearSSL::PublicKey(pubkey);\n  hash = new BearSSL::HashSHA256();\n  sign = new BearSSL::SigningVerifier(signPubKey);\n  #endif\n+\n  + LittleFS.begin();\n+\n  + Serial.print(\"Exists\"); \n  + Serial.println(LittleFS.exists(\"\/certs.ar\"));\n  + int numCerts = certStore.initCertStore(LittleFS, PSTR(\"\/certs.idx\"), PSTR(\"\/certs.ar\"));\n  + Serial.print(F(\"Number of CA certs read: \"));\n  + Serial.println(numCerts);\n  + if (numCerts == 0) {\n  +   Serial.println(F(\"No certs found. Did you run certs-from-mozill.py and upload the LittleFS directory before running?\"));\n  +   return; \/\/ Can't connect to anything w\/o certs!\n  + }\n}\n...<\/code><\/pre>\n\n\n\n<p>Um eine valide HTTPS GET Anfrage zu stellen, brauchen wir noch die lokale Zeit, bzw. eine ungef\u00e4hre Zeit. Hierf\u00fcr definieren wir eine neue Funktion, welche die aktuelle Zeit setzt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#if MANUAL_SIGNING\nBearSSL::PublicKey *signPubKey = nullptr;\nBearSSL::HashSHA256 *hash;\nBearSSL::SigningVerifier *sign;\n#endif\n\n+ #include &lt;time.h&gt;\n\n#include &lt;FS.h&gt;\n#include &lt;LittleFS.h&gt;\n...\n#include &lt;CertStoreBearSSL.h&gt;\nBearSSL::CertStore certStore;\n\nESP8266WiFiMulti WiFiMulti;\n\n+ void setClock() {\n+   configTime(0, 0, \"pool.ntp.org\", \"time.nist.gov\");  \/\/ UTC\n+ \n+   Serial.print(F(\"Waiting for NTP time sync: \"));\n+   time_t now = time(nullptr);\n+   while (now &lt; 8 * 3600 * 2) {\n+     yield();\n+     delay(500);\n+     Serial.print(F(\".\"));\n+     now = time(nullptr);\n+   }\n+ \n+   Serial.println(F(\"\"));\n+   struct tm timeinfo;\n+   gmtime_r(&amp;now, &amp;timeinfo);\n+   Serial.print(F(\"Current time: \"));\n+   Serial.print(asctime(&amp;timeinfo));\n+ }\n+ \n\nvoid setup() {<\/code><\/pre>\n\n\n\n<p>Damit haben wir die M\u00f6glichkeit kurz vor dem Stellen der Anfrage die Zeit zu setzen. Sehr wahrscheinlich stimmt unsere Zeitzone nicht, aber das sollte kein Problem sein, die ungef\u00e4hre Zeit sollte reichen. Das Setzen der Zeit, Registrieren der Zertifikate sowie das Stellen der Anfrage zeigen wir nun in einer letzten \u00c4nderung des Codes.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>  if ((WiFiMulti.run() == WL_CONNECTED)) {\n\n    - WiFiClient client;\n    + setClock();\n\n    + BearSSL::WiFiClientSecure client;\n    + bool mfln = client.probeMaxFragmentLength(\"server\", 443, 1024);  \/\/ server must be the same as in ESPhttpUpdate.update()\n    + Serial.printf(\"MFLN supported: %s\\n\", mfln ? \"yes\" : \"no\");\n    + if (mfln) {\n    +   client.setBufferSizes(1024, 1024);\n    + }\n    + client.setCertStore(&amp;certStore);\n\n    Serial.print(\"Local WiFi \");\n    Serial.println(WiFi.localIP());\n\n    #if MANUAL_SIGNING\n    \/\/ Ensure all updates are signed appropriately.  W\/o this call, all will be accepted.\n    Update.installSignature(hash, sign);\n    #endif\n    ...\n    \/\/ ACHTUNG\n        - t_httpUpdate_return ret = ESPhttpUpdate.update(client, \"192.168.178.123\", 8080, \"firmware.bin.signed\");\n        + t_httpUpdate_return ret = ESPhttpUpdate.update(client, \"192.168.178.123\", 8080, \"\/firmware.bin.signed\");<\/code><\/pre>\n\n\n\n<p>Somit sollte es nun m\u00f6glich sein ein signiertes Update verschl\u00fcsselt vom Update Server anzufragen und zu \u00fcbertragen. Der eigentliche Methoden Aufruf zum Starten des Updates \u00e4ndert sich leicht, denn wir haben den normalen WiFiClient mit einem WiFiClientSecure ausgetauscht. Zudem erwartet der NodeJS HTTP Server das wir <code>\/firmware.bin.signed<\/code> anfragen, weswegen wir dies explizit als Zieladresse der Updater Funktion \u00fcbergeben. Um dies nun zu testen, kompilieren wir und flashen den ESP. Wichtig hierbei wie immer, der Update Server muss von au\u00dfen erreichbar sein, d.h. in unserem Fall muss der TCP Port 8080 in der lokalen Firewall des Computers ge\u00f6ffnet sein.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ cd ${OTA_PROJECT_SIGNING_SECURE}\n$ pio run --target uploadfs\n$ pio run --target upload; pio device monitor -b 115200\nProcessing nodemcuv2 (platform: espressif8266; board: nodemcuv2; framework: arduino)\n-----------------------------------------------------------------------------------------------------------------------\nVerbose mode can be enabled via `-v, --verbose` option\n...\nBuilding file system image from 'data' directory to .pio\/build\/nodemcuv2\/littlefs.bin\n\/certs.ar\n\/hello.txt\nLooking for upload port...\nAuto-detected: \/dev\/ttyUSB0\nUploading .pio\/build\/nodemcuv2\/littlefs.bin\nesptool.py v3.0\nSerial port \/dev\/ttyUSB0\nConnecting....\n...\nWriting at 0x00300000... (100 %)\nWrote 1024000 bytes (2279 compressed) at 0x00300000 in 0.2 seconds (effective 40397.7 kbit\/s)...\nHash of data verified.\n\nLeaving...\nHard resetting via RTS pin...\n============================================= &#091;SUCCESS] Took 7.53 seconds =============================================\n\n\n$ pio run --target upload; pio device monitor -b 115200\nProcessing nodemcuv2 (platform: espressif8266; board: nodemcuv2; framework: arduino)\n-----------------------------------------------------------------------------------------------------------------------\nVerbose mode can be enabled via `-v, --verbose` option\n..\nWriting at 0x00050000... (100 %)\nWrote 448640 bytes (328103 compressed) at 0x00000000 in 28.9 seconds (effective 124.1 kbit\/s)...\nHash of data verified.\n\nLeaving...\nHard resetting via RTS pin...\n============================================ &#091;SUCCESS] Took 33.09 seconds ============================================\n--- Available filters and text transformations: colorize, debug, default, direct, esp8266_exception_decoder, hexlify, log2file, nocontrol, printable, send_on_enter, time\n--- More details at http:\/\/bit.ly\/pio-monitor-filters\n--- Miniterm on \/dev\/ttyUSB0  115200,8,N,1 ---\n--- Quit: Ctrl+C | Menu: Ctrl+T | Help: Ctrl+T followed by Ctrl+H ---\n&#091;SETUP] WAIT 3...\n&#091;SETUP] WAIT 2...\n&#091;SETUP] WAIT 1...\nExists1\nNumber of CA certs read: 1\nWaiting for NTP time sync:\nCurrent time: Tue Jun  8 16:06:57 2021\nMFLN supported: no\nLocal WiFi192.168.178.121\nCALLBACK:  HTTP update process started\nCALLBACK:  HTTP update process at 0 of 261748 bytes...\nCALLBACK:  HTTP update process at 4096 of 261748 bytes...\n...\nCALLBACK:  HTTP update process at 261748 of 261748 bytes...\nCALLBACK:  HTTP update process finished\n\n ets Jan  8 2013,rst cause:2, boot mode:(3,6)\n\nload 0x4010f000, len 3584, room 16\ntail 0\nchksum 0xb0\ncsum 0xb0\nv2843a5ac\n@cp:0\nld<\/code><\/pre>\n\n\n\n<p>Und tats\u00e4chlich das Updaten funktioniert. Um nun sicher zu gehen, dass der ESP auch wirklich nur noch von einem HTTPS Server Updates annimmt, beenden wir den HTTPS Update Server und starten wieder einen python HTTP Server.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>$ # Dr\u00fccken von CTRL+C im Terminal des Update Server sollte diesen beenden\n$ cd ${UPDATE_SERVER_TLS}\n$ python -m http.server --directory .\/ --bind 192.168.178.123 8080\n$ cd ${OTA_PROJECT_SIGNING_SECURE}\n$ pio run --target upload; pio device monitor -b 115200\n...\nHTTP_UPDATE_FAILD Error (-1): HTTP error: connection failed\n...<\/code><\/pre>\n\n\n\n<p>Das Update schl\u00e4gt fehl und der Python HTTP Server beschwert sich auch:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Serving HTTP on 192.168.178.123 port 8080 (http:\/\/192.168.178.123:8080\/) ...\n192.168.178.121 - - &#091;08\/Jun\/2021 18:15:29] code 400, message Bad request version ('\u00c0\\x13\u00c0')\n192.168.178.121 - - &#091;08\/Jun\/2021 18:15:29] \"\u00d7\u00d3\u00d0c\u00a4xBv\u00ba\u00ac\u00e2x1g \u00c6!@\u00b6Z\u00cc\u00a9\u00cc\u00a8\u00c0+\u00c0\/\u00c0,\u00c00\u00c0\u00ac\u00c0\u00ad\u00c0\u00ae\u00c0\u00af\u00c0#\u00c0'\u00c0$\u00c0(\u00c0    \u00c0\u00c0\" 400 -<\/code><\/pre>\n\n\n\n<p>Somit haben wir nun signierte Updates \u00fcber eine sichere verschl\u00fcsselte Verbindung. Im n\u00e4chsten Blogbeitrag m\u00f6chten wir nun auch den Clients eigene Zertifikat und Keys geben, um sichergehen zu k\u00f6nnen, dass nur ESPs welche von uns zertifizierte Zertifikate haben auch die Update Datei erhalten. Wie immer gibt es den erstellten Code und alle anderen Dateien <a href=\"https:\/\/github.com\/buermarc\/iot-blog-posts\/tree\/main\/secure-signing-ota\">hier<\/a> auf Github.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In diesem Blogbeitrag besch\u00e4ftigen wir uns mit dem verschl\u00fcsselten \u00dcbertragen von Update Dateien. Dies ist w\u00fcnschenswert, da in den momentanen Dateien Informationen wie Anmeldedaten f\u00fcr das lokale Netzwerk, als auch Adressen von weiterem Server beinhaltet ist (Update Server, sp\u00e4ter auch<\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":[],"categories":[6],"tags":[],"_links":{"self":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/619"}],"collection":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/comments?post=619"}],"version-history":[{"count":1,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/619\/revisions"}],"predecessor-version":[{"id":620,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/posts\/619\/revisions\/620"}],"wp:attachment":[{"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/media?parent=619"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/categories?post=619"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.iot-embedded.de\/iot-2021\/wp-json\/wp\/v2\/tags?post=619"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}