From b02bc29d29b4b916ae5f42cf9debfa040a3430c2 Mon Sep 17 00:00:00 2001
From: Christian Schwinne
Date: Sun, 13 Oct 2024 22:48:20 +0200
Subject: [PATCH] UI communication progress
---
readme.md | 3 +++
wled00/crypto.cpp | 14 ++++++++++++++
wled00/data/index.js | 37 +++++++++++++++++++++++++++++++++++++
wled00/wled_server.cpp | 30 +++++++++++++++++++++++++++++-
4 files changed, 83 insertions(+), 1 deletion(-)
diff --git a/readme.md b/readme.md
index 11c1733f87..a406bfd3ce 100644
--- a/readme.md
+++ b/readme.md
@@ -10,6 +10,9 @@
+> [!CAUTION]
+> This branch is actively used for research purposes. **Please do not push** to it.
+
# Welcome to my project WLED! ✨
A fast and feature-rich implementation of an ESP8266/ESP32 webserver to control NeoPixel (WS2812B, WS2811, SK6812) LEDs or also SPI based chipsets like the WS2801 and APA102!
diff --git a/wled00/crypto.cpp b/wled00/crypto.cpp
index 818c0f2d24..142f1561e3 100644
--- a/wled00/crypto.cpp
+++ b/wled00/crypto.cpp
@@ -31,6 +31,20 @@ bool hmac_verify(const char* message, const char* psk, const byte* signature) {
return true;
}
+bool verify_json_hmac(JsonObject root) {
+ JsonObject msg = root["msg"];
+ if (!msg) {
+ Serial.println(F("No message object found in JSON."));
+ return false;
+ }
+ const char *sig = msg["sig"];
+ if (sig == nullptr) {
+ Serial.println(F("No signature found in JSON."));
+ return false;
+ }
+
+}
+
bool hmac_test() {
Serial.println(F("Testing HMAC..."));
unsigned long start = millis();
diff --git a/wled00/data/index.js b/wled00/data/index.js
index 25ade11639..35b4cd502a 100644
--- a/wled00/data/index.js
+++ b/wled00/data/index.js
@@ -214,6 +214,10 @@ function loadSkinCSS(cId)
}
}
+var useSRA = false;
+var sraWindow = null;
+var sraOrigin = '';
+
function getURL(path) {
return (loc ? locproto + "//" + locip : "") + path;
}
@@ -243,6 +247,13 @@ function onLoad()
var sett = localStorage.getItem('wledUiCfg');
if (sett) cfg = mergeDeep(cfg, JSON.parse(sett));
+ if (window.opener) {
+ // can't get opener origin due to cross-origin browser policy
+ //var openerOrigin = window.opener.location.origin;
+ //console.log("WLED-UI opener origin: " + openerOrigin);
+ window.opener.postMessage('{"wled-ui":"onload"}', '*'); //openerOrigin);
+ }
+
tooltip();
resetPUtil();
initFilters();
@@ -301,6 +312,26 @@ function onLoad()
});
}
+function handleWindowMessageEvent(event) {
+ console.log(`Received message: ${event.data}`);
+ console.log(`origin: ${event.origin}`);
+ try {
+ var json = JSON.parse(event.data)
+ } catch (e) {
+ console.log(`Error parsing JSON: ${e}`);
+ return;
+ }
+ if (json['wled-rc'] === 'ready') {
+ useSRA = true;
+ sraWindow = event.source;
+ sraOrigin = event.origin;
+ } else if (json['wled-rc'] === 'hmac') {
+ console.log(`Received HMAC: ${json['hmac']}`);
+ }
+}
+
+onmessage = (event) => { handleWindowMessageEvent(event) };
+
function updateTablinks(tabI)
{
var tablinks = gEBCN("tablinks");
@@ -1703,6 +1734,12 @@ function requestJson(command=null)
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes
};
+ if (command && useSRA && !command['sig']) { // secure remote access integration, need to get HMAC from rc.wled.me
+ // if we already have a command including a signature, we are good to go
+ sraWindow.postMessage(JSON.stringify({"wled-ui":"hmac-req", "msg":command}), sraOrigin);
+ return; // TODO need a sort of pending indicator
+ }
+
if (useWs) {
ws.send(req?req:'{"v":true}');
return;
diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp
index 4a616f9aa5..cace011866 100644
--- a/wled00/wled_server.cpp
+++ b/wled00/wled_server.cpp
@@ -297,9 +297,26 @@ void initServer()
DeserializationError error = deserializeJson(*pDoc, (uint8_t*)(request->_tempObject));
// if enabled, calculate HMAC and verify it
- Serial.println("HMAC verification");
+ Serial.println(F("HMAC verification"));
Serial.write((const char*)request->_tempObject, request->contentLength());
+ // actually we need to verify the HMAC of the nested "msg" object
+ if (strlen((const char*)request->_tempObject) > request->contentLength()) {
+ Serial.println(F("HMAC verification failed: content is not null-terminated"));
+ releaseJSONBufferLock();
+ serveJsonError(request, 400, ERR_JSON);
+ return;
+ }
+ // find the "msg" object in JSON
+ char * msgPtr = strstr((const char*)request->_tempObject, "\"msg\":");
+ if (msgPtr == NULL) {
+ Serial.println(F("HMAC verification failed: no \"msg\" object found"));
+ releaseJSONBufferLock();
+ serveJsonError(request, 400, ERR_JSON);
+ return;
+ }
+ char * objStart = strchr(msgPtr, '{');
+
JsonObject root = pDoc->as();
if (error || root.isNull()) {
releaseJSONBufferLock();
@@ -307,6 +324,17 @@ void initServer()
return;
}
+ // if (root.containsKey("sig")) {
+ // const char* hmacProvided = root["sig"];
+ // char hmac_calculated[SHA256HMAC_SIZE];
+ // hmac_sign((const char*)request->_tempObject, settings.hmacKey, (byte*)hmac_calculated);
+ // if (memcmp(hmac_calculated, hmac, SHA256HMAC_SIZE) != 0) {
+ // releaseJSONBufferLock();
+ // serveJsonError(request, 401, ERR_HMAC);
+ // return;
+ // }
+ // }
+
// old 4-digit pin logic for settings authentication (no transport encryption)
if (root.containsKey("pin")) checkSettingsPIN(root["pin"].as());