mirror of
https://github.com/wled/WLED.git
synced 2025-04-23 14:27:18 +00:00
More HMAC functionality
This commit is contained in:
parent
b02bc29d29
commit
be997aa755
@ -3,62 +3,133 @@
|
||||
|
||||
#define HMAC_KEY_SIZE 32
|
||||
|
||||
void print_byte_array(const byte* arr, size_t len) {
|
||||
void printByteArray(const byte* arr, size_t len) {
|
||||
for (size_t i = 0; i < len; i++) {
|
||||
Serial.print(arr[i], HEX);
|
||||
}
|
||||
Serial.println();
|
||||
}
|
||||
|
||||
void hmac_sign(const char* message, const char* psk, byte* signature) {
|
||||
SHA256HMAC hmac((const byte*)psk, strlen(psk));
|
||||
hmac.doUpdate(message, strlen(message));
|
||||
void hexStringToByteArray(const char* hexString, unsigned char* byteArray, size_t byteArraySize) {
|
||||
for (size_t i = 0; i < byteArraySize; i++) {
|
||||
char c[3] = {hexString[2 * i], hexString[2 * i + 1], '\0'}; // Get two characters
|
||||
byteArray[i] = (unsigned char)strtoul(c, NULL, 16); // Convert to byte
|
||||
}
|
||||
}
|
||||
|
||||
void hmacSign(const byte* message, size_t msgLen, const char* pskHex, byte* signature) {
|
||||
size_t len = strlen(pskHex) / 2; // This will drop the last character if the string has an odd length
|
||||
if (len > HMAC_KEY_SIZE) {
|
||||
Serial.println(F("PSK too long!"));
|
||||
return;
|
||||
}
|
||||
unsigned char pskByteArray[len];
|
||||
hexStringToByteArray(pskHex, pskByteArray, len);
|
||||
|
||||
SHA256HMAC hmac(pskByteArray, len);
|
||||
hmac.doUpdate(message, msgLen);
|
||||
hmac.doFinal(signature);
|
||||
}
|
||||
|
||||
bool hmac_verify(const char* message, const char* psk, const byte* signature) {
|
||||
byte sig_calculated[SHA256HMAC_SIZE];
|
||||
hmac_sign(message, psk, sig_calculated);
|
||||
if (memcmp(sig_calculated, signature, SHA256HMAC_SIZE) != 0) {
|
||||
bool hmacVerify(const byte* message, size_t msgLen, const char* pskHex, const byte* signature) {
|
||||
byte sigCalculated[SHA256HMAC_SIZE];
|
||||
hmacSign(message, msgLen, pskHex, sigCalculated);
|
||||
if (memcmp(sigCalculated, signature, SHA256HMAC_SIZE) != 0) {
|
||||
DEBUG_PRINTLN(F("HMAC verification failed!"));
|
||||
Serial.print(F("Expected: "));
|
||||
print_byte_array(signature, SHA256HMAC_SIZE);
|
||||
printByteArray(signature, SHA256HMAC_SIZE);
|
||||
Serial.print(F("Calculated: "));
|
||||
print_byte_array(sig_calculated, SHA256HMAC_SIZE);
|
||||
printByteArray(sigCalculated, SHA256HMAC_SIZE);
|
||||
return false;
|
||||
}
|
||||
Serial.println(F("HMAC verification successful!"));
|
||||
return true;
|
||||
}
|
||||
|
||||
bool verify_json_hmac(JsonObject root) {
|
||||
JsonObject msg = root["msg"];
|
||||
if (!msg) {
|
||||
Serial.println(F("No message object found in JSON."));
|
||||
#define WLED_HMAC_TEST_PW "guessihadthekeyafterall"
|
||||
#define WLED_HMAC_TEST_PSK "a6f8488da62c5888d7f640276676e78da8639faf0495110b43e226b35ac37a4c"
|
||||
|
||||
bool verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen) {
|
||||
// Extract the signature from the JSON string
|
||||
size_t jsonLen = strlen(jsonStr);
|
||||
if (jsonLen > maxLen) { // memory safety
|
||||
Serial.println(F("JSON string too long!"));
|
||||
Serial.print(F("Length: "));
|
||||
Serial.print(jsonLen);
|
||||
Serial.print(F(", max: "));
|
||||
Serial.println(maxLen);
|
||||
return false;
|
||||
}
|
||||
const char *sig = msg["sig"];
|
||||
if (sig == nullptr) {
|
||||
Serial.print(F("Received JSON: "));
|
||||
Serial.println(jsonStr);
|
||||
char* sigPos = strstr(jsonStr, PSTR("\"sig\":\""));
|
||||
if (sigPos == nullptr) {
|
||||
Serial.println(F("No signature found in JSON."));
|
||||
return false;
|
||||
}
|
||||
|
||||
StaticJsonDocument<256> doc;
|
||||
DeserializationError error = deserializeJson(doc, jsonStr +7);
|
||||
if (error) {
|
||||
Serial.print(F("deserializeJson() failed: "));
|
||||
Serial.println(error.c_str());
|
||||
return false;
|
||||
}
|
||||
const char* sig = doc.as<const char*>();
|
||||
if (sig == nullptr) {
|
||||
Serial.println(F("Failed signature JSON."));
|
||||
return false;
|
||||
}
|
||||
Serial.print(F("Received signature: "));
|
||||
Serial.println(sig);
|
||||
|
||||
// extract the message object from the JSON string
|
||||
char* msgPos = strstr(jsonStr, PSTR("\"msg\":\""));
|
||||
char* objStart = strchr(msgPos + 7, '{');
|
||||
size_t maxObjLen = jsonLen - (objStart - jsonStr);
|
||||
uint32_t objDepth = 0;
|
||||
char* objEnd = nullptr;
|
||||
|
||||
for (size_t i = 0; i < maxObjLen; i++) {
|
||||
if (objStart[i] == '{') objDepth++;
|
||||
if (objStart [i] == '}') objDepth--;
|
||||
if (objDepth == 0) {
|
||||
objEnd = objStart + i;
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
if (objEnd == nullptr) {
|
||||
Serial.println(F("Couldn't find msg object end."));
|
||||
return false;
|
||||
}
|
||||
|
||||
// Convert the signature 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
|
||||
if (len != SHA256HMAC_SIZE) {
|
||||
Serial.println(F("Received sig not expected size!"));
|
||||
return false;
|
||||
}
|
||||
unsigned char sigByteArray[len];
|
||||
hexStringToByteArray(sig, sigByteArray, len);
|
||||
|
||||
// Calculate the HMAC of the message object
|
||||
return hmacVerify((const byte*)objStart, objEnd - objStart + 1, WLED_HMAC_TEST_PSK, sigByteArray);
|
||||
}
|
||||
|
||||
bool hmac_test() {
|
||||
bool hmacTest() {
|
||||
Serial.println(F("Testing HMAC..."));
|
||||
unsigned long start = millis();
|
||||
char message[] = "Hello, World!";
|
||||
char psk[] = "tokyo";
|
||||
const char message[] = "Hello, World!";
|
||||
const char psk[] = "d0c0ffeedeadbeef";
|
||||
byte signature[SHA256HMAC_SIZE];
|
||||
hmac_sign(message, psk, signature);
|
||||
hmacSign((const byte*)message, strlen(message), psk, signature);
|
||||
Serial.print(F("Took "));
|
||||
Serial.print(millis() - start);
|
||||
Serial.println(F("ms to sign message."));
|
||||
Serial.print(F("Signature: "));
|
||||
print_byte_array(signature, SHA256HMAC_SIZE);
|
||||
printByteArray(signature, SHA256HMAC_SIZE);
|
||||
start = millis();
|
||||
bool result = hmac_verify(message, psk, signature);
|
||||
bool result = hmacVerify((const byte*)message, strlen(message), psk, signature);
|
||||
Serial.print(F("Took "));
|
||||
Serial.print(millis() - start);
|
||||
Serial.println(F("ms to verify signature."));
|
||||
|
@ -326,7 +326,8 @@ function handleWindowMessageEvent(event) {
|
||||
sraWindow = event.source;
|
||||
sraOrigin = event.origin;
|
||||
} else if (json['wled-rc'] === 'hmac') {
|
||||
console.log(`Received HMAC: ${json['hmac']}`);
|
||||
console.log(`Received HMAC: ${json['sig']}`);
|
||||
requestJson(json);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1435,6 +1436,14 @@ function makeWS() {
|
||||
if (isInfo) populateInfo(i);
|
||||
} else
|
||||
i = lastinfo;
|
||||
if (json.error) {
|
||||
if (json.error == 1) {
|
||||
showToast('HMAC verification failed! Please make sure you used the right password!', true);
|
||||
return;
|
||||
}
|
||||
showToast(json.error, true);
|
||||
return;
|
||||
}
|
||||
var s = json.state ? json.state : json;
|
||||
displayRover(i, s);
|
||||
readState(s);
|
||||
|
@ -96,9 +96,10 @@ uint16_t approximateKelvinFromRGB(uint32_t rgb);
|
||||
void setRandomColor(byte* rgb);
|
||||
|
||||
//crypto.cpp
|
||||
void hmac_sign(const char* message, const char* psk, byte* signature);
|
||||
bool hmac_verify(const char* message, const char* psk, const byte* signature);
|
||||
bool hmac_test();
|
||||
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 verifyHmacFromJsonStr(const char* jsonStr, uint32_t maxLen);
|
||||
bool hmacTest();
|
||||
|
||||
//dmx.cpp
|
||||
void initDMX();
|
||||
|
@ -556,7 +556,7 @@ void WLED::setup()
|
||||
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 1); //enable brownout detector
|
||||
#endif
|
||||
|
||||
hmac_test();
|
||||
hmacTest();
|
||||
}
|
||||
|
||||
void WLED::beginStrip()
|
||||
|
@ -288,6 +288,12 @@ void initServer()
|
||||
bool isConfig = false;
|
||||
|
||||
Serial.println("JSON request");
|
||||
Serial.println((const char*)request->_tempObject);
|
||||
if (!verifyHmacFromJsonStr((const char*)request->_tempObject, request->contentLength())) {
|
||||
//releaseJSONBufferLock();
|
||||
serveJsonError(request, 401, ERR_DENIED);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!requestJSONBufferLock(14)) {
|
||||
serveJsonError(request, 503, ERR_NOBUF);
|
||||
@ -296,26 +302,26 @@ void initServer()
|
||||
|
||||
DeserializationError error = deserializeJson(*pDoc, (uint8_t*)(request->_tempObject));
|
||||
|
||||
// if enabled, calculate HMAC and verify it
|
||||
Serial.println(F("HMAC verification"));
|
||||
Serial.write((const char*)request->_tempObject, request->contentLength());
|
||||
// // if enabled, calculate HMAC and verify it
|
||||
// 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, '{');
|
||||
// // 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<JsonObject>();
|
||||
if (error || root.isNull()) {
|
||||
@ -324,17 +330,6 @@ 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<const char*>());
|
||||
|
||||
@ -348,7 +343,11 @@ void initServer()
|
||||
DEBUG_PRINTLN();
|
||||
#endif
|
||||
*/
|
||||
verboseResponse = deserializeState(root);
|
||||
if (root.containsKey("msg")) {
|
||||
verboseResponse = deserializeState(root["msg"]);
|
||||
} else {
|
||||
verboseResponse = deserializeState(root);
|
||||
}
|
||||
} else {
|
||||
if (!correctPIN && strlen(settingsPIN)>0) {
|
||||
releaseJSONBufferLock();
|
||||
|
@ -41,6 +41,9 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.print(F("WS message: "));
|
||||
Serial.println((const char*)data);
|
||||
|
||||
DeserializationError error = deserializeJson(*pDoc, data, len);
|
||||
JsonObject root = pDoc->as<JsonObject>();
|
||||
if (error || root.isNull()) {
|
||||
@ -48,12 +51,17 @@ void wsEvent(AsyncWebSocket * server, AsyncWebSocketClient * client, AwsEventTyp
|
||||
return;
|
||||
}
|
||||
if (root["v"] && root.size() == 1) {
|
||||
//if the received value is just "{"v":true}", send only to this client
|
||||
// if the received value is just "{"v":true}", send only to this client
|
||||
verboseResponse = true;
|
||||
} else if (root.containsKey("lv")) {
|
||||
wsLiveClientId = root["lv"] ? client->id() : 0;
|
||||
} else {
|
||||
verboseResponse = deserializeState(root);
|
||||
if (!verifyHmacFromJsonStr((const char*)data, len)) {
|
||||
releaseJSONBufferLock();
|
||||
client->text(F("{\"error\":1}")); // ERR_DENIED
|
||||
return;
|
||||
}
|
||||
verboseResponse = deserializeState(root["msg"]);
|
||||
}
|
||||
releaseJSONBufferLock();
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user