#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; constexpr size_t kHeaderLen = kOtaMagicLen + kGcmNonceLen; // 分块解密,避免整包读入导致 RAM 峰值约为「文件大小×2」(小内存设备易 OOM) constexpr size_t kDecryptChunk = 65536; static std::array ota_key_bytes() { 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; } bool decrypt_ota_file_impl(const std::string& input_path, const std::string& output_zip_path) { std::ifstream ifs(input_path, std::ios::binary); if (!ifs) { netcore::log_error(std::string("decrypt_ota_file: open in failed: ") + input_path); return false; } ifs.seekg(0, std::ios::end); const std::streampos szp = ifs.tellg(); if (szp <= 0) { netcore::log_error("decrypt_ota_file: empty input"); return false; } const uint64_t file_size = static_cast(szp); const size_t min_len = kHeaderLen + kGcmTagLen + 1; if (file_size < min_len) { netcore::log_error("decrypt_ota_file: too short"); return false; } const uint64_t ciphertext_len = file_size - kHeaderLen - kGcmTagLen; ifs.seekg(0, std::ios::beg); std::array header{}; ifs.read(reinterpret_cast(header.data()), static_cast(kHeaderLen)); if (ifs.gcount() != static_cast(kHeaderLen)) { netcore::log_error("decrypt_ota_file: read header failed"); return false; } if (!std::equal(header.begin(), header.begin() + kOtaMagicLen, reinterpret_cast(kOtaMagic))) { netcore::log_error("decrypt_ota_file: bad magic"); return false; } const uint8_t* nonce = header.data() + kOtaMagicLen; std::ofstream ofs(output_zip_path, std::ios::binary | std::ios::trunc); if (!ofs) { netcore::log_error(std::string("decrypt_ota_file: open out failed: ") + output_zip_path); return false; } 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(); std::vector chunk_in(kDecryptChunk); std::vector chunk_out(kDecryptChunk + EVP_MAX_BLOCK_LENGTH); 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; } uint64_t remaining = ciphertext_len; while (remaining > 0) { const size_t n = static_cast(std::min(remaining, kDecryptChunk)); ifs.read(reinterpret_cast(chunk_in.data()), static_cast(n)); if (ifs.gcount() != static_cast(n)) { netcore::log_error("decrypt_ota_file: read ciphertext chunk failed"); goto cleanup_ctx; } int outl = 0; if (1 != EVP_DecryptUpdate(ctx, chunk_out.data(), &outl, chunk_in.data(), static_cast(n))) { netcore::log_error("decrypt_ota_file: update failed"); goto cleanup_ctx; } if (outl > 0) { ofs.write(reinterpret_cast(chunk_out.data()), outl); if (!ofs) { netcore::log_error("decrypt_ota_file: write plaintext failed"); goto cleanup_ctx; } } remaining -= n; } std::array tag{}; ifs.read(reinterpret_cast(tag.data()), static_cast(kGcmTagLen)); if (ifs.gcount() != static_cast(kGcmTagLen)) { netcore::log_error("decrypt_ota_file: read tag failed"); break; } if (1 != EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, static_cast(kGcmTagLen), tag.data())) { netcore::log_error("decrypt_ota_file: set tag failed"); break; } int outl2 = 0; if (1 != EVP_DecryptFinal_ex(ctx, chunk_out.data(), &outl2)) { netcore::log_error("decrypt_ota_file: final failed (auth tag mismatch?)"); break; } if (outl2 > 0) { ofs.write(reinterpret_cast(chunk_out.data()), outl2); if (!ofs) { netcore::log_error("decrypt_ota_file: write final failed"); break; } } ok = true; } while (false); cleanup_ctx: EVP_CIPHER_CTX_free(ctx); return ok; } } // namespace netcore