#include #include // 支持 std::vector, std::map 等 #include #include #include #include #include #include #include #include #include #include "native_logger.hpp" namespace py = pybind11; using json = nlohmann::json; namespace { // 配置项 const std::string _cfg_server_ip = "www.shelingxingqiu.com"; const int _cfg_server_port = 50005; // 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; } } // 定义获取配置的函数 py::dict get_config() { py::dict config; config["SERVER_IP"] = _cfg_server_ip; config["SERVER_PORT"] = _cfg_server_port; return 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)."; // Optional: configure native logger from Python. // Default log file: /maixapp/apps/t11/netcore.log m.def("set_log_file", [](const std::string& path) { netcore::set_log_file(path); }, py::arg("path")); m.def("set_log_level", [](int level) { if (level < 0) level = 0; if (level > 3) level = 3; netcore::set_log_level(static_cast(level)); }, py::arg("level")); m.def("log_test", [](const std::string& msg) { netcore::log_info(std::string("log_test: ") + msg); }, py::arg("msg")); m.def("make_packet", &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, "Parse TCP packet, return (msg_type, body_dict)"); m.def("get_config", &get_config, "Get system configuration"); m.def( "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); }, py::arg("input_path"), py::arg("output_zip_path"), "Decrypt OTA encrypted file (MAGIC|nonce|ciphertext|tag) to plaintext zip." ); // Minimal demo: return actions for inner_cmd=41 (manual trigger + ack) m.def("actions_for_inner_cmd", [](int inner_cmd) { py::list actions; if (inner_cmd == 41) { // 1) set manual trigger flag { py::dict a; a["type"] = "SET_FLAG"; py::dict args; args["name"] = "manual_trigger_flag"; args["value"] = true; a["args"] = args; actions.append(a); } // 2) enqueue trigger_ack { py::dict a; a["type"] = "ENQUEUE"; py::dict args; args["msg_type"] = 2; args["high"] = false; py::dict body; body["result"] = "trigger_ack"; args["body"] = body; a["args"] = args; actions.append(a); } } return actions; }); }