/* * Copyright (c) 2022 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 #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 { 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); 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); return ret; } ret = CheckZipEocd(zipEocd.buffer, eocdTotalLen, signCommentTotalLen); if (ret != PKG_SUCCESS) { PKG_LOGE("CheckZipEocd() error, ret[%d]", ret); UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT); 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::ParseZipPkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment) const { if (pkgStream == nullptr) { UPDATER_LAST_WORD(PKG_INVALID_PARAM); 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; size_t readLen = 0; 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; } uint16_t signCommentAppendLen = 0; uint16_t signCommentTotalLen = 0; ret = ParsePkgFooter(footer.buffer, PKG_FOOTER_SIZE, signCommentAppendLen, signCommentTotalLen); if (ret != PKG_SUCCESS) { PKG_LOGE("ParsePkgFooter() error, ret[%d]", ret); UPDATER_LAST_WORD(ret); 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]", signFooter.signDataFlag); UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signFooter.signDataFlag); 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; } 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(PKG_INVALID_PKG_FORMAT); 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(PKG_INVALID_PKG_FORMAT); return PKG_INVALID_PKG_FORMAT; } /* the beginning 4 chars are already checked before, so begin with i = 4; (length - 3) in case for overflow */ for (size_t i = 4; i < length - 3; i++) { /* every 4 byte check if eocd, if eocd[i] = 0x50, eocd[i + 1] = 0x4b, eocd[i + 2] = 0x05, eocd[i + 3] = 0x06 */ /* the zip contain another ecod, we can consider it's invalid zip */ 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 + 2] = 0x05 */ eocd[i + 3] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[3]) { /* eocd[i + 3] = 0x06 */ PKG_LOGE("EOCD marker occurs after start of EOCD"); UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT); 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); return PKG_INVALID_PKG_FORMAT; } return PKG_SUCCESS; } } // namespace Hpackage