Debugging CoAP mit IKEA Tradfri Gateway

Einleitung

Ein Hinweis vorab: diese Anleitung ist eher für technisch Interessierte, die mehr über die CoAP Kommunikation zwischen IKEA Tradfri Gateway herausfinden möchten, aber nicht notwendig, um IKEA Smart Home Geräte in Loxone einzubinden.

Das Constrained Application Protocol (CoAP) wurde für das Internet of Things (IoT) entwickelt und 2016 der Internet Engineering Task Force (IETF) übergeben. Es ist speziell auf die knappen Speicher- und CPU-Ressourcen von IoT Geräten entwickelt und übernimmt die Grundzüge von REST, aber auch HTTP. Hier ist ein Link zur Protokolldefinition: Constrained Application Protocol (CoAP). Interessant ist auch der folgende Link, der Hinweise zur Integration in Programmiersprachen und Tools enthält https://coap.technology/.

Das Protokoll läuft über UDP und ist daher verbindungslos. Das hat für IoT Geräte den Vorteil, dass eine aufwändige TCP Kommunikation mit Quittungen und Puffern entfallen kann. Das IKEA Gateway verwendet nur die verschlüsselte Variante COAPS, die DTLS als Transport verwendet.

Die verschlüsselte Kommunikation wird dabei nicht über Zertifikate ausgehandelt, sondern initial über einen Security Code, der auf der Unterseite des Gateway aufgedruckt ist. Mit diesen Code kann ein CoAP Client z.B. die IKEA Smart Home App bei der Einrichtung ein Password für einen gewünschten User anfordern (bei der App nicht sichtbar), der für die nachfolgende Kommunikation beibehalten wird.

Erste Schritte

Mit der verlinkten sehr gut beschriebenen Anleitung https://github.com/glenndehaan/ikea-tradfri-coap-docs und dem Tool coap-client kann man sehr einfach die ersten Schritte selbst durchführen und die eigenen Geräte abfragen.

TIP: Wenn man parallel mit Node-RED und der Bibliothek node-red-contrib-ikea-tradfri arbeitet, dann sollte man den Benutzernamen und Pre-Shared Key von Node-RED übernehmen. Ich hatte zumindest Probleme, wenn mehrere Clients mit unterschiedlichen Key gleichzeitig auf das Gateway zugegriffen haben.

