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
713int 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