Cách Kiểm Tra Checksum Firmware OTA Không Cần Filesystem
Kiểm tra checksum (SHA256/MD5) khi tải firmware OTA mà không cần lưu vào filesystem (SPIFFS/LittleFS). Dưới đây là 3 phương pháp khả thi:
1. Tính Checksum Trực Trên Dòng Dữ Liệu (Streaming)
Ưu điểm:
-
Không tốn bộ nhớ lưu trữ
-
Vẫn đảm bảo tính toàn vẹn firmware
Code Mẫu (Arduino + HTTP):
cpp
Copy
#include <HTTPClient.h>
#include <Update.h>
#include <SHA256.h>
void performOTAWithChecksum() {
HTTPClient http;
http.begin("http://your-server/firmware.bin");
// Yêu cầu server gửi kèm checksum (qua header)
http.addHeader("X-Checksum-Request", "SHA256");
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
// Nhận checksum từ server (ví dụ: "a1b2c3...")
String serverChecksum = http.header("X-Firmware-Checksum");
// Khởi tạo SHA256
SHA256 sha256;
sha256.begin();
// Bắt đầu OTA
int firmwareSize = http.getSize();
if (Update.begin(firmwareSize)) {
WiFiClient *stream = http.getStreamPtr();
uint8_t buffer[512];
size_t totalRead = 0;
// Đọc từng chunk và cập nhật checksum
while (http.connected() && (totalRead < firmwareSize)) {
size_t bytesRead = stream->readBytes(buffer, sizeof(buffer));
sha256.update(buffer, bytesRead); // Cập nhật checksum
Update.write(buffer, bytesRead); // Ghi trực tiếp vào OTA partition
totalRead += bytesRead;
}
// Hoàn thành checksum
uint8_t hash[32];
sha256.end(hash);
// Chuyển hash sang dạng hex string
String localChecksum;
for (int i = 0; i < 32; i++) {
char str[3];
sprintf(str, "%02x", hash[i]);
localChecksum += str;
}
// So sánh với checksum từ server
if (serverChecksum.equalsIgnoreCase(localChecksum)) {
Serial.println("Checksum matched! Finalizing update...");
if (Update.end() && Update.isFinished()) {
ESP.restart();
}
} else {
Serial.println("Checksum mismatch! Aborting...");
Update.abort();
}
}
}
http.end();
}
2. Dùng "Two-Pass" OTA (Tải 2 Lần)
Ưu điểm:
-
Kiểm tra checksum trước khi áp dụng
-
Không cần filesystem
Cách hoạt động:
-
Lần 1: Tải firmware để tính checksum (không ghi vào flash).
-
Lần 2: Tải lại và ghi vào OTA partition nếu checksum khớp.
Code Tối Ưu:
cpp
Copy
bool verifyFirmwareChecksum(const char* url, const char* expectedChecksum) {
HTTPClient http;
http.begin(url);
int httpCode = http.GET();
if (httpCode == HTTP_CODE_OK) {
SHA256 sha256;
sha256.begin();
WiFiClient *stream = http.getStreamPtr();
uint8_t buffer[256];
while (http.connected()) {
size_t bytesRead = stream->readBytes(buffer, sizeof(buffer));
sha256.update(buffer, bytesRead);
}
uint8_t hash[32];
sha256.end(hash);
String localChecksum;
for (int i = 0; i < 32; i++) {
char str[3];
sprintf(str, "%02x", hash[i]);
localChecksum += str;
}
http.end();
return (localChecksum.equalsIgnoreCase(expectedChecksum));
}
http.end();
return false;
}
void safeOTAUpdate() {
const char* firmwareUrl = "http://server/firmware.bin";
const char* expectedChecksum = "a1b2c3..."; // Nhận từ server trước
// Bước 1: Kiểm tra checksum
if (verifyFirmwareChecksum(firmwareUrl, expectedChecksum)) {
// Bước 2: Tải và ghi vào OTA partition
performDirectOTA(firmwareUrl); // Dùng hàm ở phần trước
} else {
Serial.println("Checksum verification failed!");
}
}
3. Dùng "Delta OTA" (Chỉ Tải Phần Thay Đổi)
Ưu điểm:
-
Giảm dung lượng tải về → Giảm rủi ro checksum sai
-
Phù hợp firmware lớn (>1.5MB)
Cách triển khai:
-
Server tính toán diff giữa firmware hiện tại và mới.
-
ESP32 tải diff, áp dụng patch và kiểm tra checksum.
Thư viện hỗ trợ:
-
libxdelta3 (Cần port sang ESP32)
-
Tự triển khai thuật toán bsdiff
So Sánh Các Phương Pháp
Phương Pháp | Tốc Độ | Bộ Nhớ | Độ Tin Cậy | Phức Tạp |
|---|---|---|---|---|
Streaming Checksum | ⚡ Nhanh | 📦 Thấp | ⭐⭐ | Dễ |
Two-Pass OTA | 🐢 2 lần tải | 📦 Thấp | ⭐⭐⭐ | Trung bình |
Delta OTA | ⚡⚡ Rất nhanh | 📦📦 Cao | ⭐⭐⭐⭐ | Khó |
Lời Khuyên
-
Nếu firmware <1MB: Dùng Streaming Checksum (cân bằng giữa tốc độ và độ tin cậy).
-
Nếu firmware lớn (>1.5MB): Dùng Two-Pass OTA hoặc Delta OTA.
-
Luôn yêu cầu server gửi checksum qua header HTTP.