Mit einem Pipe auf die Tools sed und dann auf jq lassen sich die JSON-Daten besser lesbar ausgeben. Nachfolgend ist dies für einen meiner IKEA GU10 Spots exemplarisch dargestellt:

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 coap-client -m get -u my_tradfri_username -k "my_tradfri_key" "coaps://192.168.1.73:5684/15001/65550" | sed "1,3d" | jq { "9001": "Spot A1", // user friendly name "9003": 65550, // instanceID "3": { // object with vendor information "0": "IKEA of Sweden", // manufacturer name "1": "TRADFRIbulbGU10WS345lm", // model name "2": "", "6": 1, // power source (1=internal ?) "3": "1.0.012", // firmware version of light bulb "7": 8709, // OTA image type "8": 5 // unknown }, "9002": 1641675281, // creation timestamp "9054": 0, "9020": 1642111763, // last seen timestamp "9019": 1, // reachability state "5750": 2, // device type (2=light bulb) "3311": [ // light bulb object with data { "5850": 1, // onOff - type boolean "5849": 2, // action after power restored (1=remember on/off setting 2=always on/default), settable via IKEA Smart Home app "5851": 188, // brightness/dimmer - number from 0=dark to 254=bright "5717": 0, "9003": 0, "5711": 345, // color temperature in mireds - number from 250=warm to 454=cold "5709": 29090, // colorX "5710": 26728, // colorY "5706": "feb465" // color rgb type rgb hex string } ] }

Als Kommentar habe ich die o.a. Eigenschaften, die bei CoAP alle nummeriert sind, ergänzt. Diese sind z.T. aus der oben verlinkten Anleitung und zum anderen Teil von https://github.com/AlCalzone/node-tradfri-client/blob/master/src/lib/light.ts, einer low-level Bibliothek von AlCalzone, die in node-red-contrib-tradfri eingebunden ist. Andere Geräte als light bulbs sind dort ebenfalls enthalten. IKEA hat die Details zwar nicht offiziell veröffentlicht, aber mittlerweile sind fast alle Parameter bekannt. Diese sind wohl an die Definition der Open Mobile Alliance (OMA) angelehnt, siehe https://devtoolkit.openmobilealliance.org/OEditor/default.aspx

Neben einer Liste der Endgeräte kann man sich über unterschiedliche URIs eine Liste der Gruppen, Szenen/Stimmungen, Benachrichtigungen und Smart-Tasks ausgeben lassen. Über die jeweiligen instanceIDs kann man dann Details zu einem einzelnen Element einer Liste erhalten. Näheres steht alles in der verlinkten Anleitung, so dass ich mir hier Details spare.

Sniffing COAPS Protokoll

Richtig spannend wird es, wenn man die Kommunikation zwischen dem Client z.B. Node-RED und dem IKEA Tradfri Gateway per Wireshark mitsniffen möchte. Man kann so einen Mitschnitt z.B. über eine Portspiegelung auf dem eigenen Netzwerkswitch oder einer Fritzbox und dem Aufruf über die URL /html/capture.html erhalten. Zuerst sieht man nur verschlüsselte Pakete, denn das IKEA Tradfri Gateway spricht nur COAPS. Es gibt aber die Möglichkeit den Pre-Shared-Key in Wireshark zu hinterlegen, um den Datenverkehr im Klartext mitlesen zu können.

Dafür muss zunächst der Pre-Shared-Key, den z.B. Node-RED verwendet aus dem config-node ausgelesen werden. Über einen tradfri-monitor Node kann man den eingebetteten tradfri-config Node aufrufen (Edit Symbol anklicken) und den Pre-Shared-Key sehr einfach auslesen.

Read key from tradfri-config node

Der Key muss noch von ASCII auf HEX umgerechnet werden, was über diverse Tools erfolgen kann. Anschließend wird der Key im Hex-Format in Wireshark im Menü “Einstellungen”, “Protokolle”, “DTLS” eingegeben:

Nachdem man einen Paketmitschnitt gestartet hat, löst man in Node-RED ein Restart der Flows aus:

Node-RED - Restart Flows

Ab diesem Zeitpunkt wird die CoAP Kommunikation im Klartext angezeigt.

HINWEIS: nach jedem neuen Capture muss man die Flows neu starten, da über den Pre-Shared-Key und Identity-Namen ein dynamischer Schlüssel für diese “Session” erzeugt wird, den Wireshark sich leider nicht merkt, wenn man ein neues Capture startet. Damit kann man aber sicherlich leben.

Nachfolgend ist als Beispiel der PUT Befehl dargestellt, mit dem Node-RED die Werte einer Lampe mit der instanceID 65550 (Spot A1) setzt:

Wireshark - decrypted CoAP communication

Die Parameter und Werte werden im JSON Format übergeben. Im nachfolgenden Beispiel wird die Helligkeit auf 25% und die Lichtfarbe auf “warm” gesetzt:

CoAP decrypted in detail

Die Meldung wird vom IKEA Tradfri Gateway quittiert.

Ich finde es sehr interessant die CoAP Kommunikation mitzulesen, um z.B. zu prüfen, dass zwischen Node-RED und dem Tradfri Gateway Events gesendet werden und die Werte nicht abgerufen werden. Warum Node-RED laufend leere CON Meldungen sendet ist mir nicht klar. Diese werden jedoch alle mit einem RST (Reset) vom Gateway quittiert.

Wenn man eine komplette “Session” näher untersucht, dann kann sehen, dass Node-RED anfangs die Geräteliste und alle Geräte von Dieser abfragt, um die Geräte über den anwenderfreundlichen Namen auswählen zu können - zumindest bei den “control” Nodes. Außerdem werden alle instanceIDs mit einer Observe: Register Option bei abfragen “abonniert” und deshalb bei Änderungen von Werten für diese Instance ID’s einen Event bekommt. Hier wird eine Publisher - Subscriber Kommunikation verwendet, die ein ständiges Polling vermeidet.

CoAP Observe to subscribe to changes

Wenn man die Lampen z.B. über die IKEA Fernbedienung ändert, dann wird ein Event an Node-RED gesendet, wie man in dem nachfolgenden Beispiel gut erkennen kann:

Event vom Tradfri Gateway send to Node-RED

Sniffing IKEA Smart Home app

Da die IKEA Smart Home app die primäre Referenz für die Kommunikation zum Tradfri Gateway ist, wäre es natürlich super, wenn man diese Kommunikation ebenfalls mitlesen könnte.

Meine ersten Ideen dazu: Wenn man den Pre-Shared-Key aus der IKEA Smart Home App auslesen kann oder initial die Koppelung über den Security Code mitschneidet, dann könnte man vielleicht auch die Kommunikation zwischen IKEA Smart Home App und Tradfri Gateway mitlesen?

Die erste Variante habe ich nicht näher betrachtet, sondern gleich die 2. Variante ausprobiert. Ich habe also die IKEA Smart Home app von meinem Handy gelöscht und neu installiert. Dadurch werden lokal gespeicherte Informationen, wie z.B. pre-shared Keys gelöscht. Dann habe ich mich über die App wieder mit dem Gateway verbunden und wurde erwartungsgemäß zum Scannen des QR-Codes oder alternativ zur manuellen Eingabe des Security Codes aufgefordert. Nachdem diese Informationen eingegeben waren, konnte ich in der App wieder meine installierten Lampen sehen.

Anschließend habe ich das Packet Capture mit Wireshark untersucht. Anfangs sah ich nur eine DTLS Kommunikation und Hello Pakete von beiden Seiten. Bei näherer Untersuchung stellte ich fest, dass eine verschlüsselte Kommunikation ausgehandelt wird, ähnlich wie das auch bei TLS geschieht, allerdings mit weniger Ciphers. Die Authentifizierungsphase, bei der der Client vom Server einen pre-shared Key anfordert, ist ja bereits hier in der erwähnten Anleitung beschrieben worden. Diese lief bereits verschlüsselt ab, so dass man nichts im Klartext lesen konnte.

Ich habe nicht mal im RFC zu COAPS suchen müssen, sondern habe gesehen, dass der Security Code ‘zufälligerweise’ die gleiche Länge hat, wie die pre-shared Keys und vermutet, dass dieser Key für die initiale Verschlüsselung verwendet wird. Daher habe ich das Packet Capture gespeichert, den Security Code von ASCII in Hex umgewandelt und - wie oben beschrieben - in Wireshark eingetragen. Bingo! Jetzt konnte ich die Authentifizierung im Klartext mitlesen:

Am Anfang sieht man mehrere “Client Hello” und “Server Hello” Pakete und die Aushandlung der symmetrischen Verschlüsselung. Danach folgt die Authentifizierung, mit der der CoAP-Client ein Pre-Shared Key für einen gewünschten Usernamen anfordert. Hierfür sendet der Client die Property “9090” und den gewünschten (dynamisch gewählten) Username im JSON-Format an den CoAP-Server.

Nachfolgend ist die Antwort vom CoAP-Server näher erläutert:

Die Antwort im JSON-Format enthält die Property “9091” und den vom Server zufällig gewählten Pre-shared Key. Diesen Key kann man sehr einfach über die angegebenen Menüpunkte “Copy”, “…as Printable Text” in die Zwischenablage kopieren und speichern. Nachdem man diesen Pre-shared Key wieder von ASCII in Hex konvertiert hat und in Wireshark eingegeben hat, kann man endlich die Kommunikation zwischen App und Gateway mitlesen.

Sicherheit von COAPS

Auch wenn dieser Guide zeigt, wie man die verschlüsselte Kommunikation mitlesen kann, um z.B. die Kommunikation für neue Geräte zu verstehen, ist das Protokoll CoAPS nach meiner Ansicht sehr sicher. Nur wenn man die initiale Koppelung zwischen der App und dem Gateway, die über den Security Code erfolgt, mitlesen kann, ist eine Entschlüsselung möglich. Da die Kommunikation bei IKEA nur im lokalen LAN und (hoffentlich verschlüsselten) WLAN zu Hause stattfindet, ist die Sicherheit in der Praxis nicht gefährdet oder beeinträchtigt.

 

Viel Spaß beim Entschlüsseln der CoAP Kommunikation!