First successful HMAC verification

This commit is contained in:
Christian Schwinne 2024-10-21 00:06:01 +02:00
parent be997aa755
commit f3429a6c93
5 changed files with 62 additions and 41 deletions

View File

@ -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;
} }

View File

@ -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
} }

View File

@ -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

View File

@ -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;

View File

@ -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();