v1.2.2
This commit is contained in:
@@ -22,6 +22,9 @@ endif()
|
|||||||
add_library(archery_netcore MODULE
|
add_library(archery_netcore MODULE
|
||||||
archery_netcore.cpp
|
archery_netcore.cpp
|
||||||
native_logger.cpp
|
native_logger.cpp
|
||||||
|
utils.cpp
|
||||||
|
decrypt_ota_file.cpp
|
||||||
|
msg_handler.cpp
|
||||||
)
|
)
|
||||||
|
|
||||||
target_include_directories(archery_netcore PRIVATE
|
target_include_directories(archery_netcore PRIVATE
|
||||||
|
|||||||
@@ -7,9 +7,11 @@
|
|||||||
#include <string>
|
#include <string>
|
||||||
#include <fstream>
|
#include <fstream>
|
||||||
#include <array>
|
#include <array>
|
||||||
#include <algorithm>
|
|
||||||
#include <openssl/evp.h>
|
#include "msg_handler.hpp"
|
||||||
#include "native_logger.hpp"
|
#include "native_logger.hpp"
|
||||||
|
#include "decrypt_ota_file.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
namespace py = pybind11;
|
namespace py = pybind11;
|
||||||
using json = nlohmann::json;
|
using json = nlohmann::json;
|
||||||
@@ -18,29 +20,9 @@ namespace {
|
|||||||
// 配置项
|
// 配置项
|
||||||
const std::string _cfg_server_ip = "www.shelingxingqiu.com";
|
const std::string _cfg_server_ip = "www.shelingxingqiu.com";
|
||||||
const int _cfg_server_port = 50005;
|
const int _cfg_server_port = 50005;
|
||||||
|
const std::string _cfg_device_id_file = "/device_key";
|
||||||
|
|
||||||
// OTA AEAD format: MAGIC(7) | nonce(12) | ciphertext(N) | tag(16)
|
|
||||||
constexpr const char* kOtaMagic = "AROTAE1";
|
|
||||||
constexpr size_t kOtaMagicLen = 7;
|
|
||||||
constexpr size_t kGcmNonceLen = 12;
|
|
||||||
constexpr size_t kGcmTagLen = 16;
|
|
||||||
|
|
||||||
// 固定 32-byte AES-256-GCM key(提高被直接查看的成本;不是绝对安全)
|
|
||||||
// 注意:需要与打包端传入的 --aead-key-hex 保持一致。
|
|
||||||
static std::array<uint8_t, 32> ota_key_bytes() {
|
|
||||||
// 简单拆分混淆:key = a XOR b
|
|
||||||
static const std::array<uint8_t, 32> a = {
|
|
||||||
0x92,0x99,0x4d,0x06,0x6f,0xb6,0xa6,0x3d,0x85,0x08,0xbe,0x73,0x5e,0x73,0x4d,0x8a,
|
|
||||||
0x53,0x88,0xe6,0x99,0xfc,0x10,0x29,0xb9,0x16,0x9b,0xe7,0x0c,0x65,0x21,0x1c,0xce
|
|
||||||
};
|
|
||||||
static const std::array<uint8_t, 32> b = {
|
|
||||||
0xcf,0x60,0xa2,0xc2,0x32,0x7a,0x61,0xb0,0x4c,0x8e,0x8a,0x62,0x31,0xc7,0x82,0xff,
|
|
||||||
0xec,0xac,0xa1,0x04,0x2a,0x4d,0xaa,0xf2,0xb0,0x5b,0x39,0x2b,0xf4,0xb3,0xad,0xad
|
|
||||||
};
|
|
||||||
std::array<uint8_t, 32> k{};
|
|
||||||
for (size_t i = 0; i < k.size(); i++) k[i] = static_cast<uint8_t>(a[i] ^ b[i]);
|
|
||||||
return k;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 定义获取配置的函数
|
// 定义获取配置的函数
|
||||||
@@ -52,286 +34,8 @@ py::dict get_config() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 辅助函数:将 py::dict 转为 nlohmann::json
|
|
||||||
json py_dict_to_json(py::dict d) {
|
|
||||||
json j;
|
|
||||||
for (auto item : d) {
|
|
||||||
std::string key = py::str(item.first);
|
|
||||||
py::object val = py::reinterpret_borrow<py::object>(item.second);
|
|
||||||
|
|
||||||
if (py::isinstance<py::dict>(val)) {
|
|
||||||
j[key] = py_dict_to_json(py::cast<py::dict>(val));
|
|
||||||
} else if (py::isinstance<py::list>(val)) {
|
|
||||||
py::list py_list = py::cast<py::list>(val);
|
|
||||||
json arr = json::array();
|
|
||||||
for (auto elem : py_list) {
|
|
||||||
py::object elem_obj = py::reinterpret_borrow<py::object>(elem);
|
|
||||||
if (py::isinstance<py::dict>(elem_obj)) {
|
|
||||||
arr.push_back(py_dict_to_json(py::cast<py::dict>(elem_obj)));
|
|
||||||
} else if (py::isinstance<py::int_>(elem_obj)) {
|
|
||||||
arr.push_back(py::cast<int64_t>(elem_obj));
|
|
||||||
} else if (py::isinstance<py::float_>(elem_obj)) {
|
|
||||||
arr.push_back(py::cast<double>(elem_obj));
|
|
||||||
} else {
|
|
||||||
arr.push_back(py::str(elem_obj));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
j[key] = arr;
|
|
||||||
} else if (py::isinstance<py::int_>(val)) {
|
|
||||||
j[key] = py::cast<int64_t>(val);
|
|
||||||
} else if (py::isinstance<py::float_>(val)) {
|
|
||||||
j[key] = py::cast<double>(val);
|
|
||||||
} else if (py::isinstance<py::bool_>(val)) {
|
|
||||||
j[key] = py::cast<bool>(val);
|
|
||||||
} else if (val.is_none()) {
|
|
||||||
j[key] = nullptr;
|
|
||||||
} else {
|
|
||||||
j[key] = py::str(val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return j;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 辅助函数:将 nlohmann::json 转为 py::dict
|
|
||||||
py::dict json_to_py_dict(const json& j) {
|
|
||||||
py::dict d;
|
|
||||||
if (j.is_object()) {
|
|
||||||
for (auto& item : j.items()) {
|
|
||||||
std::string key = item.key();
|
|
||||||
json val = item.value();
|
|
||||||
|
|
||||||
if (val.is_object()) {
|
|
||||||
d[py::str(key)] = json_to_py_dict(val);
|
|
||||||
} else if (val.is_array()) {
|
|
||||||
py::list py_list;
|
|
||||||
for (auto& elem : val) {
|
|
||||||
if (elem.is_object()) {
|
|
||||||
py_list.append(json_to_py_dict(elem));
|
|
||||||
} else if (elem.is_number_integer()) {
|
|
||||||
py_list.append(py::int_(elem.get<int64_t>()));
|
|
||||||
} else if (elem.is_number_float()) {
|
|
||||||
py_list.append(py::float_(elem.get<double>()));
|
|
||||||
} else if (elem.is_boolean()) {
|
|
||||||
py_list.append(py::bool_(elem.get<bool>()));
|
|
||||||
} else if (elem.is_null()) {
|
|
||||||
py_list.append(py::none());
|
|
||||||
} else {
|
|
||||||
py_list.append(py::str(elem.get<std::string>()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
d[py::str(key)] = py_list;
|
|
||||||
} else if (val.is_number_integer()) {
|
|
||||||
d[py::str(key)] = py::int_(val.get<int64_t>());
|
|
||||||
} else if (val.is_number_float()) {
|
|
||||||
d[py::str(key)] = py::float_(val.get<double>());
|
|
||||||
} else if (val.is_boolean()) {
|
|
||||||
d[py::str(key)] = py::bool_(val.get<bool>());
|
|
||||||
} else if (val.is_null()) {
|
|
||||||
d[py::str(key)] = py::none();
|
|
||||||
} else {
|
|
||||||
d[py::str(key)] = py::str(val.get<std::string>());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return d;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool read_file_all(const std::string& path, std::vector<uint8_t>& out) {
|
|
||||||
std::ifstream ifs(path, std::ios::binary);
|
|
||||||
if (!ifs) return false;
|
|
||||||
ifs.seekg(0, std::ios::end);
|
|
||||||
std::streampos size = ifs.tellg();
|
|
||||||
if (size <= 0) return false;
|
|
||||||
ifs.seekg(0, std::ios::beg);
|
|
||||||
out.resize(static_cast<size_t>(size));
|
|
||||||
if (!ifs.read(reinterpret_cast<char*>(out.data()), size)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool write_file_all(const std::string& path, const uint8_t* data, size_t len) {
|
|
||||||
std::ofstream ofs(path, std::ios::binary | std::ios::trunc);
|
|
||||||
if (!ofs) return false;
|
|
||||||
ofs.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(len));
|
|
||||||
return static_cast<bool>(ofs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) {
|
|
||||||
std::vector<uint8_t> in;
|
|
||||||
if (!read_file_all(input_path, in)) {
|
|
||||||
netcore::log_error(std::string("decrypt_ota_file: read failed: ") + input_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t min_len = kOtaMagicLen + kGcmNonceLen + kGcmTagLen + 1;
|
|
||||||
if (in.size() < min_len) {
|
|
||||||
netcore::log_error("decrypt_ota_file: too short");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!std::equal(in.begin(), in.begin() + kOtaMagicLen, reinterpret_cast<const uint8_t*>(kOtaMagic))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: bad magic");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* nonce = in.data() + kOtaMagicLen;
|
|
||||||
const uint8_t* ct_and_tag = in.data() + kOtaMagicLen + kGcmNonceLen;
|
|
||||||
const size_t ct_and_tag_len = in.size() - (kOtaMagicLen + kGcmNonceLen);
|
|
||||||
if (ct_and_tag_len <= kGcmTagLen) {
|
|
||||||
netcore::log_error("decrypt_ota_file: no ciphertext");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t ciphertext_len = ct_and_tag_len - kGcmTagLen;
|
|
||||||
const uint8_t* ciphertext = ct_and_tag;
|
|
||||||
const uint8_t* tag = ct_and_tag + ciphertext_len;
|
|
||||||
|
|
||||||
std::vector<uint8_t> plain(ciphertext_len);
|
|
||||||
int out_len1 = 0;
|
|
||||||
int out_len2 = 0;
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
|
||||||
if (!ctx) {
|
|
||||||
netcore::log_error("decrypt_ota_file: EVP_CIPHER_CTX_new failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
auto key = ota_key_bytes();
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: DecryptInit failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(kGcmNonceLen), nullptr)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set ivlen failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptInit_ex(ctx, nullptr, nullptr, key.data(), nonce)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set key/iv failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptUpdate(ctx, plain.data(), &out_len1, ciphertext, static_cast<int>(ciphertext_len))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: update failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, static_cast<int>(kGcmTagLen), const_cast<uint8_t*>(tag))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set tag failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptFinal_ex(ctx, plain.data() + out_len1, &out_len2)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: final failed (auth tag mismatch?)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const size_t plain_len = static_cast<size_t>(out_len1 + out_len2);
|
|
||||||
if (!write_file_all(output_zip_path, plain.data(), plain_len)) {
|
|
||||||
netcore::log_error(std::string("decrypt_ota_file: write failed: ") + output_zip_path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ok = true;
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX_free(ctx);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 打包 TCP 数据包
|
|
||||||
py::bytes make_packet(int msg_type, py::dict body_dict) {
|
|
||||||
netcore::log_debug(std::string("make_packet msg_type=") + std::to_string(msg_type));
|
|
||||||
// 1) 将 py::dict 转为 JSON 字符串
|
|
||||||
json j = py_dict_to_json(body_dict);
|
|
||||||
std::string body_str = j.dump();
|
|
||||||
|
|
||||||
// 2) 计算 body_len 和 checksum
|
|
||||||
uint32_t body_len = body_str.size();
|
|
||||||
uint32_t checksum = body_len + msg_type;
|
|
||||||
|
|
||||||
// 3) 打包头部(大端序)
|
|
||||||
std::vector<uint8_t> packet;
|
|
||||||
packet.reserve(12 + body_len);
|
|
||||||
|
|
||||||
// body_len (big-endian, 4 bytes)
|
|
||||||
packet.push_back((body_len >> 24) & 0xFF);
|
|
||||||
packet.push_back((body_len >> 16) & 0xFF);
|
|
||||||
packet.push_back((body_len >> 8) & 0xFF);
|
|
||||||
packet.push_back(body_len & 0xFF);
|
|
||||||
|
|
||||||
// msg_type (big-endian, 4 bytes)
|
|
||||||
packet.push_back((msg_type >> 24) & 0xFF);
|
|
||||||
packet.push_back((msg_type >> 16) & 0xFF);
|
|
||||||
packet.push_back((msg_type >> 8) & 0xFF);
|
|
||||||
packet.push_back(msg_type & 0xFF);
|
|
||||||
|
|
||||||
// checksum (big-endian, 4 bytes)
|
|
||||||
packet.push_back((checksum >> 24) & 0xFF);
|
|
||||||
packet.push_back((checksum >> 16) & 0xFF);
|
|
||||||
packet.push_back((checksum >> 8) & 0xFF);
|
|
||||||
packet.push_back(checksum & 0xFF);
|
|
||||||
|
|
||||||
// 4) 追加 body
|
|
||||||
packet.insert(packet.end(), body_str.begin(), body_str.end());
|
|
||||||
|
|
||||||
netcore::log_debug(std::string("make_packet done bytes=") + std::to_string(packet.size()));
|
|
||||||
return py::bytes(reinterpret_cast<const char*>(packet.data()), packet.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 解析 TCP 数据包
|
|
||||||
py::tuple parse_packet(py::bytes data) {
|
|
||||||
// 1) 转换为 bytes view
|
|
||||||
py::buffer_info buf = py::buffer(data).request();
|
|
||||||
if (buf.size < 12) {
|
|
||||||
netcore::log_error(std::string("parse_packet too_short len=") + std::to_string(buf.size));
|
|
||||||
return py::make_tuple(py::none(), py::none());
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* ptr = static_cast<const uint8_t*>(buf.ptr);
|
|
||||||
|
|
||||||
// 2) 解析头部(大端序)
|
|
||||||
uint32_t body_len = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
|
|
||||||
uint32_t msg_type = (ptr[4] << 24) | (ptr[5] << 16) | (ptr[6] << 8) | ptr[7];
|
|
||||||
uint32_t checksum = (ptr[8] << 24) | (ptr[9] << 16) | (ptr[10] << 8) | ptr[11];
|
|
||||||
|
|
||||||
// 3) 校验 checksum(可选,你现有代码不强制校验)
|
|
||||||
// if (checksum != (body_len + msg_type)) {
|
|
||||||
// return py::make_tuple(py::none(), py::none());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// 4) 检查长度
|
|
||||||
uint32_t expected_len = 12 + body_len;
|
|
||||||
if (buf.size < expected_len) {
|
|
||||||
// 半包
|
|
||||||
netcore::log_warn(std::string("parse_packet incomplete got=") + std::to_string(buf.size) +
|
|
||||||
" expected=" + std::to_string(expected_len));
|
|
||||||
return py::make_tuple(py::none(), py::none());
|
|
||||||
}
|
|
||||||
|
|
||||||
// 5) 防御性检查:如果 data 比预期长,说明可能有粘包
|
|
||||||
// (只解析第一个包,忽略多余数据)
|
|
||||||
if (buf.size > expected_len) {
|
|
||||||
netcore::log_warn(std::string("parse_packet concat got=") + std::to_string(buf.size) +
|
|
||||||
" expected=" + std::to_string(expected_len) +
|
|
||||||
" body_len=" + std::to_string(body_len) +
|
|
||||||
" msg_type=" + std::to_string(msg_type));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6) 提取 body 并解析 JSON
|
|
||||||
std::string body_str(reinterpret_cast<const char*>(ptr + 12), body_len);
|
|
||||||
|
|
||||||
try {
|
|
||||||
json j = json::parse(body_str);
|
|
||||||
py::dict body_dict = json_to_py_dict(j);
|
|
||||||
return py::make_tuple(py::int_(msg_type), body_dict);
|
|
||||||
} catch (const json::parse_error& e) {
|
|
||||||
// JSON 解析失败,返回 raw(兼容你现有的逻辑)
|
|
||||||
netcore::log_error(std::string("parse_packet json_parse_error: ") + e.what());
|
|
||||||
py::dict raw_dict;
|
|
||||||
raw_dict["raw"] = body_str;
|
|
||||||
return py::make_tuple(py::int_(msg_type), raw_dict);
|
|
||||||
} catch (const std::exception& e) {
|
|
||||||
netcore::log_error(std::string("parse_packet json_parse_error: ") + e.what());
|
|
||||||
py::dict raw_dict;
|
|
||||||
raw_dict["raw"] = body_str;
|
|
||||||
return py::make_tuple(py::int_(msg_type), raw_dict);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
PYBIND11_MODULE(archery_netcore, m) {
|
PYBIND11_MODULE(archery_netcore, m) {
|
||||||
m.doc() = "Archery net core (native, pybind11).";
|
m.doc() = "Archery net core (native, pybind11).";
|
||||||
@@ -348,11 +52,11 @@ PYBIND11_MODULE(archery_netcore, m) {
|
|||||||
netcore::log_info(std::string("log_test: ") + msg);
|
netcore::log_info(std::string("log_test: ") + msg);
|
||||||
}, py::arg("msg"));
|
}, py::arg("msg"));
|
||||||
|
|
||||||
m.def("make_packet", &make_packet,
|
m.def("make_packet", &netcore::make_packet,
|
||||||
"Pack TCP packet: header (len+type+checksum) + JSON body",
|
"Pack TCP packet: header (len+type+checksum) + JSON body",
|
||||||
py::arg("msg_type"), py::arg("body_dict"));
|
py::arg("msg_type"), py::arg("body_dict"));
|
||||||
|
|
||||||
m.def("parse_packet", &parse_packet,
|
m.def("parse_packet", &netcore::parse_packet,
|
||||||
"Parse TCP packet, return (msg_type, body_dict)");
|
"Parse TCP packet, return (msg_type, body_dict)");
|
||||||
|
|
||||||
m.def("get_config", &get_config, "Get system configuration");
|
m.def("get_config", &get_config, "Get system configuration");
|
||||||
@@ -361,7 +65,7 @@ PYBIND11_MODULE(archery_netcore, m) {
|
|||||||
"decrypt_ota_file",
|
"decrypt_ota_file",
|
||||||
[](const std::string& input_path, const std::string& output_zip_path) {
|
[](const std::string& input_path, const std::string& output_zip_path) {
|
||||||
netcore::log_info(std::string("decrypt_ota_file in=") + input_path + " out=" + output_zip_path);
|
netcore::log_info(std::string("decrypt_ota_file in=") + input_path + " out=" + output_zip_path);
|
||||||
return decrypt_ota_file_impl(input_path, output_zip_path);
|
return netcore::decrypt_ota_file_impl(input_path, output_zip_path);
|
||||||
},
|
},
|
||||||
py::arg("input_path"),
|
py::arg("input_path"),
|
||||||
py::arg("output_zip_path"),
|
py::arg("output_zip_path"),
|
||||||
|
|||||||
135
cpp_ext/decrypt_ota_file.cpp
Normal file
135
cpp_ext/decrypt_ota_file.cpp
Normal file
@@ -0,0 +1,135 @@
|
|||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h> // 支持 std::vector, std::map 等
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include <array>
|
||||||
|
#include <openssl/evp.h>
|
||||||
|
#include <algorithm>
|
||||||
|
#include "native_logger.hpp"
|
||||||
|
|
||||||
|
namespace netcore{
|
||||||
|
|
||||||
|
// OTA AEAD format: MAGIC(7) | nonce(12) | ciphertext(N) | tag(16)
|
||||||
|
constexpr const char* kOtaMagic = "AROTAE1";
|
||||||
|
constexpr size_t kOtaMagicLen = 7;
|
||||||
|
constexpr size_t kGcmNonceLen = 12;
|
||||||
|
constexpr size_t kGcmTagLen = 16;
|
||||||
|
|
||||||
|
// 固定 32-byte AES-256-GCM key(提高被直接查看的成本;不是绝对安全)
|
||||||
|
// 注意:需要与打包端传入的 --aead-key-hex 保持一致。
|
||||||
|
static std::array<uint8_t, 32> ota_key_bytes() {
|
||||||
|
// 简单拆分混淆:key = a XOR b
|
||||||
|
static const std::array<uint8_t, 32> a = {
|
||||||
|
0x92,0x99,0x4d,0x06,0x6f,0xb6,0xa6,0x3d,0x85,0x08,0xbe,0x73,0x5e,0x73,0x4d,0x8a,
|
||||||
|
0x53,0x88,0xe6,0x99,0xfc,0x10,0x29,0xb9,0x16,0x9b,0xe7,0x0c,0x65,0x21,0x1c,0xce
|
||||||
|
};
|
||||||
|
static const std::array<uint8_t, 32> b = {
|
||||||
|
0xcf,0x60,0xa2,0xc2,0x32,0x7a,0x61,0xb0,0x4c,0x8e,0x8a,0x62,0x31,0xc7,0x82,0xff,
|
||||||
|
0xec,0xac,0xa1,0x04,0x2a,0x4d,0xaa,0xf2,0xb0,0x5b,0x39,0x2b,0xf4,0xb3,0xad,0xad
|
||||||
|
};
|
||||||
|
std::array<uint8_t, 32> k{};
|
||||||
|
for (size_t i = 0; i < k.size(); i++) k[i] = static_cast<uint8_t>(a[i] ^ b[i]);
|
||||||
|
return k;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool read_file_all(const std::string& path, std::vector<uint8_t>& out) {
|
||||||
|
std::ifstream ifs(path, std::ios::binary);
|
||||||
|
if (!ifs) return false;
|
||||||
|
ifs.seekg(0, std::ios::end);
|
||||||
|
std::streampos size = ifs.tellg();
|
||||||
|
if (size <= 0) return false;
|
||||||
|
ifs.seekg(0, std::ios::beg);
|
||||||
|
out.resize(static_cast<size_t>(size));
|
||||||
|
if (!ifs.read(reinterpret_cast<char*>(out.data()), size)) return false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool write_file_all(const std::string& path, const uint8_t* data, size_t len) {
|
||||||
|
std::ofstream ofs(path, std::ios::binary | std::ios::trunc);
|
||||||
|
if (!ofs) return false;
|
||||||
|
ofs.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(len));
|
||||||
|
return static_cast<bool>(ofs);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) {
|
||||||
|
std::vector<uint8_t> in;
|
||||||
|
if (!netcore::read_file_all(input_path, in)) {
|
||||||
|
netcore::log_error(std::string("decrypt_ota_file: read failed: ") + input_path);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const size_t min_len = kOtaMagicLen + kGcmNonceLen + kGcmTagLen + 1;
|
||||||
|
if (in.size() < min_len) {
|
||||||
|
netcore::log_error("decrypt_ota_file: too short");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!std::equal(in.begin(), in.begin() + kOtaMagicLen, reinterpret_cast<const uint8_t*>(kOtaMagic))) {
|
||||||
|
netcore::log_error("decrypt_ota_file: bad magic");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* nonce = in.data() + kOtaMagicLen;
|
||||||
|
const uint8_t* ct_and_tag = in.data() + kOtaMagicLen + kGcmNonceLen;
|
||||||
|
const size_t ct_and_tag_len = in.size() - (kOtaMagicLen + kGcmNonceLen);
|
||||||
|
if (ct_and_tag_len <= kGcmTagLen) {
|
||||||
|
netcore::log_error("decrypt_ota_file: no ciphertext");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
const size_t ciphertext_len = ct_and_tag_len - kGcmTagLen;
|
||||||
|
const uint8_t* ciphertext = ct_and_tag;
|
||||||
|
const uint8_t* tag = ct_and_tag + ciphertext_len;
|
||||||
|
|
||||||
|
std::vector<uint8_t> plain(ciphertext_len);
|
||||||
|
int out_len1 = 0;
|
||||||
|
int out_len2 = 0;
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
||||||
|
if (!ctx) {
|
||||||
|
netcore::log_error("decrypt_ota_file: EVP_CIPHER_CTX_new failed");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
auto key = ota_key_bytes();
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr)) {
|
||||||
|
netcore::log_error("decrypt_ota_file: DecryptInit failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(kGcmNonceLen), nullptr)) {
|
||||||
|
netcore::log_error("decrypt_ota_file: set ivlen failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (1 != EVP_DecryptInit_ex(ctx, nullptr, nullptr, key.data(), nonce)) {
|
||||||
|
netcore::log_error("decrypt_ota_file: set key/iv failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (1 != EVP_DecryptUpdate(ctx, plain.data(), &out_len1, ciphertext, static_cast<int>(ciphertext_len))) {
|
||||||
|
netcore::log_error("decrypt_ota_file: update failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, static_cast<int>(kGcmTagLen), const_cast<uint8_t*>(tag))) {
|
||||||
|
netcore::log_error("decrypt_ota_file: set tag failed");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (1 != EVP_DecryptFinal_ex(ctx, plain.data() + out_len1, &out_len2)) {
|
||||||
|
netcore::log_error("decrypt_ota_file: final failed (auth tag mismatch?)");
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
const size_t plain_len = static_cast<size_t>(out_len1 + out_len2);
|
||||||
|
if (!netcore::write_file_all(output_zip_path, plain.data(), plain_len)) {
|
||||||
|
netcore::log_error(std::string("decrypt_ota_file: write failed: ") + output_zip_path);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
ok = true;
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
EVP_CIPHER_CTX_free(ctx);
|
||||||
|
return ok;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,98 +1,7 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <string>
|
||||||
|
|
||||||
namespace netcore{
|
namespace netcore{
|
||||||
static bool read_file_all(const std::string& path, std::vector<uint8_t>& out) {
|
bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path);
|
||||||
std::ifstream ifs(path, std::ios::binary);
|
|
||||||
if (!ifs) return false;
|
|
||||||
ifs.seekg(0, std::ios::end);
|
|
||||||
std::streampos size = ifs.tellg();
|
|
||||||
if (size <= 0) return false;
|
|
||||||
ifs.seekg(0, std::ios::beg);
|
|
||||||
out.resize(static_cast<size_t>(size));
|
|
||||||
if (!ifs.read(reinterpret_cast<char*>(out.data()), size)) return false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool write_file_all(const std::string& path, const uint8_t* data, size_t len) {
|
|
||||||
std::ofstream ofs(path, std::ios::binary | std::ios::trunc);
|
|
||||||
if (!ofs) return false;
|
|
||||||
ofs.write(reinterpret_cast<const char*>(data), static_cast<std::streamsize>(len));
|
|
||||||
return static_cast<bool>(ofs);
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) {
|
|
||||||
std::vector<uint8_t> in;
|
|
||||||
if (!netcore::read_file_all(input_path, in)) {
|
|
||||||
netcore::log_error(std::string("decrypt_ota_file: read failed: ") + input_path);
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const size_t min_len = kOtaMagicLen + kGcmNonceLen + kGcmTagLen + 1;
|
|
||||||
if (in.size() < min_len) {
|
|
||||||
netcore::log_error("decrypt_ota_file: too short");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (!std::equal(in.begin(), in.begin() + kOtaMagicLen, reinterpret_cast<const uint8_t*>(kOtaMagic))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: bad magic");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
const uint8_t* nonce = in.data() + kOtaMagicLen;
|
|
||||||
const uint8_t* ct_and_tag = in.data() + kOtaMagicLen + kGcmNonceLen;
|
|
||||||
const size_t ct_and_tag_len = in.size() - (kOtaMagicLen + kGcmNonceLen);
|
|
||||||
if (ct_and_tag_len <= kGcmTagLen) {
|
|
||||||
netcore::log_error("decrypt_ota_file: no ciphertext");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const size_t ciphertext_len = ct_and_tag_len - kGcmTagLen;
|
|
||||||
const uint8_t* ciphertext = ct_and_tag;
|
|
||||||
const uint8_t* tag = ct_and_tag + ciphertext_len;
|
|
||||||
|
|
||||||
std::vector<uint8_t> plain(ciphertext_len);
|
|
||||||
int out_len1 = 0;
|
|
||||||
int out_len2 = 0;
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX* ctx = EVP_CIPHER_CTX_new();
|
|
||||||
if (!ctx) {
|
|
||||||
netcore::log_error("decrypt_ota_file: EVP_CIPHER_CTX_new failed");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool ok = false;
|
|
||||||
auto key = ota_key_bytes();
|
|
||||||
|
|
||||||
do {
|
|
||||||
if (1 != EVP_DecryptInit_ex(ctx, EVP_aes_256_gcm(), nullptr, nullptr, nullptr)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: DecryptInit failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_IVLEN, static_cast<int>(kGcmNonceLen), nullptr)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set ivlen failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptInit_ex(ctx, nullptr, nullptr, key.data(), nonce)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set key/iv failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptUpdate(ctx, plain.data(), &out_len1, ciphertext, static_cast<int>(ciphertext_len))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: update failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, static_cast<int>(kGcmTagLen), const_cast<uint8_t*>(tag))) {
|
|
||||||
netcore::log_error("decrypt_ota_file: set tag failed");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if (1 != EVP_DecryptFinal_ex(ctx, plain.data() + out_len1, &out_len2)) {
|
|
||||||
netcore::log_error("decrypt_ota_file: final failed (auth tag mismatch?)");
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
const size_t plain_len = static_cast<size_t>(out_len1 + out_len2);
|
|
||||||
if (!netcore::write_file_all(output_zip_path, plain.data(), plain_len)) {
|
|
||||||
netcore::log_error(std::string("decrypt_ota_file: write failed: ") + output_zip_path);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
ok = true;
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
EVP_CIPHER_CTX_free(ctx);
|
|
||||||
return ok;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,113 @@
|
|||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <string>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <vector>
|
||||||
|
#include "native_logger.hpp"
|
||||||
|
#include "msg_handler.hpp"
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
namespace netcore {
|
||||||
|
// 打包 TCP 数据包
|
||||||
|
py::bytes make_packet(int msg_type, py::dict body_dict) {
|
||||||
|
netcore::log_debug(std::string("make_packet msg_type=") + std::to_string(msg_type));
|
||||||
|
// 1) 将 py::dict 转为 JSON 字符串
|
||||||
|
json j = netcore::py_dict_to_json(body_dict);
|
||||||
|
std::string body_str = j.dump();
|
||||||
|
|
||||||
|
// 2) 计算 body_len 和 checksum
|
||||||
|
uint32_t body_len = body_str.size();
|
||||||
|
uint32_t checksum = body_len + msg_type;
|
||||||
|
|
||||||
|
// 3) 打包头部(大端序)
|
||||||
|
std::vector<uint8_t> packet;
|
||||||
|
packet.reserve(12 + body_len);
|
||||||
|
|
||||||
|
// body_len (big-endian, 4 bytes)
|
||||||
|
packet.push_back((body_len >> 24) & 0xFF);
|
||||||
|
packet.push_back((body_len >> 16) & 0xFF);
|
||||||
|
packet.push_back((body_len >> 8) & 0xFF);
|
||||||
|
packet.push_back(body_len & 0xFF);
|
||||||
|
|
||||||
|
// msg_type (big-endian, 4 bytes)
|
||||||
|
packet.push_back((msg_type >> 24) & 0xFF);
|
||||||
|
packet.push_back((msg_type >> 16) & 0xFF);
|
||||||
|
packet.push_back((msg_type >> 8) & 0xFF);
|
||||||
|
packet.push_back(msg_type & 0xFF);
|
||||||
|
|
||||||
|
// checksum (big-endian, 4 bytes)
|
||||||
|
packet.push_back((checksum >> 24) & 0xFF);
|
||||||
|
packet.push_back((checksum >> 16) & 0xFF);
|
||||||
|
packet.push_back((checksum >> 8) & 0xFF);
|
||||||
|
packet.push_back(checksum & 0xFF);
|
||||||
|
|
||||||
|
// 4) 追加 body
|
||||||
|
packet.insert(packet.end(), body_str.begin(), body_str.end());
|
||||||
|
|
||||||
|
netcore::log_debug(std::string("make_packet done bytes=") + std::to_string(packet.size()));
|
||||||
|
return py::bytes(reinterpret_cast<const char*>(packet.data()), packet.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 解析 TCP 数据包
|
||||||
|
py::tuple parse_packet(py::bytes data) {
|
||||||
|
// 1) 转换为 bytes view
|
||||||
|
py::buffer_info buf = py::buffer(data).request();
|
||||||
|
if (buf.size < 12) {
|
||||||
|
netcore::log_error(std::string("parse_packet too_short len=") + std::to_string(buf.size));
|
||||||
|
return py::make_tuple(py::none(), py::none());
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t* ptr = static_cast<const uint8_t*>(buf.ptr);
|
||||||
|
|
||||||
|
// 2) 解析头部(大端序)
|
||||||
|
uint32_t body_len = (ptr[0] << 24) | (ptr[1] << 16) | (ptr[2] << 8) | ptr[3];
|
||||||
|
uint32_t msg_type = (ptr[4] << 24) | (ptr[5] << 16) | (ptr[6] << 8) | ptr[7];
|
||||||
|
uint32_t checksum = (ptr[8] << 24) | (ptr[9] << 16) | (ptr[10] << 8) | ptr[11];
|
||||||
|
|
||||||
|
// 3) 校验 checksum(可选,你现有代码不强制校验)
|
||||||
|
// if (checksum != (body_len + msg_type)) {
|
||||||
|
// return py::make_tuple(py::none(), py::none());
|
||||||
|
// }
|
||||||
|
|
||||||
|
// 4) 检查长度
|
||||||
|
uint32_t expected_len = 12 + body_len;
|
||||||
|
if (buf.size < expected_len) {
|
||||||
|
// 半包
|
||||||
|
netcore::log_warn(std::string("parse_packet incomplete got=") + std::to_string(buf.size) +
|
||||||
|
" expected=" + std::to_string(expected_len));
|
||||||
|
return py::make_tuple(py::none(), py::none());
|
||||||
|
}
|
||||||
|
|
||||||
|
// 5) 防御性检查:如果 data 比预期长,说明可能有粘包
|
||||||
|
// (只解析第一个包,忽略多余数据)
|
||||||
|
if (buf.size > expected_len) {
|
||||||
|
netcore::log_warn(std::string("parse_packet concat got=") + std::to_string(buf.size) +
|
||||||
|
" expected=" + std::to_string(expected_len) +
|
||||||
|
" body_len=" + std::to_string(body_len) +
|
||||||
|
" msg_type=" + std::to_string(msg_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 6) 提取 body 并解析 JSON
|
||||||
|
std::string body_str(reinterpret_cast<const char*>(ptr + 12), body_len);
|
||||||
|
|
||||||
|
try {
|
||||||
|
json j = json::parse(body_str);
|
||||||
|
py::dict body_dict = netcore::json_to_py_dict(j);
|
||||||
|
return py::make_tuple(py::int_(msg_type), body_dict);
|
||||||
|
} catch (const json::parse_error& e) {
|
||||||
|
// JSON 解析失败,返回 raw(兼容你现有的逻辑)
|
||||||
|
netcore::log_error(std::string("parse_packet json_parse_error: ") + e.what());
|
||||||
|
py::dict raw_dict;
|
||||||
|
raw_dict["raw"] = body_str;
|
||||||
|
return py::make_tuple(py::int_(msg_type), raw_dict);
|
||||||
|
} catch (const std::exception& e) {
|
||||||
|
netcore::log_error(std::string("parse_packet json_parse_error: ") + e.what());
|
||||||
|
py::dict raw_dict;
|
||||||
|
raw_dict["raw"] = body_str;
|
||||||
|
return py::make_tuple(py::int_(msg_type), raw_dict);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
14
cpp_ext/msg_handler.hpp
Normal file
14
cpp_ext/msg_handler.hpp
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h> // 支持 std::vector, std::map 等
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
|
||||||
|
namespace netcore {
|
||||||
|
|
||||||
|
// 打包 TCP 数据包
|
||||||
|
py::bytes make_packet(int msg_type, py::dict body_dict);
|
||||||
|
// 解包 TCP 数据包
|
||||||
|
py::tuple parse_packet(py::bytes data);
|
||||||
|
}
|
||||||
@@ -0,0 +1,95 @@
|
|||||||
|
#include <fstream>
|
||||||
|
#include <cstring>
|
||||||
|
#include <cstdint>
|
||||||
|
#include <string>
|
||||||
|
#include <fstream>
|
||||||
|
#include "utils.hpp"
|
||||||
|
|
||||||
|
namespace netcore {
|
||||||
|
// 辅助函数:将 py::dict 转为 nlohmann::json
|
||||||
|
json py_dict_to_json(py::dict d) {
|
||||||
|
json j;
|
||||||
|
for (auto item : d) {
|
||||||
|
std::string key = py::str(item.first);
|
||||||
|
py::object val = py::reinterpret_borrow<py::object>(item.second);
|
||||||
|
|
||||||
|
if (py::isinstance<py::dict>(val)) {
|
||||||
|
j[key] = py_dict_to_json(py::cast<py::dict>(val));
|
||||||
|
} else if (py::isinstance<py::list>(val)) {
|
||||||
|
py::list py_list = py::cast<py::list>(val);
|
||||||
|
json arr = json::array();
|
||||||
|
for (auto elem : py_list) {
|
||||||
|
py::object elem_obj = py::reinterpret_borrow<py::object>(elem);
|
||||||
|
if (py::isinstance<py::dict>(elem_obj)) {
|
||||||
|
arr.push_back(py_dict_to_json(py::cast<py::dict>(elem_obj)));
|
||||||
|
} else if (py::isinstance<py::int_>(elem_obj)) {
|
||||||
|
arr.push_back(py::cast<int64_t>(elem_obj));
|
||||||
|
} else if (py::isinstance<py::float_>(elem_obj)) {
|
||||||
|
arr.push_back(py::cast<double>(elem_obj));
|
||||||
|
} else {
|
||||||
|
arr.push_back(py::str(elem_obj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
j[key] = arr;
|
||||||
|
} else if (py::isinstance<py::int_>(val)) {
|
||||||
|
j[key] = py::cast<int64_t>(val);
|
||||||
|
} else if (py::isinstance<py::float_>(val)) {
|
||||||
|
j[key] = py::cast<double>(val);
|
||||||
|
} else if (py::isinstance<py::bool_>(val)) {
|
||||||
|
j[key] = py::cast<bool>(val);
|
||||||
|
} else if (val.is_none()) {
|
||||||
|
j[key] = nullptr;
|
||||||
|
} else {
|
||||||
|
j[key] = py::str(val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return j;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 辅助函数:将 nlohmann::json 转为 py::dict
|
||||||
|
py::dict json_to_py_dict(const json& j) {
|
||||||
|
py::dict d;
|
||||||
|
if (j.is_object()) {
|
||||||
|
for (auto& item : j.items()) {
|
||||||
|
std::string key = item.key();
|
||||||
|
json val = item.value();
|
||||||
|
|
||||||
|
if (val.is_object()) {
|
||||||
|
d[py::str(key)] = json_to_py_dict(val);
|
||||||
|
} else if (val.is_array()) {
|
||||||
|
py::list py_list;
|
||||||
|
for (auto& elem : val) {
|
||||||
|
if (elem.is_object()) {
|
||||||
|
py_list.append(json_to_py_dict(elem));
|
||||||
|
} else if (elem.is_number_integer()) {
|
||||||
|
py_list.append(py::int_(elem.get<int64_t>()));
|
||||||
|
} else if (elem.is_number_float()) {
|
||||||
|
py_list.append(py::float_(elem.get<double>()));
|
||||||
|
} else if (elem.is_boolean()) {
|
||||||
|
py_list.append(py::bool_(elem.get<bool>()));
|
||||||
|
} else if (elem.is_null()) {
|
||||||
|
py_list.append(py::none());
|
||||||
|
} else {
|
||||||
|
py_list.append(py::str(elem.get<std::string>()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
d[py::str(key)] = py_list;
|
||||||
|
} else if (val.is_number_integer()) {
|
||||||
|
d[py::str(key)] = py::int_(val.get<int64_t>());
|
||||||
|
} else if (val.is_number_float()) {
|
||||||
|
d[py::str(key)] = py::float_(val.get<double>());
|
||||||
|
} else if (val.is_boolean()) {
|
||||||
|
d[py::str(key)] = py::bool_(val.get<bool>());
|
||||||
|
} else if (val.is_null()) {
|
||||||
|
d[py::str(key)] = py::none();
|
||||||
|
} else {
|
||||||
|
d[py::str(key)] = py::str(val.get<std::string>());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return d;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <pybind11/pybind11.h>
|
||||||
|
#include <pybind11/stl.h> // 支持 std::vector, std::map 等
|
||||||
|
#include <nlohmann/json.hpp>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
namespace py = pybind11;
|
||||||
|
using json = nlohmann::json;
|
||||||
|
|
||||||
|
namespace netcore {
|
||||||
|
|
||||||
|
json py_dict_to_json(py::dict d);
|
||||||
|
py::dict json_to_py_dict(const json& j);
|
||||||
|
}
|
||||||
52
keygen.py
Normal file
52
keygen.py
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
import os
|
||||||
|
|
||||||
|
|
||||||
|
def generate_key_pair():
|
||||||
|
"""
|
||||||
|
生成一对新的密钥a和b,使得a XOR b等于原始key
|
||||||
|
:return: (a, b, key) 元组,每个元素都是32字节的字节数组
|
||||||
|
"""
|
||||||
|
# 原始key值
|
||||||
|
key = bytes([
|
||||||
|
0x5d, 0xf9, 0xef, 0xc4, 0x5d, 0xcc, 0xc7, 0x8d, 0xc9, 0x86, 0x34, 0x11, 0x6f, 0xb4, 0xcf, 0x75,
|
||||||
|
0xbf, 0x24, 0x47, 0x9d, 0xd6, 0x5d, 0x83, 0x4b, 0xa6, 0xc0, 0xde, 0x27, 0x91, 0x92, 0xb1, 0x63
|
||||||
|
])
|
||||||
|
|
||||||
|
# 随机生成a
|
||||||
|
a = os.urandom(32)
|
||||||
|
|
||||||
|
# 计算b = key XOR a
|
||||||
|
b = bytes([key[i] ^ a[i] for i in range(32)])
|
||||||
|
|
||||||
|
return a, b, key
|
||||||
|
|
||||||
|
|
||||||
|
def format_hex_array(data):
|
||||||
|
"""
|
||||||
|
将字节数组格式化为C++风格的十六进制数组
|
||||||
|
:param data: 字节数组
|
||||||
|
:return: 格式化后的字符串
|
||||||
|
"""
|
||||||
|
return "{" + ",".join([f"0x{b:02x}" for b in data]) + "}"
|
||||||
|
|
||||||
|
|
||||||
|
def generate_new_key_pair():
|
||||||
|
"""
|
||||||
|
生成新的密钥对并打印出来
|
||||||
|
"""
|
||||||
|
a, b, key = generate_key_pair()
|
||||||
|
|
||||||
|
print("原始key:")
|
||||||
|
print(format_hex_array(key))
|
||||||
|
print("\n新的密钥对:")
|
||||||
|
print("a =", format_hex_array(a))
|
||||||
|
print("b =", format_hex_array(b))
|
||||||
|
|
||||||
|
# 验证a XOR b是否等于key
|
||||||
|
verify_key = bytes([a[i] ^ b[i] for i in range(32)])
|
||||||
|
assert verify_key == key, "验证失败:a XOR b 不等于 key"
|
||||||
|
print("\n验证成功:a XOR b 等于 key")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
generate_new_key_pair()
|
||||||
@@ -1193,6 +1193,8 @@ class OTAManager:
|
|||||||
from network import network_manager, safe_enqueue
|
from network import network_manager, safe_enqueue
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
# 与 4G 一致:OTA 期间暂停主循环 / 心跳等
|
||||||
|
self._begin_ota()
|
||||||
ip, error = network_manager.connect_wifi(ssid, password)
|
ip, error = network_manager.connect_wifi(ssid, password)
|
||||||
if error:
|
if error:
|
||||||
safe_enqueue({"result": "wifi_failed", "error": error}, 2)
|
safe_enqueue({"result": "wifi_failed", "error": error}, 2)
|
||||||
@@ -1231,6 +1233,7 @@ class OTAManager:
|
|||||||
self.logger.error(err_msg)
|
self.logger.error(err_msg)
|
||||||
finally:
|
finally:
|
||||||
self._stop_update_thread()
|
self._stop_update_thread()
|
||||||
|
self._end_ota() # 与 4G 一致
|
||||||
print("[UPDATE] 更新线程执行完毕,即将退出。")
|
print("[UPDATE] 更新线程执行完毕,即将退出。")
|
||||||
|
|
||||||
def restore_from_backup(self, backup_dir_path=None):
|
def restore_from_backup(self, backup_dir_path=None):
|
||||||
|
|||||||
Reference in New Issue
Block a user