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 size_t fileLen = pkgStream->GetFileLength();
55 size_t eocdTotalLen = ZIP_EOCD_FIXED_PART_LEN + signCommentTotalLen;
56 if (fileLen <= eocdTotalLen) {
57 PKG_LOGE("Invalid eocd len[%zu]", eocdTotalLen);
58 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
59 return PKG_INVALID_PKG_FORMAT;
60 }
61
62 size_t zipEocdStart = fileLen - eocdTotalLen;
63 PkgBuffer zipEocd(eocdTotalLen);
64 int32_t ret = pkgStream->Read(zipEocd, zipEocdStart, eocdTotalLen, readLen);
65 if (ret != PKG_SUCCESS) {
66 PKG_LOGE("read zip eocd failed %s", pkgStream->GetFileName().c_str());
67 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
68 return ret;
69 }
70
71 ret = CheckZipEocd(zipEocd.buffer, eocdTotalLen, signCommentTotalLen);
72 if (ret != PKG_SUCCESS) {
73 PKG_LOGE("CheckZipEocd() error, ret[%d]", ret);
74 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
75 return ret;
76 }
77
78 if (fileLen <= signCommentTotalLen) {
79 PKG_LOGE("file len[%zu] < signCommentTotalLen[%zu]", fileLen, signCommentTotalLen);
80 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, fileLen, signCommentTotalLen);
81 return PKG_INVALID_FILE;
82 }
83 pkgSignComment.signCommentTotalLen = signCommentTotalLen;
84 pkgSignComment.signCommentAppendLen = signCommentAppendLen;
85
86 return PKG_SUCCESS;
87 }
88
ParseZipPkg(PkgStreamPtr pkgStream,PkgSignComment & pkgSignComment) const89 int32_t ZipPkgParse::ParseZipPkg(PkgStreamPtr pkgStream, PkgSignComment &pkgSignComment) const
90 {
91 if (pkgStream == nullptr) {
92 UPDATER_LAST_WORD(PKG_INVALID_PARAM);
93 return PKG_INVALID_PARAM;
94 }
95 size_t fileLen = pkgStream->GetFileLength();
96 size_t footerSize = PKG_FOOTER_SIZE;
97 if (fileLen <= footerSize) {
98 PKG_LOGE("file len[%zu] < footerSize.", pkgStream->GetFileLength());
99 UPDATER_LAST_WORD(PKG_INVALID_FILE, fileLen);
100 return PKG_INVALID_FILE;
101 }
102 size_t footerStart = fileLen - footerSize;
103 size_t readLen = 0;
104 PkgBuffer footer(footerSize);
105 int32_t ret = pkgStream->Read(footer, footerStart, footerSize, readLen);
106 if (ret != PKG_SUCCESS) {
107 PKG_LOGE("read FOOTER struct failed %s", pkgStream->GetFileName().c_str());
108 UPDATER_LAST_WORD(ret);
109 return ret;
110 }
111
112 uint16_t signCommentAppendLen = 0;
113 uint16_t signCommentTotalLen = 0;
114 ret = ParsePkgFooter(footer.buffer, PKG_FOOTER_SIZE, signCommentAppendLen, signCommentTotalLen);
115 if (ret != PKG_SUCCESS) {
116 PKG_LOGE("ParsePkgFooter() error, ret[%d]", ret);
117 UPDATER_LAST_WORD(ret);
118 return ret;
119 }
120 return DoParseZipPkg(pkgStream, pkgSignComment, readLen, signCommentAppendLen, signCommentTotalLen);
121 }
122
ParsePkgFooter(const uint8_t * footer,size_t length,uint16_t & signCommentAppendLen,uint16_t & signCommentTotalLen) const123 int32_t ZipPkgParse::ParsePkgFooter(const uint8_t *footer, size_t length,
124 uint16_t &signCommentAppendLen, uint16_t &signCommentTotalLen) const
125 {
126 Updater::UPDATER_INIT_RECORD;
127 if (length < PKG_FOOTER_SIZE) {
128 PKG_LOGE("length[%d] < Footer Size[%d]", length, PKG_FOOTER_SIZE);
129 UPDATER_LAST_WORD(PKG_INVALID_PARAM, length);
130 return PKG_INVALID_PARAM;
131 }
132
133 Footer signFooter = {0};
134 size_t offset = 0;
135 signFooter.signDataStart = ReadLE16(footer);
136 offset += sizeof(uint16_t);
137 signFooter.signDataFlag = ReadLE16(footer + offset);
138 offset += sizeof(uint16_t);
139 signFooter.signDataSize = ReadLE16(footer + offset);
140 if (signFooter.signDataFlag != PKG_ZIP_EOCD_FOOTER_FLAG) {
141 PKG_LOGE("error FooterFlag[0x%04X]", signFooter.signDataFlag);
142 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signFooter.signDataFlag);
143 return PKG_INVALID_PKG_FORMAT;
144 }
145
146 signCommentAppendLen = signFooter.signDataStart;
147 signCommentTotalLen = signFooter.signDataSize;
148 if ((signCommentAppendLen < PKG_FOOTER_SIZE) || (signCommentTotalLen < PKG_FOOTER_SIZE) ||
149 (signCommentAppendLen > signCommentTotalLen)) {
150 PKG_LOGE("bad footer length: append[0x%04X], total[0x%04X]",
151 signCommentAppendLen, signCommentTotalLen);
152 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT, signCommentAppendLen, signCommentTotalLen);
153 return PKG_INVALID_PKG_FORMAT;
154 }
155
156 return PKG_SUCCESS;
157 }
158
CheckZipEocd(const uint8_t * eocd,size_t length,uint16_t signCommentTotalLen) const159 int32_t ZipPkgParse::CheckZipEocd(const uint8_t *eocd, size_t length,
160 uint16_t signCommentTotalLen) const
161 {
162 Updater::UPDATER_INIT_RECORD;
163 if (length < PKG_ZIP_EOCD_MIN_LEN) {
164 PKG_LOGE("bad eocd length: append[0x%04X]", length);
165 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
166 return PKG_INVALID_PKG_FORMAT;
167 }
168
169 uint32_t eocdSignature = ReadLE32(eocd);
170 if (eocdSignature != ZIP_EOCD_SIGNATURE) {
171 PKG_LOGE("bad zip eocd flag[%zu]", eocdSignature);
172 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
173 return PKG_INVALID_PKG_FORMAT;
174 }
175
176 /* the beginning 4 chars are already checked before, so begin with i = 4; (length - 3) in case for overflow */
177 for (size_t i = 4; i < length - 3; i++) {
178 /* every 4 byte check if eocd, if eocd[i] = 0x50, eocd[i + 1] = 0x4b, eocd[i + 2] = 0x05, eocd[i + 3] = 0x06 */
179 /* the zip contain another ecod, we can consider it's invalid zip */
180 if (eocd[i] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[0] &&
181 eocd[i + 1] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[1] &&
182 eocd[i + 2] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[2] && /* eocd[i + 2] = 0x05 */
183 eocd[i + 3] == ZIP_EOCD_SIGNATURE_BIG_ENDIAN[3]) { /* eocd[i + 3] = 0x06 */
184 PKG_LOGE("EOCD marker occurs after start of EOCD");
185 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
186 return PKG_INVALID_PKG_FORMAT;
187 }
188 }
189
190 const uint8_t *zipSignCommentAddr = eocd + ZIP_EOCD_LEN_EXCLUDE_COMMENT;
191 uint16_t tempLen = ReadLE16(zipSignCommentAddr);
192 if (signCommentTotalLen != tempLen) {
193 PKG_LOGE("compare sign comment length: eocd[0x%04X], footer[0x%04X] error", tempLen, signCommentTotalLen);
194 UPDATER_LAST_WORD(PKG_INVALID_PKG_FORMAT);
195 return PKG_INVALID_PKG_FORMAT;
196 }
197
198 return PKG_SUCCESS;
199 }
200 } // namespace Hpackage
201