@@ -1,7 +1,13 @@
# include "wled.h"
# include "fcn_declare.h"
# include "const.h"
# ifdef ESP8266
# include "user_interface.h" // for bootloop detection
# include <Hash.h> // for SHA1 on ESP8266
# else
# include "mbedtls/sha1.h" // for SHA1 on ESP32
# include "esp_efuse.h"
# endif
//helper to get int value at a position in string
int getNumVal ( const String * req , uint16_t pos )
@@ -669,3 +675,94 @@ int32_t hw_random(int32_t lowerlimit, int32_t upperlimit) {
uint32_t diff = upperlimit - lowerlimit ;
return hw_random ( diff ) + lowerlimit ;
}
// Platform-agnostic SHA1 computation from String input
String computeSHA1 ( const String & input ) {
# ifdef ESP8266
return sha1 ( input ) ; // ESP8266 has built-in sha1() function
# else
// ESP32: Compute SHA1 hash using mbedtls
unsigned char shaResult [ 20 ] ; // SHA1 produces 20 bytes
mbedtls_sha1_context ctx ;
mbedtls_sha1_init ( & ctx ) ;
mbedtls_sha1_starts_ret ( & ctx ) ;
mbedtls_sha1_update_ret ( & ctx , ( const unsigned char * ) input . c_str ( ) , input . length ( ) ) ;
mbedtls_sha1_finish_ret ( & ctx , shaResult ) ;
mbedtls_sha1_free ( & ctx ) ;
// Convert to hexadecimal string
char hexString [ 41 ] ;
for ( int i = 0 ; i < 20 ; i + + ) {
sprintf ( & hexString [ i * 2 ] , " %02x " , shaResult [ i ] ) ;
}
hexString [ 40 ] = ' \0 ' ;
return String ( hexString ) ;
# endif
}
# ifdef ESP32
static String dump_raw_block ( esp_efuse_block_t block )
{
const int WORDS = 8 ; // ESP32: 8× 32-bit words per block i.e. 256bits
uint32_t buf [ WORDS ] = { 0 } ;
const esp_efuse_desc_t d = {
. efuse_block = block ,
. bit_start = 0 ,
. bit_count = WORDS * 32
} ;
const esp_efuse_desc_t * field [ 2 ] = { & d , NULL } ;
esp_err_t err = esp_efuse_read_field_blob ( field , buf , WORDS * 32 ) ;
if ( err ! = ESP_OK ) {
return " " ;
}
String result = " " ;
for ( const unsigned int i : buf ) {
char line [ 32 ] ;
sprintf ( line , " 0x%08X " , i ) ;
result + = line ;
}
return result ;
}
# endif
// Generate a device ID based on SHA1 hash of MAC address salted with "WLED"
// Returns: original SHA1 + last 2 chars of double-hashed SHA1 (42 chars total)
String getDeviceId ( ) {
static String cachedDeviceId = " " ;
if ( cachedDeviceId . length ( ) > 0 ) return cachedDeviceId ;
uint8_t mac [ 6 ] ;
WiFi . macAddress ( mac ) ;
char macStr [ 18 ] ;
sprintf ( macStr , " %02x:%02x:%02x:%02x:%02x:%02x " , mac [ 0 ] , mac [ 1 ] , mac [ 2 ] , mac [ 3 ] , mac [ 4 ] , mac [ 5 ] ) ;
// The device string is deterministic as it needs to be consistent for the same device, even after a full flash erase
// MAC is salted with other consistent device info to avoid rainbow table attacks.
// If the MAC address is known by malicious actors, they could precompute SHA1 hashes to impersonate devices,
// but as WLED developers are just looking at statistics and not authenticating devices, this is acceptable.
// If the usage data was exfiltrated, you could not easily determine the MAC from the device ID without brute forcing SHA1
# ifdef ESP8266
String deviceString = String ( macStr ) + " WLED " + ESP . getFlashChipId ( ) ;
# else
String deviceString = String ( macStr ) + " WLED " + ESP . getChipModel ( ) + ESP . getChipRevision ( ) ;
deviceString + = dump_raw_block ( EFUSE_BLK0 ) ;
deviceString + = dump_raw_block ( EFUSE_BLK1 ) ;
deviceString + = dump_raw_block ( EFUSE_BLK2 ) ;
deviceString + = dump_raw_block ( EFUSE_BLK3 ) ;
# endif
String firstHash = computeSHA1 ( deviceString ) ;
// Second hash: SHA1 of the first hash
String secondHash = computeSHA1 ( firstHash ) ;
// Concatenate first hash + last 2 chars of second hash
cachedDeviceId = firstHash + secondHash . substring ( 38 ) ;
return cachedDeviceId ;
}