diff --git a/cpp_ext/CMakeLists.txt b/cpp_ext/CMakeLists.txt index 590db1d..ebe9d5a 100644 --- a/cpp_ext/CMakeLists.txt +++ b/cpp_ext/CMakeLists.txt @@ -22,6 +22,9 @@ endif() add_library(archery_netcore MODULE archery_netcore.cpp native_logger.cpp + utils.cpp + decrypt_ota_file.cpp + msg_handler.cpp ) target_include_directories(archery_netcore PRIVATE diff --git a/cpp_ext/archery_netcore.cpp b/cpp_ext/archery_netcore.cpp index 56a43ec..6970753 100644 --- a/cpp_ext/archery_netcore.cpp +++ b/cpp_ext/archery_netcore.cpp @@ -7,9 +7,11 @@ #include #include #include -#include -#include + +#include "msg_handler.hpp" #include "native_logger.hpp" +#include "decrypt_ota_file.hpp" +#include "utils.hpp" namespace py = pybind11; using json = nlohmann::json; @@ -18,29 +20,9 @@ namespace { // 配置项 const std::string _cfg_server_ip = "www.shelingxingqiu.com"; 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 ota_key_bytes() { - // 简单拆分混淆:key = a XOR b - static const std::array 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 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 k{}; - for (size_t i = 0; i < k.size(); i++) k[i] = static_cast(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(item.second); - - if (py::isinstance(val)) { - j[key] = py_dict_to_json(py::cast(val)); - } else if (py::isinstance(val)) { - py::list py_list = py::cast(val); - json arr = json::array(); - for (auto elem : py_list) { - py::object elem_obj = py::reinterpret_borrow(elem); - if (py::isinstance(elem_obj)) { - arr.push_back(py_dict_to_json(py::cast(elem_obj))); - } else if (py::isinstance(elem_obj)) { - arr.push_back(py::cast(elem_obj)); - } else if (py::isinstance(elem_obj)) { - arr.push_back(py::cast(elem_obj)); - } else { - arr.push_back(py::str(elem_obj)); - } - } - j[key] = arr; - } else if (py::isinstance(val)) { - j[key] = py::cast(val); - } else if (py::isinstance(val)) { - j[key] = py::cast(val); - } else if (py::isinstance(val)) { - j[key] = py::cast(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())); - } else if (elem.is_number_float()) { - py_list.append(py::float_(elem.get())); - } else if (elem.is_boolean()) { - py_list.append(py::bool_(elem.get())); - } else if (elem.is_null()) { - py_list.append(py::none()); - } else { - py_list.append(py::str(elem.get())); - } - } - d[py::str(key)] = py_list; - } else if (val.is_number_integer()) { - d[py::str(key)] = py::int_(val.get()); - } else if (val.is_number_float()) { - d[py::str(key)] = py::float_(val.get()); - } else if (val.is_boolean()) { - d[py::str(key)] = py::bool_(val.get()); - } else if (val.is_null()) { - d[py::str(key)] = py::none(); - } else { - d[py::str(key)] = py::str(val.get()); - } - } - } - return d; -} -static bool read_file_all(const std::string& path, std::vector& 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)); - if (!ifs.read(reinterpret_cast(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(data), static_cast(len)); - return static_cast(ofs); -} - -static bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) { - std::vector 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(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 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(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(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(kGcmTagLen), const_cast(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(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 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(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(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(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) { m.doc() = "Archery net core (native, pybind11)."; @@ -348,11 +52,11 @@ PYBIND11_MODULE(archery_netcore, m) { netcore::log_info(std::string("log_test: ") + 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", 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)"); m.def("get_config", &get_config, "Get system configuration"); @@ -361,7 +65,7 @@ PYBIND11_MODULE(archery_netcore, m) { "decrypt_ota_file", [](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); - 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("output_zip_path"), diff --git a/cpp_ext/decrypt_ota_file.cpp b/cpp_ext/decrypt_ota_file.cpp new file mode 100644 index 0000000..02d7d6b --- /dev/null +++ b/cpp_ext/decrypt_ota_file.cpp @@ -0,0 +1,135 @@ +#include +#include // 支持 std::vector, std::map 等 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 ota_key_bytes() { + // 简单拆分混淆:key = a XOR b + static const std::array 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 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 k{}; + for (size_t i = 0; i < k.size(); i++) k[i] = static_cast(a[i] ^ b[i]); + return k; + } + + static bool read_file_all(const std::string& path, std::vector& 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)); + if (!ifs.read(reinterpret_cast(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(data), static_cast(len)); + return static_cast(ofs); + } + + bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) { + std::vector 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(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 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(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(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(kGcmTagLen), const_cast(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(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; + } +} \ No newline at end of file diff --git a/cpp_ext/decrypt_ota_file.hpp b/cpp_ext/decrypt_ota_file.hpp index 18ebd0e..7ea5815 100644 --- a/cpp_ext/decrypt_ota_file.hpp +++ b/cpp_ext/decrypt_ota_file.hpp @@ -1,98 +1,7 @@ +#pragma once + +#include + namespace netcore{ -static bool read_file_all(const std::string& path, std::vector& 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)); - if (!ifs.read(reinterpret_cast(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(data), static_cast(len)); - return static_cast(ofs); -} - -static bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) { - std::vector 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(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 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(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(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(kGcmTagLen), const_cast(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(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; -} + bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path); } \ No newline at end of file diff --git a/cpp_ext/msg_handler.cpp b/cpp_ext/msg_handler.cpp index e69de29..d06d900 100644 --- a/cpp_ext/msg_handler.cpp +++ b/cpp_ext/msg_handler.cpp @@ -0,0 +1,113 @@ +#include +#include +#include +#include +#include +#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 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(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(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(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); + } + } +} \ No newline at end of file diff --git a/cpp_ext/msg_handler.hpp b/cpp_ext/msg_handler.hpp new file mode 100644 index 0000000..fcee18f --- /dev/null +++ b/cpp_ext/msg_handler.hpp @@ -0,0 +1,14 @@ +#pragma once + +#include +#include // 支持 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); +} \ No newline at end of file diff --git a/cpp_ext/utils.cpp b/cpp_ext/utils.cpp index e69de29..57ec6f3 100644 --- a/cpp_ext/utils.cpp +++ b/cpp_ext/utils.cpp @@ -0,0 +1,95 @@ +#include +#include +#include +#include +#include +#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(item.second); + + if (py::isinstance(val)) { + j[key] = py_dict_to_json(py::cast(val)); + } else if (py::isinstance(val)) { + py::list py_list = py::cast(val); + json arr = json::array(); + for (auto elem : py_list) { + py::object elem_obj = py::reinterpret_borrow(elem); + if (py::isinstance(elem_obj)) { + arr.push_back(py_dict_to_json(py::cast(elem_obj))); + } else if (py::isinstance(elem_obj)) { + arr.push_back(py::cast(elem_obj)); + } else if (py::isinstance(elem_obj)) { + arr.push_back(py::cast(elem_obj)); + } else { + arr.push_back(py::str(elem_obj)); + } + } + j[key] = arr; + } else if (py::isinstance(val)) { + j[key] = py::cast(val); + } else if (py::isinstance(val)) { + j[key] = py::cast(val); + } else if (py::isinstance(val)) { + j[key] = py::cast(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())); + } else if (elem.is_number_float()) { + py_list.append(py::float_(elem.get())); + } else if (elem.is_boolean()) { + py_list.append(py::bool_(elem.get())); + } else if (elem.is_null()) { + py_list.append(py::none()); + } else { + py_list.append(py::str(elem.get())); + } + } + d[py::str(key)] = py_list; + } else if (val.is_number_integer()) { + d[py::str(key)] = py::int_(val.get()); + } else if (val.is_number_float()) { + d[py::str(key)] = py::float_(val.get()); + } else if (val.is_boolean()) { + d[py::str(key)] = py::bool_(val.get()); + } else if (val.is_null()) { + d[py::str(key)] = py::none(); + } else { + d[py::str(key)] = py::str(val.get()); + } + } + } + return d; +} + + + +} \ No newline at end of file diff --git a/cpp_ext/utils.hpp b/cpp_ext/utils.hpp index e69de29..53e34d5 100644 --- a/cpp_ext/utils.hpp +++ b/cpp_ext/utils.hpp @@ -0,0 +1,15 @@ +#pragma once + +#include +#include // 支持 std::vector, std::map 等 +#include +#include + +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); +} \ No newline at end of file diff --git a/keygen.py b/keygen.py new file mode 100644 index 0000000..00beda0 --- /dev/null +++ b/keygen.py @@ -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() diff --git a/ota_manager.py b/ota_manager.py index 4686585..36206c1 100644 --- a/ota_manager.py +++ b/ota_manager.py @@ -1193,6 +1193,8 @@ class OTAManager: from network import network_manager, safe_enqueue try: + # 与 4G 一致:OTA 期间暂停主循环 / 心跳等 + self._begin_ota() ip, error = network_manager.connect_wifi(ssid, password) if error: safe_enqueue({"result": "wifi_failed", "error": error}, 2) @@ -1231,6 +1233,7 @@ class OTAManager: self.logger.error(err_msg) finally: self._stop_update_thread() + self._end_ota() # 与 4G 一致 print("[UPDATE] 更新线程执行完毕,即将退出。") def restore_from_backup(self, backup_dir_path=None):