Skip to content

Commit fea140e

Browse files
Copilotnetmindz
andcommitted
Backport version reporting from PR #5093 and #5111
Co-authored-by: netmindz <[email protected]>
1 parent 799f065 commit fea140e

File tree

3 files changed

+103
-0
lines changed

3 files changed

+103
-0
lines changed

wled00/fcn_declare.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -400,6 +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 crc16(const unsigned char* data_p, size_t length);
403+
String computeSHA1(const String& input);
404+
String getDeviceId();
403405
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);
404406
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);
405407
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: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -631,6 +631,8 @@ void serializeInfo(JsonObject root)
631631
root[F("vid")] = VERSION;
632632
root[F("cn")] = F(WLED_CODENAME);
633633
root[F("release")] = releaseString;
634+
root[F("repo")] = repoString;
635+
root[F("deviceId")] = getDeviceId();
634636

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

wled00/util.cpp

Lines changed: 99 additions & 0 deletions
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_adc_cal.h"
1316
#endif
1417

1518

@@ -745,3 +748,99 @@ void handleBootLoop() {
745748

746749
ESP.restart(); // restart cleanly and don't wait for another crash
747750
}
751+
752+
// Platform-agnostic SHA1 computation from String input
753+
String computeSHA1(const String& input) {
754+
#ifdef ESP8266
755+
return sha1(input); // ESP8266 has built-in sha1() function
756+
#else
757+
// ESP32: Compute SHA1 hash using mbedtls
758+
unsigned char shaResult[20]; // SHA1 produces 20 bytes
759+
mbedtls_sha1_context ctx;
760+
761+
mbedtls_sha1_init(&ctx);
762+
mbedtls_sha1_starts_ret(&ctx);
763+
mbedtls_sha1_update_ret(&ctx, (const unsigned char*)input.c_str(), input.length());
764+
mbedtls_sha1_finish_ret(&ctx, shaResult);
765+
mbedtls_sha1_free(&ctx);
766+
767+
// Convert to hexadecimal string
768+
char hexString[41];
769+
for (int i = 0; i < 20; i++) {
770+
sprintf(&hexString[i*2], "%02x", shaResult[i]);
771+
}
772+
hexString[40] = '\0';
773+
774+
return String(hexString);
775+
#endif
776+
}
777+
778+
#ifdef ESP32
779+
String generateDeviceFingerprint() {
780+
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
781+
esp_chip_info_t chip_info;
782+
esp_chip_info(&chip_info);
783+
esp_efuse_mac_get_default((uint8_t*)fp);
784+
fp[1] ^= ESP.getFlashChipSize();
785+
#if ESP_IDF_VERSION >= ESP_IDF_VERSION_VAL(5, 0, 0)
786+
fp[0] ^= chip_info.full_revision | (chip_info.model << 16);
787+
#else
788+
fp[0] ^= chip_info.revision | (chip_info.model << 16);
789+
#endif
790+
// mix in ADC calibration data:
791+
esp_adc_cal_characteristics_t ch;
792+
#if SOC_ADC_MAX_BITWIDTH == 13 // S2 has 13 bit ADC
793+
#define BIT_WIDTH ADC_WIDTH_BIT_13
794+
#else
795+
#define BIT_WIDTH ADC_WIDTH_BIT_12
796+
#endif
797+
esp_adc_cal_characterize(ADC_UNIT_1, ADC_ATTEN_DB_11, BIT_WIDTH, 1100, &ch);
798+
fp[0] ^= ch.coeff_a;
799+
fp[1] ^= ch.coeff_b;
800+
if (ch.low_curve) {
801+
for (int i = 0; i < 8; i++) {
802+
fp[0] ^= ch.low_curve[i];
803+
}
804+
}
805+
if (ch.high_curve) {
806+
for (int i = 0; i < 8; i++) {
807+
fp[1] ^= ch.high_curve[i];
808+
}
809+
}
810+
char fp_string[17]; // 16 hex chars + null terminator
811+
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
812+
return String(fp_string);
813+
}
814+
#else // ESP8266
815+
String generateDeviceFingerprint() {
816+
uint32_t fp[2] = {0, 0}; // create 64 bit fingerprint
817+
WiFi.macAddress((uint8_t*)&fp); // use MAC address as fingerprint base
818+
fp[0] ^= ESP.getFlashChipId();
819+
fp[1] ^= ESP.getFlashChipSize() | ESP.getFlashChipVendorId() << 16;
820+
char fp_string[17]; // 16 hex chars + null terminator
821+
sprintf(fp_string, "%08X%08X", fp[1], fp[0]);
822+
return String(fp_string);
823+
}
824+
#endif
825+
826+
// Generate a device ID based on SHA1 hash of MAC address salted with other unique device info
827+
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
828+
String getDeviceId() {
829+
static String cachedDeviceId = "";
830+
if (cachedDeviceId.length() > 0) return cachedDeviceId;
831+
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
832+
// MAC is salted with other consistent device info to avoid rainbow table attacks.
833+
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
834+
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
835+
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
836+
837+
String firstHash = computeSHA1(generateDeviceFingerprint());
838+
839+
// Second hash: SHA1 of the first hash
840+
String secondHash = computeSHA1(firstHash);
841+
842+
// Concatenate first hash + last 2 chars of second hash
843+
cachedDeviceId = firstHash + secondHash.substring(38);
844+
845+
return cachedDeviceId;
846+
}

0 commit comments

Comments
 (0)