forked from xiaozhi/xiaozhi-esp32
Add activation version 2
This commit is contained in:
131
main/ota.cc
131
main/ota.cc
@@ -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;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user