Skip to content

Commit d047ee0

Browse files
committed
Add deviceId
1 parent 315f147 commit d047ee0

File tree

3 files changed

+101
-2
lines changed

3 files changed

+101
-2
lines changed

wled00/fcn_declare.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,7 +400,8 @@ uint8_t extractModeSlider(uint8_t mode, uint8_t slider, char *dest, uint8_t maxL
400400
int16_t extractModeDefaults(uint8_t mode, const char *segVar);
401401
void checkSettingsPIN(const char *pin);
402402
uint16_t __attribute__((pure)) crc16(const unsigned char* data_p, size_t length); // WLEDMM: added attribute pure
403-
403+
String computeSHA1(const String& input);
404+
String getDeviceId();
404405
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);
405406
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);
406407
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: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -913,6 +913,7 @@ void serializeInfo(JsonObject root)
913913
//root[F("cn")] = F(WLED_CODENAME); //WLEDMM removed
914914
root[F("release")] = FPSTR(releaseString);
915915
root[F("rel")] = FPSTR(releaseString); //WLEDMM to add bin name
916+
root[F("deviceId")] = getDeviceId();
916917

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

wled00/util.cpp

Lines changed: 98 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,13 @@
11
#include "wled.h"
22
#include "fcn_declare.h"
33
#include "const.h"
4-
4+
#ifdef ESP8266
5+
#include "user_interface.h" // for bootloop detection
6+
#include <Hash.h> // for SHA1 on ESP8266
7+
#else
8+
#include "mbedtls/sha1.h" // for SHA1 on ESP32
9+
#include "esp_efuse.h"
10+
#endif
511

612
//helper to get int value at a position in string
713
int getNumVal(const String* req, uint16_t pos)
@@ -668,3 +674,94 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
668674
uint32_t diff = upperlimit - lowerlimit;
669675
return hw_random(diff) + lowerlimit;
670676
}
677+
678+
// Platform-agnostic SHA1 computation from String input
679+
String computeSHA1(const String& input) {
680+
#ifdef ESP8266
681+
return sha1(input); // ESP8266 has built-in sha1() function
682+
#else
683+
// ESP32: Compute SHA1 hash using mbedtls
684+
unsigned char shaResult[20]; // SHA1 produces 20 bytes
685+
mbedtls_sha1_context ctx;
686+
687+
mbedtls_sha1_init(&ctx);
688+
mbedtls_sha1_starts_ret(&ctx);
689+
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)input.c_str(), input.length());
690+
mbedtls_sha1_finish_ret(&ctx, shaResult);
691+
mbedtls_sha1_free(&ctx);
692+
693+
// Convert to hexadecimal string
694+
char hexString[41];
695+
for (int i = 0; i < 20; i++) {
696+
sprintf(&hexString[i*2], "%02x", shaResult[i]);
697+
}
698+
hexString[40] = '\0';
699+
700+
return String(hexString);
701+
#endif
702+
}
703+
704+
#ifdef ESP32
705+
static String dump_raw_block(esp_efuse_block_t block)
706+
{
707+
const int WORDS = 8; // ESP32: 8×32-bit words per block i.e. 256bits
708+
uint32_t buf[WORDS] = {0};
709+
710+
const esp_efuse_desc_t d = {
711+
.efuse_block = block,
712+
.bit_start = 0,
713+
.bit_count = WORDS * 32
714+
};
715+
const esp_efuse_desc_t* field[2] = { &d, NULL };
716+
717+
esp_err_t err = esp_efuse_read_field_blob(field, buf, WORDS * 32);
718+
if (err != ESP_OK) {
719+
return "";
720+
}
721+
722+
String result = "";
723+
for (const unsigned int i : buf) {
724+
char line[32];
725+
sprintf(line, "0x%08X", i);
726+
result += line;
727+
}
728+
return result;
729+
}
730+
#endif
731+
732+
733+
// Generate a device ID based on SHA1 hash of MAC address salted with "WLED"
734+
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
735+
String getDeviceId() {
736+
static String cachedDeviceId = "";
737+
if (cachedDeviceId.length() > 0) return cachedDeviceId;
738+
739+
uint8_t mac[6];
740+
WiFi.macAddress(mac);
741+
char macStr[18];
742+
sprintf(macStr, "%02x:%02x:%02x:%02x:%02x:%02x", mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]);
743+
744+
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
745+
// MAC is salted with other consistent device info to avoid rainbow table attacks.
746+
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
747+
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
748+
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
749+
#ifdef ESP8266
750+
String deviceString = String(macStr) + "WLED" + ESP.getFlashChipId();
751+
#else
752+
String deviceString = String(macStr) + "WLED" + ESP.getChipModel() + ESP.getChipRevision();
753+
deviceString += dump_raw_block(EFUSE_BLK0);
754+
deviceString += dump_raw_block(EFUSE_BLK1);
755+
deviceString += dump_raw_block(EFUSE_BLK2);
756+
deviceString += dump_raw_block(EFUSE_BLK3);
757+
#endif
758+
String firstHash = computeSHA1(deviceString);
759+
760+
// Second hash: SHA1 of the first hash
761+
String secondHash = computeSHA1(firstHash);
762+
763+
// Concatenate first hash + last 2 chars of second hash
764+
cachedDeviceId = firstHash + secondHash.substring(38);
765+
766+
return cachedDeviceId;
767+
}

0 commit comments

Comments
 (0)