diff --git a/wled00/wled_server.cpp b/wled00/wled_server.cpp index a8cbc433..f6b259fe 100644 --- a/wled00/wled_server.cpp +++ b/wled00/wled_server.cpp @@ -246,44 +246,140 @@ static bool isValidBootloader(const uint8_t* data, size_t len) { uint8_t segmentCount = data[1]; if (segmentCount > 16) return false; - // Use ESP-IDF image verification for more thorough validation - esp_image_metadata_t metadata; - esp_image_load_mode_t mode = ESP_IMAGE_VERIFY; - - // Create a simple data structure for verification - // Note: esp_image_verify expects data in flash, so we do basic checks here - // The full verification will be done after buffering is complete - return true; } -// Verify complete buffered bootloader using ESP-IDF validation +// Verify complete buffered bootloader using ESP-IDF validation approach +// This matches the key validation steps from esp_image_verify() in ESP-IDF static bool verifyBootloaderImage(const uint8_t* buffer, size_t len) { - // Basic magic byte check - if (len < 32 || buffer[0] != 0xE9) { + // ESP32 image header structure (based on esp_image_format.h) + // Offset 0: magic (0xE9) + // Offset 1: segment_count + // Offset 2: spi_mode + // Offset 3: spi_speed (4 bits) + spi_size (4 bits) + // Offset 4-7: entry_addr (uint32_t) + // Offset 8: wp_pin + // Offset 9-11: spi_pin_drv[3] + // Offset 12-13: chip_id (uint16_t, little-endian) + // Offset 14: min_chip_rev + // Offset 15-22: reserved[8] + // Offset 23: hash_appended + + const size_t MIN_IMAGE_HEADER_SIZE = 24; + + // 1. Validate minimum size for header + if (len < MIN_IMAGE_HEADER_SIZE) { + DEBUG_PRINTLN(F("Bootloader too small - invalid header")); + return false; + } + + // 2. Magic byte check (matches esp_image_verify step 1) + if (buffer[0] != 0xE9) { DEBUG_PRINTLN(F("Invalid bootloader magic byte")); return false; } - // Check segment count + // 3. Segment count validation (matches esp_image_verify step 2) uint8_t segmentCount = buffer[1]; - if (segmentCount > 16) { - DEBUG_PRINTLN(F("Invalid segment count")); + if (segmentCount == 0 || segmentCount > 16) { + DEBUG_PRINTF_P(PSTR("Invalid segment count: %d\n"), segmentCount); return false; } - // Verify chip ID matches (basic check - the image header contains chip ID at offset 12) - if (len >= 16) { - uint16_t chipId = (buffer[13] << 8) | buffer[12]; - // ESP32 chip IDs: 0x0000 (ESP32), 0x0002 (ESP32-S2), 0x0005 (ESP32-C3), 0x0009 (ESP32-S3), etc. - // For now, we just check it's not obviously wrong - if (chipId > 0x00FF) { - DEBUG_PRINTLN(F("Invalid chip ID in bootloader")); - return false; - } + // 4. SPI mode validation (basic sanity check) + uint8_t spiMode = buffer[2]; + if (spiMode > 3) { // Valid modes are 0-3 (QIO, QOUT, DIO, DOUT) + DEBUG_PRINTF_P(PSTR("Invalid SPI mode: %d\n"), spiMode); + return false; } - DEBUG_PRINTLN(F("Bootloader validation passed")); + // 5. Chip ID validation (matches esp_image_verify step 3) + uint16_t chipId = buffer[12] | (buffer[13] << 8); // Little-endian + + // Known ESP32 chip IDs from ESP-IDF: + // 0x0000 = ESP32 + // 0x0002 = ESP32-S2 + // 0x0005 = ESP32-C3 + // 0x0009 = ESP32-S3 + // 0x000C = ESP32-C2 + // 0x000D = ESP32-C6 + // 0x0010 = ESP32-H2 + + #if defined(CONFIG_IDF_TARGET_ESP32) + if (chipId != 0x0000) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32 (0x0000), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32S2) + if (chipId != 0x0002) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-S2 (0x0002), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32C3) + if (chipId != 0x0005) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C3 (0x0005), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32S3) + if (chipId != 0x0009) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-S3 (0x0009), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32C2) + if (chipId != 0x000C) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C2 (0x000C), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32C6) + if (chipId != 0x000D) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-C6 (0x000D), got 0x%04X\n"), chipId); + return false; + } + #elif defined(CONFIG_IDF_TARGET_ESP32H2) + if (chipId != 0x0010) { + DEBUG_PRINTF_P(PSTR("Chip ID mismatch - expected ESP32-H2 (0x0010), got 0x%04X\n"), chipId); + return false; + } + #else + // Generic validation - chip ID should be valid + if (chipId > 0x00FF) { + DEBUG_PRINTF_P(PSTR("Invalid chip ID: 0x%04X\n"), chipId); + return false; + } + #endif + + // 6. Entry point validation (should be in valid memory range) + uint32_t entryAddr = buffer[4] | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24); + // ESP32 bootloader entry points are typically in IRAM range (0x40000000 - 0x40400000) + // or ROM range (0x40000000 and above) + if (entryAddr < 0x40000000 || entryAddr > 0x50000000) { + DEBUG_PRINTF_P(PSTR("Invalid entry address: 0x%08X\n"), entryAddr); + return false; + } + + // 7. Basic segment structure validation + // Each segment has a header: load_addr (4 bytes) + data_len (4 bytes) + size_t offset = MIN_IMAGE_HEADER_SIZE; + for (uint8_t i = 0; i < segmentCount && offset + 8 <= len; i++) { + uint32_t segmentSize = buffer[offset + 4] | (buffer[offset + 5] << 8) | + (buffer[offset + 6] << 16) | (buffer[offset + 7] << 24); + + // Segment size sanity check (shouldn't be > 32KB for bootloader segments) + if (segmentSize > 0x8000) { + DEBUG_PRINTF_P(PSTR("Segment %d too large: %d bytes\n"), i, segmentSize); + return false; + } + + offset += 8 + segmentSize; // Skip segment header and data + } + + // 8. Verify total size is reasonable + if (len > 0x8000) { // Bootloader shouldn't exceed 32KB + DEBUG_PRINTF_P(PSTR("Bootloader too large: %d bytes\n"), len); + return false; + } + + DEBUG_PRINTLN(F("Bootloader validation passed - matches esp_image_verify checks")); return true; } #endif