Skip to content

Commit 4615eb8

Browse files
authored
Merge pull request #5093 from netmindz/deviceId
Add Device ID to JSON Info
2 parents 54b7dfe + 9b787e1 commit 4615eb8

File tree

3 files changed

+101
-1
lines changed

3 files changed

+101
-1
lines changed

wled00/fcn_declare.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -401,6 +401,8 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
401401
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
402402
void checkSettingsPIN(const char *pin);
403403
uint16_t crc16(const unsigned char* data_p, size_t length);
404+
String computeSHA1(const String& input);
405+
String getDeviceId();
404406
uint16_t beatsin88_t(accum88 beats_per_minute_88, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
405407
uint16_t beatsin16_t(accum88 beats_per_minute, uint16_t lowest = 0, uint16_t highest = 65535, uint32_t timebase = 0, uint16_t phase_offset = 0);
406408
uint8_t beatsin8_t(accum88 beats_per_minute, uint8_t lowest = 0, uint8_t highest = 255, uint32_t timebase = 0, uint8_t phase_offset = 0);

wled00/json.cpp

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
#include "wled.h"
22

3+
34
#define JSON_PATH_STATE 1
45
#define JSON_PATH_INFO 2
56
#define JSON_PATH_STATE_INFO 3
@@ -690,13 +691,15 @@ void serializeState(JsonObject root, bool forPreset, bool includeBri, bool segme
690691
}
691692
}
692693

694+
693695
void serializeInfo(JsonObject root)
694696
{
695697
root[F("ver")] = versionString;
696698
root[F("vid")] = VERSION;
697699
root[F("cn")] = F(WLED_CODENAME);
698700
root[F("release")] = releaseString;
699701
root[F("repo")] = repoString;
702+
root[F("deviceId")] = getDeviceId();
700703

701704
JsonObject leds = root.createNestedObject(F("leds"));
702705
leds[F("count")] = strip.getLengthTotal();

wled00/util.cpp

Lines changed: 96 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,16 @@
33
#include "const.h"
44
#ifdef ESP8266
55
#include "user_interface.h" // for bootloop detection
6+
#include <Hash.h> // for SHA1 on ESP8266
67
#else
78
#include <Update.h>
89
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(4, 4, 0)
910
#include "esp32/rtc.h" // for bootloop detection
1011
#elif ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(3, 3, 0)
1112
#include "soc/rtc.h"
1213
#endif
14+
#include "mbedtls/sha1.h" // for SHA1 on ESP32
15+
#include "esp_efuse.h"
1316
#endif
1417

1518

@@ -1125,4 +1128,96 @@ uint8_t perlin8(uint16_t x, uint16_t y) {
11251128

11261129
uint8_t perlin8(uint16_t x, uint16_t y, uint16_t z) {
11271130
return (((perlin3D_raw((uint32_t)x << 8, (uint32_t)y << 8, (uint32_t)z << 8, true) * 2015) >> 10) + 33168) >> 8; //scale to 16 bit, offset, then scale to 8bit
1128-
}
1131+
}
1132+
1133+
// Platform-agnostic SHA1 computation from String input
1134+
String computeSHA1(const String& input) {
1135+
#ifdef ESP8266
1136+
return sha1(input); // ESP8266 has built-in sha1() function
1137+
#else
1138+
// ESP32: Compute SHA1 hash using mbedtls
1139+
unsigned char shaResult[20]; // SHA1 produces 20 bytes
1140+
mbedtls_sha1_context ctx;
1141+
1142+
mbedtls_sha1_init(&ctx);
1143+
mbedtls_sha1_starts_ret(&ctx);
1144+
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)input.c_str(), input.length());
1145+
mbedtls_sha1_finish_ret(&ctx, shaResult);
1146+
mbedtls_sha1_free(&ctx);
1147+
1148+
// Convert to hexadecimal string
1149+
char hexString[41];
1150+
for (int i = 0; i < 20; i++) {
1151+
sprintf(&hexString[i*2], "%02x", shaResult[i]);
1152+
}
1153+
hexString[40] = '\0';
1154+
1155+
return String(hexString);
1156+
#endif
1157+
}
1158+
1159+
#ifdef ESP32
1160+
static String dump_raw_block(esp_efuse_block_t block)
1161+
{
1162+
const int WORDS = 8; // ESP32: 8×32-bit words per block i.e. 256bits
1163+
uint32_t buf[WORDS] = {0};
1164+
1165+
const esp_efuse_desc_t d = {
1166+
.efuse_block = block,
1167+
.bit_start = 0,
1168+
.bit_count = WORDS * 32
1169+
};
1170+
const esp_efuse_desc_t* field[2] = { &d, NULL };
1171+
1172+
esp_err_t err = esp_efuse_read_field_blob(field, buf, WORDS * 32);
1173+
if (err != ESP_OK) {
1174+
return "";
1175+
}
1176+
1177+
String result = "";
1178+
for (const unsigned int i : buf) {
1179+
char line[32];
1180+
sprintf(line, "0x%08X", i);
1181+
result += line;
1182+
}
1183+
return result;
1184+
}
1185+
#endif
1186+
1187+
1188+
// Generate a device ID based on SHA1 hash of MAC address salted with "WLED"
1189+
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
1190+
String getDeviceId() {
1191+
static String cachedDeviceId = "";
1192+
if (cachedDeviceId.length() > 0) return cachedDeviceId;
1193+
1194+
uint8_t mac[6];
1195+
WiFi.macAddress(mac);
1196+
char macStr[18];
1197+
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
1198+
1199+
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
1200+
// MAC is salted with other consistent device info to avoid rainbow table attacks.
1201+
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
1202+
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
1203+
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
1204+
#ifdef ESP8266
1205+
String deviceString = String(macStr) + "WLED" + ESP.getFlashChipId();
1206+
#else
1207+
String deviceString = String(macStr) + "WLED" + ESP.getChipModel() + ESP.getChipRevision();
1208+
deviceString += dump_raw_block(EFUSE_BLK0);
1209+
deviceString += dump_raw_block(EFUSE_BLK1);
1210+
deviceString += dump_raw_block(EFUSE_BLK2);
1211+
deviceString += dump_raw_block(EFUSE_BLK3);
1212+
#endif
1213+
String firstHash = computeSHA1(deviceString);
1214+
1215+
// Second hash: SHA1 of the first hash
1216+
String secondHash = computeSHA1(firstHash);
1217+
1218+
// Concatenate first hash + last 2 chars of second hash
1219+
cachedDeviceId = firstHash + secondHash.substring(38);
1220+
1221+
return cachedDeviceId;
1222+
}
1223+

0 commit comments

Comments
 (0)