• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2022 Huawei Device Co., Ltd.
3  * Licensed under the Apache License, Version 2.0 (the "License");
4  * you may not use this file except in compliance with the License.
5  * You may obtain a copy of the License at
6  *
7  *     http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  */
15 
16 #include "zip_pkg_parse.h"
17 #include <vector>
18 #include "dump.h"
19 #include "pkg_utils.h"
20 
21 namespace Hpackage {
22 struct Footer {
23     uint16_t signDataStart;
24     uint16_t signDataFlag;
25     uint16_t signDataSize;
26 };
27 
28 namespace {
29 constexpr uint32_t ZIP_EOCD_LEN_EXCLUDE_COMMENT = 20;
30 constexpr uint32_t ZIP_EOCD_FIXED_PART_LEN = 22;
31 constexpr uint32_t PKG_FOOTER_SIZE = 6;
32 constexpr uint32_t PKG_ZIP_EOCD_MIN_LEN = ZIP_EOCD_FIXED_PART_LEN + PKG_FOOTER_SIZE;
33 constexpr uint32_t ZIP_EOCD_SIGNATURE = 0x06054b50;
34 constexpr uint16_t PKG_ZIP_EOCD_FOOTER_FLAG = 0xFFFF;
35 const uint8_t ZIP_EOCD_SIGNATURE_BIG_ENDIAN[4] = {0x50, 0x4b, 0x05, 0x06};
36 }
37 
38 /*
39  * ZIP:  File Entry(1..n) + CD(1..n) + EOCD(1)
40  *
41  * EOCD: FLAG(4 bytes) + FIX PART1(16 bytes) + comment length(2 bytes) + comment('comment length' bytes)
42  *
43  * EOCD comment: RESERVED(18 bytes) + SIGNATYRE(variable size) + FOOTER (6 bytes)
44  *
45  * FOOTER                           6 bytes (little endian)
46  *     append signed result length  2 bytes (SIGNATYRE's length + FOOTER's length) = SIGNATYRE reversed offset
47  *     0xFFFF                       2 bytes
48  *     = .ZIP file comment length   2 bytes
49  */
50 
DoParseZipPkg(PkgStreamPtr pkgStream,PkgSignComment & pkgSignComment,size_t & readLen,const uint16_t & signCommentAppendLen,uint16_t & signCommentTotalLen) const51 int32_t ZipPkgParse::DoParseZipPkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment,
52     size_t &readLen, const uint16_t &signCommentAppendLen, uint16_t &signCommentTotalLen) const
53 {
54     Updater::UPDATER_INIT_RECORD;
55     size_t fileLen = pkgStream->GetFileLength();
56     size_t eocdTotalLen = ZIP_EOCD_FIXED_PART_LEN + signCommentTotalLen;
57     if (fileLen <= eocdTotalLen) {
58         PKG_LOGE("Invalid eocd len[%zu]", eocdTotalLen);
59         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "Invalid eocd len[%zu]", eocdTotalLen);
60         return PKG_INVALID_PKG_FORMAT;
61     }
62 
63     size_t zipEocdStart = fileLen - eocdTotalLen;
64     PkgBuffer zipEocd(eocdTotalLen);
65     int32_t ret = pkgStream->Read(zipEocd, zipEocdStart, eocdTotalLen, readLen);
66     if (ret != PKG_SUCCESS) {
67         PKG_LOGE("read zip eocd failed %s", pkgStream->GetFileName().c_str());
68         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "read zip eocd failed " + pkgStream->GetFileName());
69         return ret;
70     }
71 
72     ret = CheckZipEocd(zipEocd.buffer, eocdTotalLen, signCommentTotalLen);
73     if (ret != PKG_SUCCESS) {
74         PKG_LOGE("CheckZipEocd() error, ret[%d]", ret);
75         UPDATER_LAST_WORD(ret, "CheckZipEocd() error");
76         return ret;
77     }
78 
79     if (fileLen <= signCommentTotalLen) {
80         PKG_LOGE("file len[%zu] < signCommentTotalLen[%zu]", fileLen, signCommentTotalLen);
81         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, fileLen, signCommentTotalLen);
82         return PKG_INVALID_FILE;
83     }
84     pkgSignComment.signCommentTotalLen = signCommentTotalLen;
85     pkgSignComment.signCommentAppendLen = signCommentAppendLen;
86 
87     return PKG_SUCCESS;
88 }
89 
ParseZipPkg(PkgStreamPtr pkgStream,PkgSignComment & pkgSignComment) const90 int32_t ZipPkgParse::ParseZipPkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment) const
91 {
92     Updater::UPDATER_INIT_RECORD;
93     if (pkgStream == nullptr) {
94         UPDATER_LAST_WORD(PKG_INVALID_PARAM, "pkgStream is invalid");
95         return PKG_INVALID_PARAM;
96     }
97     size_t fileLen = pkgStream->GetFileLength();
98     size_t footerSize = PKG_FOOTER_SIZE;
99     if (fileLen <= footerSize) {
100         PKG_LOGE("file len[%zu] < footerSize.", pkgStream->GetFileLength());
101         UPDATER_LAST_WORD(PKG_INVALID_FILE, fileLen);
102         return PKG_INVALID_FILE;
103     }
104     size_t footerStart = fileLen - footerSize;
105     size_t readLen = 0;
106     PkgBuffer footer(footerSize);
107     int32_t ret = pkgStream->Read(footer, footerStart, footerSize, readLen);
108     if (ret != PKG_SUCCESS) {
109         PKG_LOGE("read FOOTER struct failed %s", pkgStream->GetFileName().c_str());
110         UPDATER_LAST_WORD(ret);
111         return ret;
112     }
113 
114     uint16_t signCommentAppendLen = 0;
115     uint16_t signCommentTotalLen = 0;
116     ret = ParsePkgFooter(footer.buffer, PKG_FOOTER_SIZE, signCommentAppendLen, signCommentTotalLen);
117     if (ret != PKG_SUCCESS) {
118         PKG_LOGE("ParsePkgFooter() error, ret[%d]", ret);
119         UPDATER_LAST_WORD(ret, "ParsePkgFooter() error");
120         return ret;
121     }
122     return DoParseZipPkg(pkgStream, pkgSignComment, readLen, signCommentAppendLen, signCommentTotalLen);
123 }
124 
ParsePkgFooter(const uint8_t * footer,size_t length,uint16_t & signCommentAppendLen,uint16_t & signCommentTotalLen) const125 int32_t ZipPkgParse::ParsePkgFooter(const uint8_t *footer, size_t length,
126     uint16_t &signCommentAppendLen, uint16_t &signCommentTotalLen) const
127 {
128     Updater::UPDATER_INIT_RECORD;
129     if (length < PKG_FOOTER_SIZE) {
130         PKG_LOGE("length[%d] < Footer Size[%d]", length, PKG_FOOTER_SIZE);
131         UPDATER_LAST_WORD(PKG_INVALID_PARAM, length);
132         return PKG_INVALID_PARAM;
133     }
134 
135     Footer signFooter = {0};
136     size_t offset = 0;
137     signFooter.signDataStart = ReadLE16(footer);
138     offset += sizeof(uint16_t);
139     signFooter.signDataFlag = ReadLE16(footer + offset);
140     offset += sizeof(uint16_t);
141     signFooter.signDataSize = ReadLE16(footer + offset);
142     if (signFooter.signDataFlag != PKG_ZIP_EOCD_FOOTER_FLAG) {
143         PKG_LOGE("error FooterFlag[0x%04X]", signFooter.signDataFlag);
144         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signFooter.signDataFlag);
145         return PKG_INVALID_PKG_FORMAT;
146     }
147 
148     signCommentAppendLen = signFooter.signDataStart;
149     signCommentTotalLen = signFooter.signDataSize;
150     if ((signCommentAppendLen < PKG_FOOTER_SIZE) || (signCommentTotalLen < PKG_FOOTER_SIZE) ||
151         (signCommentAppendLen > signCommentTotalLen)) {
152         PKG_LOGE("bad footer length: append[0x%04X], total[0x%04X]",
153             signCommentAppendLen, signCommentTotalLen);
154         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signCommentAppendLen, signCommentTotalLen);
155         return PKG_INVALID_PKG_FORMAT;
156     }
157 
158     return PKG_SUCCESS;
159 }
160 
CheckZipEocd(const uint8_t * eocd,size_t length,uint16_t signCommentTotalLen) const161 int32_t ZipPkgParse::CheckZipEocd(const uint8_t *eocd, size_t length,
162     uint16_t signCommentTotalLen) const
163 {
164     Updater::UPDATER_INIT_RECORD;
165     if (length < PKG_ZIP_EOCD_MIN_LEN) {
166         PKG_LOGE("bad eocd length: append[0x%04X]", length);
167         UPDATER_LAST_WORD("bad eocd length", length);
168         return PKG_INVALID_PKG_FORMAT;
169     }
170 
171     uint32_t eocdSignature = ReadLE32(eocd);
172     if (eocdSignature != ZIP_EOCD_SIGNATURE) {
173         PKG_LOGE("bad zip eocd flag[%zu]", eocdSignature);
174         UPDATER_LAST_WORD("bad zip eocd flag", eocdSignature);
175         return PKG_INVALID_PKG_FORMAT;
176     }
177 
178     /* the beginning 4 chars are already checked before, so begin with i = 4; (length - 3) in case for overflow */
179     for (size_t i = 4; i < length - 3; i++) {
180         /* every 4 byte check if eocd, if eocd[i] = 0x50, eocd[i + 1] = 0x4b, eocd[i + 2] = 0x05, eocd[i + 3] = 0x06 */
181         /* the zip contain another ecod, we can consider it's invalid zip */
182         if (eocd[i] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[0] &&
183             eocd[i + 1] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[1] &&
184             eocd[i + 2] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[2] && /* eocd[i + 2] = 0x05 */
185             eocd[i + 3] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[3]) { /* eocd[i + 3] = 0x06 */
186             PKG_LOGE("EOCD marker occurs after start of EOCD");
187             UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, "EOCD marker occurs after start of EOCD");
188             return PKG_INVALID_PKG_FORMAT;
189         }
190     }
191 
192     const uint8_t *zipSignCommentAddr = eocd + ZIP_EOCD_LEN_EXCLUDE_COMMENT;
193     uint16_t tempLen = ReadLE16(zipSignCommentAddr);
194     if (signCommentTotalLen != tempLen) {
195         PKG_LOGE("compare sign comment length: eocd[0x%04X], footer[0x%04X] error", tempLen, signCommentTotalLen);
196         UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, tempLen, signCommentTotalLen);
197         return PKG_INVALID_PKG_FORMAT;
198     }
199 
200     return PKG_SUCCESS;
201 }
202 } // namespace Hpackage
203