Compare commits

...

4 Commits

Author SHA1 Message Date
copilot-swe-agent[bot]
b4ef18bc8d Complete bootloader checksum fix - ready for review
Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2026-01-31 12:02:59 +00:00
copilot-swe-agent[bot]
1febf0bdd9 Address code review feedback
- Define MAX_SEGMENT_SIZE constant instead of magic number
- Use bitwise alignment operation for better efficiency
- Clarify that checksum byte is part of ESP-IDF image format

Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2026-01-31 12:02:13 +00:00
copilot-swe-agent[bot]
b50b2a95e4 Fix bootloader SHA256 to only hash actual bootloader data, not full 32KB region
- Add getActualBootloaderSize() helper to parse bootloader image structure
- Calculate actual size by parsing segment headers, hash, and checksum
- Only hash the actual bootloader data instead of full BOOTLOADER_SIZE
- This prevents different hashes for same bootloader due to garbage data in unused memory

Co-authored-by: netmindz <442066+netmindz@users.noreply.github.com>
2026-01-31 11:59:56 +00:00
copilot-swe-agent[bot]
0e2225868a Initial plan 2026-01-31 11:53:23 +00:00
2 changed files with 108 additions and 3 deletions

View File

@@ -0,0 +1 @@
.

View File

@@ -289,11 +289,115 @@ void markOTAvalid() {
// Cache for bootloader SHA256 digest as hex string
static String bootloaderSHA256HexCache = "";
// Helper function to calculate actual bootloader size from flash
// Returns the actual size of the bootloader image, or 0 if invalid
static size_t getActualBootloaderSize() {
const size_t MIN_IMAGE_HEADER_SIZE = 24;
const size_t MAX_SEGMENT_SIZE = 0x20000; // 128KB maximum per segment
// We need to read enough data to parse all segment headers
// Typical bootloader has 4-6 segments, each header is 8 bytes
// Reading first 2KB should be sufficient for header parsing
const size_t PARSE_BUFFER_SIZE = 2048;
uint8_t* parseBuffer = (uint8_t*)malloc(PARSE_BUFFER_SIZE);
if (!parseBuffer) {
DEBUG_PRINTLN(F("Failed to allocate parse buffer"));
return 0;
}
// Read initial portion for parsing
if (esp_flash_read(NULL, parseBuffer, BOOTLOADER_OFFSET, PARSE_BUFFER_SIZE) != ESP_OK) {
DEBUG_PRINTLN(F("Failed to read bootloader header"));
free(parseBuffer);
return 0;
}
// Validate magic byte
if (parseBuffer[0] != 0xE9) {
DEBUG_PRINTF_P(PSTR("Invalid bootloader magic byte: 0x%02X\n"), parseBuffer[0]);
free(parseBuffer);
return 0;
}
// Get segment count
uint8_t segmentCount = parseBuffer[1];
if (segmentCount == 0 || segmentCount > 16) {
DEBUG_PRINTF_P(PSTR("Invalid segment count: %d\n"), segmentCount);
free(parseBuffer);
return 0;
}
// Parse segments to find actual bootloader size
size_t offset = MIN_IMAGE_HEADER_SIZE;
for (uint8_t i = 0; i < segmentCount; i++) {
// Check if segment header is within our parse buffer
if (offset + 8 > PARSE_BUFFER_SIZE) {
DEBUG_PRINTF_P(PSTR("Segment %d header at offset %d beyond parse buffer\n"), i, offset);
free(parseBuffer);
// Fall back to full size if we can't parse all segments
return BOOTLOADER_SIZE;
}
// Read segment size (little-endian uint32_t at offset+4)
uint32_t segmentSize = parseBuffer[offset + 4] |
(parseBuffer[offset + 5] << 8) |
(parseBuffer[offset + 6] << 16) |
(parseBuffer[offset + 7] << 24);
// Sanity check segment size
if (segmentSize > MAX_SEGMENT_SIZE) {
DEBUG_PRINTF_P(PSTR("Segment %d too large: %u bytes\n"), i, segmentSize);
free(parseBuffer);
return BOOTLOADER_SIZE; // Fall back to full size on error
}
offset += 8 + segmentSize; // Skip segment header (8 bytes) and data
}
size_t actualSize = offset;
// Check for appended SHA256 hash (byte 23 in header)
uint8_t hashAppended = parseBuffer[23];
if (hashAppended != 0) {
actualSize += 32;
DEBUG_PRINTLN(F("Bootloader has appended SHA256"));
}
// ESP32 bootloader images include a 1-byte checksum after segments/hash
// This is part of the standard ESP-IDF image format
actualSize += 1;
// Align to 16 bytes (ESP32 flash requirement)
// Use bitwise operation for efficient power-of-2 alignment
actualSize = (actualSize + 15) & ~15;
free(parseBuffer);
// Sanity check - actual size should not exceed allocated bootloader space
if (actualSize > BOOTLOADER_SIZE) {
DEBUG_PRINTF_P(PSTR("Calculated size %d exceeds max %d, using max\n"), actualSize, BOOTLOADER_SIZE);
return BOOTLOADER_SIZE;
}
DEBUG_PRINTF_P(PSTR("Actual bootloader size: %d bytes (from %d segments)\n"), actualSize, segmentCount);
return actualSize;
}
// Calculate and cache the bootloader SHA256 digest as hex string
void calculateBootloaderSHA256() {
if (!bootloaderSHA256HexCache.isEmpty()) return;
// Calculate SHA256
// First, determine the actual bootloader size
size_t actualBootloaderSize = getActualBootloaderSize();
if (actualBootloaderSize == 0) {
DEBUG_PRINTLN(F("Failed to determine bootloader size, using empty hash"));
bootloaderSHA256HexCache = "";
return;
}
// Calculate SHA256 only over the actual bootloader data
uint8_t sha256[32];
mbedtls_sha256_context ctx;
mbedtls_sha256_init(&ctx);
@@ -302,8 +406,8 @@ void calculateBootloaderSHA256() {
const size_t chunkSize = 256;
uint8_t buffer[chunkSize];
for (uint32_t offset = 0; offset < BOOTLOADER_SIZE; offset += chunkSize) {
size_t readSize = min((size_t)(BOOTLOADER_SIZE - offset), chunkSize);
for (uint32_t offset = 0; offset < actualBootloaderSize; offset += chunkSize) {
size_t readSize = min((size_t)(actualBootloaderSize - offset), chunkSize);
if (esp_flash_read(NULL, buffer, BOOTLOADER_OFFSET + offset, readSize) == ESP_OK) {
mbedtls_sha256_update(&ctx, buffer, readSize);
}