mirror of
https://github.com/wled/WLED.git
synced 2025-04-27 16:27:18 +00:00
First successful HMAC verification
This commit is contained in:
parent
be997aa755
commit
f3429a6c93
@ -34,12 +34,12 @@ void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* sign
|
|||||||
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature) {
|
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature) {
|
||||||
byte sigCalculated[SHA256HMAC_SIZE];
|
byte sigCalculated[SHA256HMAC_SIZE];
|
||||||
hmacSign(message, msgLen, pskHex, sigCalculated);
|
hmacSign(message, msgLen, pskHex, sigCalculated);
|
||||||
|
Serial.print(F("Calculated: "));
|
||||||
|
printByteArray(sigCalculated, SHA256HMAC_SIZE);
|
||||||
if (memcmp(sigCalculated, signature, SHA256HMAC_SIZE) != 0) {
|
if (memcmp(sigCalculated, signature, SHA256HMAC_SIZE) != 0) {
|
||||||
DEBUG_PRINTLN(F("HMAC verification failed!"));
|
Serial.println(F("HMAC verification failed!"));
|
||||||
Serial.print(F("Expected: "));
|
Serial.print(F("Expected: "));
|
||||||
printByteArray(signature, SHA256HMAC_SIZE);
|
printByteArray(signature, SHA256HMAC_SIZE);
|
||||||
Serial.print(F("Calculated: "));
|
|
||||||
printByteArray(sigCalculated, SHA256HMAC_SIZE);
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Serial.println(F("HMAC verification successful!"));
|
Serial.println(F("HMAC verification successful!"));
|
||||||
@ -49,71 +49,89 @@ bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const by
|
|||||||
#define WLED_HMAC_TEST_PW "guessihadthekeyafterall"
|
#define WLED_HMAC_TEST_PW "guessihadthekeyafterall"
|
||||||
#define WLED_HMAC_TEST_PSK "a6f8488da62c5888d7f640276676e78da8639faf0495110b43e226b35ac37a4c"
|
#define WLED_HMAC_TEST_PSK "a6f8488da62c5888d7f640276676e78da8639faf0495110b43e226b35ac37a4c"
|
||||||
|
|
||||||
|
bool verifyHmacFromJsonString0Term(byte* jsonStr, size_t len) {
|
||||||
|
// Zero-terminate the JSON string (replace the last character, usually '}', with a null terminator temporarily)
|
||||||
|
char lastChar = jsonStr[len-1];
|
||||||
|
jsonStr[len-1] = '\0';
|
||||||
|
bool result = verifyHmacFromJsonStr((const char*)jsonStr, len);
|
||||||
|
jsonStr[len-1] = lastChar;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
bool verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen) {
|
bool verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen) {
|
||||||
// Extract the signature from the JSON string
|
// Extract the signature from the JSON string
|
||||||
size_t jsonLen = strlen(jsonStr);
|
size_t jsonLen = strlen(jsonStr);
|
||||||
|
Serial.print(F("Length: "));
|
||||||
|
Serial.println(jsonLen);
|
||||||
if (jsonLen > maxLen) { // memory safety
|
if (jsonLen > maxLen) { // memory safety
|
||||||
Serial.println(F("JSON string too long!"));
|
Serial.print(F("JSON string too long!"));
|
||||||
Serial.print(F("Length: "));
|
|
||||||
Serial.print(jsonLen);
|
|
||||||
Serial.print(F(", max: "));
|
Serial.print(F(", max: "));
|
||||||
Serial.println(maxLen);
|
Serial.println(maxLen);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Serial.print(F("Received JSON: "));
|
Serial.print(F("Received JSON: "));
|
||||||
Serial.println(jsonStr);
|
Serial.println(jsonStr);
|
||||||
char* sigPos = strstr(jsonStr, PSTR("\"sig\":\""));
|
|
||||||
if (sigPos == nullptr) {
|
char* macPos = strstr(jsonStr, "\"mac\":\"");
|
||||||
Serial.println(F("No signature found in JSON."));
|
if (macPos == nullptr) {
|
||||||
|
Serial.println(F("No MAC found in JSON."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
StaticJsonDocument<256> doc;
|
StaticJsonDocument<256> doc;
|
||||||
DeserializationError error = deserializeJson(doc, jsonStr +7);
|
DeserializationError error = deserializeJson(doc, macPos +6);
|
||||||
if (error) {
|
if (error) {
|
||||||
Serial.print(F("deserializeJson() failed: "));
|
Serial.print(F("deserializeJson() failed: "));
|
||||||
Serial.println(error.c_str());
|
Serial.println(error.c_str());
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
const char* sig = doc.as<const char*>();
|
const char* mac = doc.as<const char*>();
|
||||||
if (sig == nullptr) {
|
if (mac == nullptr) {
|
||||||
Serial.println(F("Failed signature JSON."));
|
Serial.println(F("Failed MAC JSON."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
Serial.print(F("Received signature: "));
|
Serial.print(F("Received MAC: "));
|
||||||
Serial.println(sig);
|
Serial.println(mac);
|
||||||
|
|
||||||
// extract the message object from the JSON string
|
// extract the message object from the JSON string
|
||||||
char* msgPos = strstr(jsonStr, PSTR("\"msg\":\""));
|
char* msgPos = strstr(jsonStr, "\"msg\":");
|
||||||
char* objStart = strchr(msgPos + 7, '{');
|
char* objStart = strchr(msgPos + 6, '{');
|
||||||
|
if (objStart == nullptr) {
|
||||||
|
Serial.println(F("Couldn't find msg object start."));
|
||||||
|
return false;
|
||||||
|
}
|
||||||
size_t maxObjLen = jsonLen - (objStart - jsonStr);
|
size_t maxObjLen = jsonLen - (objStart - jsonStr);
|
||||||
uint32_t objDepth = 0;
|
Serial.print(F("Max object length: ")); Serial.println(maxObjLen);
|
||||||
|
int32_t objDepth = 0;
|
||||||
char* objEnd = nullptr;
|
char* objEnd = nullptr;
|
||||||
|
|
||||||
for (size_t i = 0; i < maxObjLen; i++) {
|
for (size_t i = 0; i < maxObjLen; i++) {
|
||||||
|
Serial.write(objStart[i]);
|
||||||
if (objStart[i] == '{') objDepth++;
|
if (objStart[i] == '{') objDepth++;
|
||||||
if (objStart [i] == '}') objDepth--;
|
if (objStart[i] == '}') objDepth--;
|
||||||
if (objDepth == 0) {
|
if (objDepth == 0) {
|
||||||
|
Serial.print(F("Found msg object end: "));
|
||||||
|
Serial.println(i);
|
||||||
objEnd = objStart + i;
|
objEnd = objStart + i;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
i++;
|
//i++;
|
||||||
}
|
}
|
||||||
if (objEnd == nullptr) {
|
if (objEnd == nullptr) {
|
||||||
Serial.println(F("Couldn't find msg object end."));
|
Serial.println(F("Couldn't find msg object end."));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convert the signature from hex string to byte array
|
// Convert the MAC from hex string to byte array
|
||||||
size_t len = strlen(sig) / 2; // This will drop the last character if the string has an odd length
|
size_t len = strlen(mac) / 2; // This will drop the last character if the string has an odd length
|
||||||
if (len != SHA256HMAC_SIZE) {
|
if (len != SHA256HMAC_SIZE) {
|
||||||
Serial.println(F("Received sig not expected size!"));
|
Serial.println(F("Received MAC not expected size!"));
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
unsigned char sigByteArray[len];
|
unsigned char macByteArray[len];
|
||||||
hexStringToByteArray(sig, sigByteArray, len);
|
hexStringToByteArray(mac, macByteArray, len);
|
||||||
|
|
||||||
// Calculate the HMAC of the message object
|
// Calculate the HMAC of the message object
|
||||||
return hmacVerify((const byte*)objStart, objEnd - objStart + 1, WLED_HMAC_TEST_PSK, sigByteArray);
|
return hmacVerify((const byte*)objStart, objEnd - objStart + 1, WLED_HMAC_TEST_PSK, macByteArray);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool hmacTest() {
|
bool hmacTest() {
|
||||||
@ -121,17 +139,17 @@ bool hmacTest() {
|
|||||||
unsigned long start = millis();
|
unsigned long start = millis();
|
||||||
const char message[] = "Hello, World!";
|
const char message[] = "Hello, World!";
|
||||||
const char psk[] = "d0c0ffeedeadbeef";
|
const char psk[] = "d0c0ffeedeadbeef";
|
||||||
byte signature[SHA256HMAC_SIZE];
|
byte mac[SHA256HMAC_SIZE];
|
||||||
hmacSign((const byte*)message, strlen(message), psk, signature);
|
hmacSign((const byte*)message, strlen(message), psk, mac);
|
||||||
Serial.print(F("Took "));
|
Serial.print(F("Took "));
|
||||||
Serial.print(millis() - start);
|
Serial.print(millis() - start);
|
||||||
Serial.println(F("ms to sign message."));
|
Serial.println(F("ms to sign message."));
|
||||||
Serial.print(F("Signature: "));
|
Serial.print(F("MAC: "));
|
||||||
printByteArray(signature, SHA256HMAC_SIZE);
|
printByteArray(mac, SHA256HMAC_SIZE);
|
||||||
start = millis();
|
start = millis();
|
||||||
bool result = hmacVerify((const byte*)message, strlen(message), psk, signature);
|
bool result = hmacVerify((const byte*)message, strlen(message), psk, mac);
|
||||||
Serial.print(F("Took "));
|
Serial.print(F("Took "));
|
||||||
Serial.print(millis() - start);
|
Serial.print(millis() - start);
|
||||||
Serial.println(F("ms to verify signature."));
|
Serial.println(F("ms to verify MAC."));
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
@ -326,7 +326,7 @@ function handleWindowMessageEvent(event) {
|
|||||||
sraWindow = event.source;
|
sraWindow = event.source;
|
||||||
sraOrigin = event.origin;
|
sraOrigin = event.origin;
|
||||||
} else if (json['wled-rc'] === 'hmac') {
|
} else if (json['wled-rc'] === 'hmac') {
|
||||||
console.log(`Received HMAC: ${json['sig']}`);
|
console.log(`Received HMAC: ${json['mac']}`);
|
||||||
requestJson(json);
|
requestJson(json);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1743,8 +1743,8 @@ function requestJson(command=null)
|
|||||||
if (req.length > 500 && lastinfo && lastinfo.arch == "esp8266") useWs = false; // esp8266 can only handle 500 bytes
|
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 (command && useSRA && !command['mac']) { // 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
|
// if we already have a command including a MAC, we are good to go
|
||||||
sraWindow.postMessage(JSON.stringify({"wled-ui":"hmac-req", "msg":command}), sraOrigin);
|
sraWindow.postMessage(JSON.stringify({"wled-ui":"hmac-req", "msg":command}), sraOrigin);
|
||||||
return; // TODO need a sort of pending indicator
|
return; // TODO need a sort of pending indicator
|
||||||
}
|
}
|
||||||
|
@ -99,6 +99,7 @@ void setRandomColor(byte* rgb);
|
|||||||
void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* signature);
|
void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* signature);
|
||||||
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature);
|
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature);
|
||||||
bool verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen);
|
bool verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen);
|
||||||
|
bool verifyHmacFromJsonString0Term(byte* jsonStr, size_t len);
|
||||||
bool hmacTest();
|
bool hmacTest();
|
||||||
|
|
||||||
//dmx.cpp
|
//dmx.cpp
|
||||||
|
@ -289,7 +289,7 @@ void initServer()
|
|||||||
|
|
||||||
Serial.println("JSON request");
|
Serial.println("JSON request");
|
||||||
Serial.println((const char*)request->_tempObject);
|
Serial.println((const char*)request->_tempObject);
|
||||||
if (!verifyHmacFromJsonStr((const char*)request->_tempObject, request->contentLength())) {
|
if (!verifyHmacFromJsonString0Term((byte*)request->_tempObject, request->contentLength())) {
|
||||||
//releaseJSONBufferLock();
|
//releaseJSONBufferLock();
|
||||||
serveJsonError(request, 401, ERR_DENIED);
|
serveJsonError(request, 401, ERR_DENIED);
|
||||||
return;
|
return;
|
||||||
|
@ -43,8 +43,10 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
|||||||
|
|
||||||
Serial.print(F("WS message: "));
|
Serial.print(F("WS message: "));
|
||||||
Serial.println((const char*)data);
|
Serial.println((const char*)data);
|
||||||
|
verifyHmacFromJsonString0Term(data, len);
|
||||||
|
|
||||||
DeserializationError error = deserializeJson(*pDoc, data, len);
|
DeserializationError error = deserializeJson(*pDoc, data, len);
|
||||||
|
verifyHmacFromJsonString0Term(data, len);
|
||||||
JsonObject root = pDoc->as<JsonObject>();
|
JsonObject root = pDoc->as<JsonObject>();
|
||||||
if (error || root.isNull()) {
|
if (error || root.isNull()) {
|
||||||
releaseJSONBufferLock();
|
releaseJSONBufferLock();
|
||||||
@ -56,11 +58,11 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
|||||||
} else if (root.containsKey("lv")) {
|
} else if (root.containsKey("lv")) {
|
||||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||||
} else {
|
} else {
|
||||||
if (!verifyHmacFromJsonStr((const char*)data, len)) {
|
// if (!verifyHmacFromJsonString0Term(data, len)) {
|
||||||
releaseJSONBufferLock();
|
// releaseJSONBufferLock();
|
||||||
client->text(F("{\"error\":1}")); // ERR_DENIED
|
// client->text(F("{\"error\":1}")); // ERR_DENIED
|
||||||
return;
|
// return;
|
||||||
}
|
// }
|
||||||
verboseResponse = deserializeState(root["msg"]);
|
verboseResponse = deserializeState(root["msg"]);
|
||||||
}
|
}
|
||||||
releaseJSONBufferLock();
|
releaseJSONBufferLock();
|
||||||
|
Loading…
x
Reference in New Issue
Block a user