Add activation version 2

This commit is contained in:
Terrence
2025-04-14 15:44:06 +08:00
parent f76f31aa12
commit d5d8b34b2b
6 changed files with 194 additions and 38 deletions

View File

@@ -1,6 +1,5 @@
#include "ota.h"
#include "system_info.h"
#include "board.h"
#include "settings.h"
#include "assets/lang_config.h"
@@ -9,6 +8,9 @@
#include <esp_partition.h>
#include <esp_ota_ops.h>
#include <esp_app_format.h>
#include <esp_efuse.h>
#include <esp_efuse_table.h>
#include <esp_hmac.h>
#include <cstring>
#include <vector>
@@ -20,6 +22,17 @@
Ota::Ota() {
SetCheckVersionUrl(CONFIG_OTA_VERSION_URL);
// Read Serial Number from efuse user_data
uint8_t serial_number[33] = {0};
if (esp_efuse_read_field_blob(ESP_EFUSE_USER_DATA, serial_number, 32 * 8) == ESP_OK) {
if (serial_number[0] == 0) {
has_serial_number_ = false;
} else {
serial_number_ = std::string(reinterpret_cast<char*>(serial_number), 32);
has_serial_number_ = true;
}
}
}
Ota::~Ota() {
@@ -33,6 +46,25 @@ void Ota::SetHeader(const std::string& key, const std::string& value) {
headers_[key] = value;
}
Http* Ota::SetupHttp() {
auto& board = Board::GetInstance();
auto app_desc = esp_app_get_description();
auto http = board.CreateHttp();
for (const auto& header : headers_) {
http->SetHeader(header.first, header.second);
}
http->SetHeader("Activation-Version", has_serial_number_ ? "2" : "1");
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
http->SetHeader("Client-Id", board.GetUuid());
http->SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);
http->SetHeader("Accept-Language", Lang::CODE);
http->SetHeader("Content-Type", "application/json");
return http;
}
bool Ota::CheckVersion() {
auto& board = Board::GetInstance();
auto app_desc = esp_app_get_description();
@@ -46,17 +78,7 @@ bool Ota::CheckVersion() {
return false;
}
auto http = board.CreateHttp();
for (const auto& header : headers_) {
http->SetHeader(header.first, header.second);
}
http->SetHeader("Ota-Version", "2");
http->SetHeader("Device-Id", SystemInfo::GetMacAddress().c_str());
http->SetHeader("Client-Id", board.GetUuid());
http->SetHeader("User-Agent", std::string(BOARD_NAME "/") + app_desc->version);
http->SetHeader("Accept-Language", Lang::CODE);
http->SetHeader("Content-Type", "application/json");
auto http = SetupHttp();
std::string data = board.GetJson();
std::string method = data.length() > 0 ? "POST" : "GET";
@@ -81,6 +103,7 @@ bool Ota::CheckVersion() {
}
has_activation_code_ = false;
has_activation_challenge_ = false;
cJSON *activation = cJSON_GetObjectItem(root, "activation");
if (activation != NULL) {
cJSON* message = cJSON_GetObjectItem(activation, "message");
@@ -90,8 +113,17 @@ bool Ota::CheckVersion() {
cJSON* code = cJSON_GetObjectItem(activation, "code");
if (code != NULL) {
activation_code_ = code->valuestring;
has_activation_code_ = true;
}
cJSON* challenge = cJSON_GetObjectItem(activation, "challenge");
if (challenge != NULL) {
activation_challenge_ = challenge->valuestring;
has_activation_challenge_ = true;
}
cJSON* timeout_ms = cJSON_GetObjectItem(activation, "timeout_ms");
if (timeout_ms != NULL) {
activation_timeout_ms_ = timeout_ms->valueint;
}
has_activation_code_ = true;
}
has_mqtt_config_ = false;
@@ -327,3 +359,76 @@ bool Ota::IsNewVersionAvailable(const std::string& currentVersion, const std::st
return newer.size() > current.size();
}
std::string Ota::GetActivationPayload() {
if (!has_serial_number_) {
ESP_LOGI(TAG, "No serial number found");
return "{}";
}
uint8_t hmac_result[32]; // SHA-256 输出为32字节
// 使用Key0计算HMAC
esp_err_t ret = esp_hmac_calculate(HMAC_KEY0, (uint8_t*)activation_challenge_.data(), activation_challenge_.size(), hmac_result);
if (ret != ESP_OK) {
ESP_LOGE(TAG, "HMAC calculation failed: %s", esp_err_to_name(ret));
return "{}";
}
std::string hmac_hex;
for (size_t i = 0; i < sizeof(hmac_result); i++) {
char buffer[3];
sprintf(buffer, "%02x", hmac_result[i]);
hmac_hex += buffer;
}
cJSON *payload = cJSON_CreateObject();
cJSON_AddStringToObject(payload, "algorithm", "hmac-sha256");
cJSON_AddStringToObject(payload, "serial_number", serial_number_.c_str());
cJSON_AddStringToObject(payload, "challenge", activation_challenge_.c_str());
cJSON_AddStringToObject(payload, "hmac", hmac_hex.c_str());
std::string json = cJSON_Print(payload);
cJSON_Delete(payload);
ESP_LOGI(TAG, "Activation payload: %s", json.c_str());
return json;
}
esp_err_t Ota::Activate() {
if (!has_activation_challenge_) {
ESP_LOGW(TAG, "No activation challenge found");
return ESP_FAIL;
}
std::string url = check_version_url_;
if (url.back() != '/') {
url += "/activate";
} else {
url += "activate";
}
auto http = SetupHttp();
std::string data = GetActivationPayload();
if (!http->Open("POST", url, data)) {
ESP_LOGE(TAG, "Failed to open HTTP connection");
delete http;
return ESP_FAIL;
}
auto status_code = http->GetStatusCode();
data = http->GetBody();
http->Close();
delete http;
if (status_code == 202) {
return ESP_ERR_TIMEOUT;
}
if (status_code != 200) {
ESP_LOGE(TAG, "Failed to activate, code: %d, body: %s", status_code, data.c_str());
return ESP_FAIL;
}
ESP_LOGI(TAG, "Activation successful");
return ESP_OK;
}