* Copyright (c) 2026 Huawei Device Co., Ltd.
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "zip_pkg_parse.h"
#include <vector>
#include "dump.h"
#include "pkg_utils.h"
namespace Hpackage {
struct Footer {
uint16_t signDataStart;
uint16_t signDataFlag;
uint16_t signDataSize;
};
namespace {
constexpr uint32_t ZIP_EOCD_LEN_EXCLUDE_COMMENT = 20;
constexpr uint32_t ZIP_EOCD_FIXED_PART_LEN = 22;
constexpr uint32_t PKG_FOOTER_SIZE = 6;
constexpr uint32_t PKG_ZIP_EOCD_MIN_LEN = ZIP_EOCD_FIXED_PART_LEN + PKG_FOOTER_SIZE;
constexpr uint32_t ZIP_EOCD_SIGNATURE = 0x06054b50;
constexpr uint16_t PKG_ZIP_EOCD_FOOTER_FLAG = 0xFFFF;
const uint8_t ZIP_EOCD_SIGNATURE_BIG_ENDIAN[4] = {0x50, 0x4b, 0x05, 0x06};
}
* ZIP: File Entry(1..n) + CD(1..n) + EOCD(1)
*
* EOCD: FLAG(4 bytes) + FIX PART1(16 bytes) + comment length(2 bytes) + comment('comment length' bytes)
*
* EOCD comment: RESERVED(18 bytes) + SIGNATYRE(variable size) + FOOTER (6 bytes)
*
* FOOTER 6 bytes (little endian)
* append signed result length 2 bytes (SIGNATYRE's length + FOOTER's length) = SIGNATYRE reversed offset
* 0xFFFF 2 bytes
* = .ZIP file comment length 2 bytes
*/
int32_t ZipPkgParse::DoParseZipPkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment,
size_t &readLen, const uint16_t &signCommentAppendLen, uint16_t &signCommentTotalLen) const
{
Updater::UPDATER_INIT_RECORD;
size_t fileLen = pkgStream->GetFileLength();
size_t eocdTotalLen = ZIP_EOCD_FIXED_PART_LEN + signCommentTotalLen;
if (fileLen <= eocdTotalLen) {
PKG_LOGE("Invalid eocd len[%zu]", eocdTotalLen);
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "Invalid eocd len ", eocdTotalLen);
return PKG_INVALID_PKG_FORMAT;
}
size_t zipEocdStart = fileLen - eocdTotalLen;
PkgBuffer zipEocd(eocdTotalLen);
int32_t ret = pkgStream->Read(zipEocd, zipEocdStart, eocdTotalLen, readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("read zip eocd failed %s", pkgStream->GetFileName().c_str());
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "read zip eocd failed " + pkgStream->GetFileName());
return ret;
}
ret = CheckZipEocd(zipEocd.buffer, eocdTotalLen, signCommentTotalLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("CheckZipEocd() error, ret[%d]", ret);
UPDATER_LAST_WORD(ret, "CheckZipEocd() error");
return ret;
}
if (fileLen <= signCommentTotalLen) {
PKG_LOGE("file len[%zu] < signCommentTotalLen[%zu]", fileLen, signCommentTotalLen);
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, fileLen, signCommentTotalLen);
return PKG_INVALID_FILE;
}
pkgSignComment.signCommentTotalLen = signCommentTotalLen;
pkgSignComment.signCommentAppendLen = signCommentAppendLen;
return PKG_SUCCESS;
}
int32_t ZipPkgParse::ReadFooterFromStream(PkgStreamPtr pkgStream, size_t &readLen, uint16_t &signCommentAppendLen,
uint16_t &signCommentTotalLen) const
{
Updater::UPDATER_INIT_RECORD;
if (pkgStream == nullptr) {
PKG_LOGE("pkgStream is null");
UPDATER_LAST_WORD(PKG_INVALID_PARAM, "pkgStream is invalid");
return PKG_INVALID_PARAM;
}
size_t fileLen = pkgStream->GetFileLength();
size_t footerSize = PKG_FOOTER_SIZE;
if (fileLen <= footerSize) {
PKG_LOGE("file len[%zu] < footerSize.", pkgStream->GetFileLength());
UPDATER_LAST_WORD(PKG_INVALID_FILE, fileLen);
return PKG_INVALID_FILE;
}
size_t footerStart = fileLen - footerSize;
PkgBuffer footer(footerSize);
int32_t ret = pkgStream->Read(footer, footerStart, footerSize, readLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("read FOOTER struct failed %s", pkgStream->GetFileName().c_str());
UPDATER_LAST_WORD(ret);
return ret;
}
ret = ParsePkgFooter(footer.buffer, PKG_FOOTER_SIZE, signCommentAppendLen, signCommentTotalLen);
if (ret != PKG_SUCCESS) {
PKG_LOGE("ParsePkgFooter() error, ret[%d]", ret);
UPDATER_LAST_WORD(ret, "ParsePkgFooter() error");
return ret;
}
return PKG_SUCCESS;
}
int32_t ZipPkgParse::ParsePkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment) const
{
Updater::UPDATER_INIT_RECORD;
uint16_t signCommentAppendLen = 0;
uint16_t signCommentTotalLen = 0;
size_t readLen = 0;
if (auto ret = ReadFooterFromStream(pkgStream, readLen, signCommentAppendLen, signCommentTotalLen);
ret != PKG_SUCCESS) {
return ret;
}
return DoParseZipPkg(pkgStream, pkgSignComment, readLen, signCommentAppendLen, signCommentTotalLen);
}
int32_t ZipPkgParse::ParsePkgFooter(const uint8_t *footer, size_t length,
uint16_t &signCommentAppendLen, uint16_t &signCommentTotalLen) const
{
Updater::UPDATER_INIT_RECORD;
if (length < PKG_FOOTER_SIZE) {
PKG_LOGE("length[%d] < Footer Size[%d]", length, PKG_FOOTER_SIZE);
UPDATER_LAST_WORD(PKG_INVALID_PARAM, length);
return PKG_INVALID_PARAM;
}
Footer signFooter = {0};
size_t offset = 0;
signFooter.signDataStart = ReadLE16(footer);
offset += sizeof(uint16_t);
signFooter.signDataFlag = ReadLE16(footer + offset);
offset += sizeof(uint16_t);
signFooter.signDataSize = ReadLE16(footer + offset);
if (signFooter.signDataFlag != PKG_ZIP_EOCD_FOOTER_FLAG) {
PKG_LOGE("error FooterFlag[0x%04X], start is %zu, size is %zu, len is %zu, offset is %zu",
signFooter.signDataFlag, signFooter.signDataStart, signFooter.signDataSize, length, offset);
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "signFooter.signDataFlag != PKG_ZIP_EOCD_FOOTER_FLAG",
signFooter.signDataFlag, signFooter.signDataStart, signFooter.signDataSize, length, offset);
return PKG_INVALID_PKG_FORMAT;
}
signCommentAppendLen = signFooter.signDataStart;
signCommentTotalLen = signFooter.signDataSize;
if ((signCommentAppendLen < PKG_FOOTER_SIZE) || (signCommentTotalLen < PKG_FOOTER_SIZE) ||
(signCommentAppendLen > signCommentTotalLen)) {
PKG_LOGE("bad footer length: append[0x%04X], total[0x%04X]",
signCommentAppendLen, signCommentTotalLen);
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signCommentAppendLen, signCommentTotalLen);
return PKG_INVALID_PKG_FORMAT;
}
return PKG_SUCCESS;
}
uint32_t ZipPkgParse::GetFixedPartLen(void) const
{
return ZIP_EOCD_FIXED_PART_LEN;
}
bool ZipPkgParse::IsSupportOldSig(void) const
{
return true;
}
int32_t ZipPkgParse::CheckZipEocd(const uint8_t *eocd, size_t length,
uint16_t signCommentTotalLen) const
{
Updater::UPDATER_INIT_RECORD;
if (length < PKG_ZIP_EOCD_MIN_LEN) {
PKG_LOGE("bad eocd length: append[0x%04X]", length);
UPDATER_LAST_WORD("bad eocd length", length);
return PKG_INVALID_PKG_FORMAT;
}
uint32_t eocdSignature = ReadLE32(eocd);
if (eocdSignature != ZIP_EOCD_SIGNATURE) {
PKG_LOGE("bad zip eocd flag[%zu]", eocdSignature);
UPDATER_LAST_WORD("bad zip eocd flag", eocdSignature);
return PKG_INVALID_PKG_FORMAT;
}
for (size_t i = 4; i < length - 3; i++) {
if (eocd[i] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[0] &&
eocd[i + 1] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[1] &&
eocd[i + 2] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[2] &&
eocd[i + 3] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[3]) {
PKG_LOGE("EOCD marker occurs after start of EOCD");
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "EOCD marker occurs after start of EOCD");
return PKG_INVALID_PKG_FORMAT;
}
}
const uint8_t *zipSignCommentAddr = eocd + ZIP_EOCD_LEN_EXCLUDE_COMMENT;
uint16_t tempLen = ReadLE16(zipSignCommentAddr);
if (signCommentTotalLen != tempLen) {
PKG_LOGE("compare sign comment length: eocd[0x%04X], footer[0x%04X] error", tempLen, signCommentTotalLen);
UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, tempLen, signCommentTotalLen);
return PKG_INVALID_PKG_FORMAT;
}
return PKG_SUCCESS;
}
}