1 /*
2 * Copyright (c) 2024-2024 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 <algorithm>
17 #include <filesystem>
18
19 #include "file_utils.h"
20 #include "zip_entry.h"
21 #include "zip_signer.h"
22
23 namespace OHOS {
24 namespace SignatureTools {
Init(std::ifstream & inputFile)25 bool ZipSigner::Init(std::ifstream& inputFile)
26 {
27 if (!inputFile.good()) {
28 return false;
29 }
30
31 /* 1. get eocd data */
32 m_endOfCentralDirectory = GetZipEndOfCentralDirectory(inputFile);
33 if (!m_endOfCentralDirectory) {
34 SIGNATURE_TOOLS_LOGE("get eocd data failed.");
35 return false;
36 }
37
38 m_cDOffset = m_endOfCentralDirectory->GetOffset();
39
40 /* 2. use eocd's cd offset, get cd data */
41 if (!GetZipCentralDirectory(inputFile)) {
42 SIGNATURE_TOOLS_LOGE("get zip central directory failed.");
43 return false;
44 }
45
46 /* 3. use cd's entry offset and file size, get entry data */
47 if (!GetZipEntries(inputFile)) {
48 SIGNATURE_TOOLS_LOGE("get zip entries failed.");
49 return false;
50 }
51
52 ZipEntry* endEntry = m_zipEntries[m_zipEntries.size() - 1];
53 CentralDirectory* endCD = endEntry->GetCentralDirectory();
54 ZipEntryData* endEntryData = endEntry->GetZipEntryData();
55 m_signingOffset = endCD->GetOffset() + endEntryData->GetLength();
56
57 /* 4. file all data - eocd - cd - entry = sign block */
58 m_signingBlock = GetSigningBlock(inputFile);
59
60 return true;
61 }
62
GetZipEndOfCentralDirectory(std::ifstream & input)63 EndOfCentralDirectory* ZipSigner::GetZipEndOfCentralDirectory(std::ifstream& input)
64 {
65 /* move file pointer to the end */
66 input.seekg(0, std::ios::end);
67 uint64_t fileSize = static_cast<uint64_t>(input.tellg());
68 /* move file pointer to the begin */
69 input.seekg(0, std::ios::beg);
70
71 if (fileSize < EndOfCentralDirectory::EOCD_LENGTH) {
72 SIGNATURE_TOOLS_LOGE("find zip eocd failed");
73 return nullptr;
74 }
75
76 /* try to read EOCD without comment */
77 int eocdLength = EndOfCentralDirectory::EOCD_LENGTH;
78 m_eOCDOffset = fileSize - eocdLength;
79
80 std::string retStr;
81 int ret = FileUtils::ReadFileByOffsetAndLength(input, m_eOCDOffset, eocdLength, retStr);
82 if (0 != ret) {
83 SIGNATURE_TOOLS_LOGE("read eocd without comment failed in file");
84 return nullptr;
85 }
86
87 std::optional<EndOfCentralDirectory*> eocdByBytes = EndOfCentralDirectory::GetEOCDByBytes(retStr);
88 if (eocdByBytes) {
89 return eocdByBytes.value();
90 }
91
92 /* try to search EOCD with comment */
93 uint64_t eocdMaxLength = std::min(static_cast<uint64_t>(EndOfCentralDirectory::EOCD_LENGTH + MAX_COMMENT_LENGTH),
94 fileSize);
95 m_eOCDOffset = static_cast<uint64_t>(input.tellg()) - eocdMaxLength;
96
97 retStr.clear();
98 ret = FileUtils::ReadFileByOffsetAndLength(input, m_eOCDOffset, eocdMaxLength, retStr);
99 if (0 != ret) {
100 SIGNATURE_TOOLS_LOGE("read eocd with comment failed in file");
101 return nullptr;
102 }
103
104 for (uint64_t start = 0; start < eocdMaxLength; start++) {
105 eocdByBytes = EndOfCentralDirectory::GetEOCDByBytes(retStr, start);
106 if (eocdByBytes) {
107 m_eOCDOffset += start;
108 return eocdByBytes.value();
109 }
110 }
111 SIGNATURE_TOOLS_LOGE("read zip failed: can not find eocd in file");
112 PrintErrorNumberMsg("ZIP_ERROR", ZIP_ERROR, "can not find eocd in file");
113 return nullptr;
114 }
115
GetZipCentralDirectory(std::ifstream & input)116 bool ZipSigner::GetZipCentralDirectory(std::ifstream& input)
117 {
118 input.seekg(0, std::ios::beg);
119
120 uint16_t cDtotal = m_endOfCentralDirectory->GetcDTotal();
121 m_zipEntries.reserve(cDtotal);
122 /* read full central directory bytes */
123 std::string retStr;
124
125 int ret = FileUtils::ReadFileByOffsetAndLength(input, m_cDOffset, m_endOfCentralDirectory->GetcDSize(), retStr);
126 if (0 != ret) {
127 SIGNATURE_TOOLS_LOGE("read full central directory failed in file");
128 return false;
129 }
130
131 if (retStr.size() < CentralDirectory::CD_LENGTH) {
132 SIGNATURE_TOOLS_LOGE("find zip cd failed");
133 return false;
134 }
135
136 ByteBuffer bf(retStr.c_str(), retStr.size());
137
138 std::string::size_type offset = 0;
139 /* one by one format central directory */
140 while (offset < retStr.size()) {
141 CentralDirectory* cd = new CentralDirectory();
142 if (!CentralDirectory::GetCentralDirectory(bf, cd)) {
143 return false;
144 }
145 ZipEntry* entry = new ZipEntry();
146 entry->SetCentralDirectory(cd);
147 m_zipEntries.emplace_back(entry);
148 offset += cd->GetLength();
149 }
150
151 if (offset + m_cDOffset != m_eOCDOffset) {
152 SIGNATURE_TOOLS_LOGE("cd end offset not equals to eocd offset, maybe this is a zip64 file");
153 return false;
154 }
155 return true;
156 }
157
GetSigningBlock(std::ifstream & file)158 std::string ZipSigner::GetSigningBlock(std::ifstream& file)
159 {
160 int64_t size = static_cast<int64_t>(m_cDOffset) - static_cast<int64_t>(m_signingOffset);
161 if (size < 0) {
162 SIGNATURE_TOOLS_LOGE("signing offset in front of entry end");
163 return "";
164 }
165 if (size == 0) {
166 return "";
167 }
168
169 std::string retStr;
170 int ret = FileUtils::ReadFileByOffsetAndLength(file, m_signingOffset, size, retStr);
171 if (0 != ret) {
172 SIGNATURE_TOOLS_LOGE("read signing block failed in file");
173 return "";
174 }
175 return retStr;
176 }
177
GetZipEntries(std::ifstream & input)178 bool ZipSigner::GetZipEntries(std::ifstream& input)
179 {
180 /* use central directory data, find entry data */
181 for (auto& entry : m_zipEntries) {
182 CentralDirectory* cd = entry->GetCentralDirectory();
183 uint32_t offset = cd->GetOffset();
184 uint32_t unCompressedSize = cd->GetUnCompressedSize();
185 uint32_t compressedSize = cd->GetCompressedSize();
186 uint32_t fileSize = cd->GetMethod() == FILE_UNCOMPRESS_METHOD_FLAG ? unCompressedSize : compressedSize;
187
188 ZipEntryData* zipEntryData = ZipEntryData::GetZipEntry(input, offset, fileSize);
189 if (!zipEntryData) {
190 return false;
191 }
192 if (m_cDOffset - offset < zipEntryData->GetLength()) {
193 SIGNATURE_TOOLS_LOGE("cd offset in front of entry end");
194 return false;
195 }
196 entry->SetZipEntryData(zipEntryData);
197 }
198 return true;
199 }
200
ToFile(std::ifstream & input,std::ofstream & output)201 bool ZipSigner::ToFile(std::ifstream& input, std::ofstream& output)
202 {
203 SIGNATURE_TOOLS_LOGI("Zip To File begin");
204 if (!input.good()) {
205 SIGNATURE_TOOLS_LOGE("read zip input file failed");
206 return false;
207 }
208 if (!output.good()) {
209 SIGNATURE_TOOLS_LOGE("read zip output file failed");
210 return false;
211 }
212
213 for (const auto& entry : m_zipEntries) {
214 ZipEntryData* zipEntryData = entry->GetZipEntryData();
215 std::string zipEntryHeaderStr = zipEntryData->GetZipEntryHeader()->ToBytes();
216 if (!FileUtils::WriteByteToOutFile(zipEntryHeaderStr, output)) {
217 return false;
218 }
219
220 uint32_t fileOffset = zipEntryData->GetFileOffset();
221 uint32_t fileSize = zipEntryData->GetFileSize();
222 bool isSuccess = FileUtils::AppendWriteFileByOffsetToFile(input, output, fileOffset, fileSize);
223 if (!isSuccess) {
224 SIGNATURE_TOOLS_LOGE("write zip data failed");
225 return false;
226 }
227 DataDescriptor* dataDescriptor = zipEntryData->GetDataDescriptor();
228 if (dataDescriptor) {
229 std::string dataDescriptorStr = dataDescriptor->ToBytes();
230 if (!FileUtils::WriteByteToOutFile(dataDescriptorStr, output)) {
231 return false;
232 }
233 }
234 }
235
236 if (!m_signingBlock.empty()) {
237 if (!FileUtils::WriteByteToOutFile(m_signingBlock, output)) {
238 return false;
239 }
240 }
241
242 for (const auto& entry : m_zipEntries) {
243 CentralDirectory* cd = entry->GetCentralDirectory();
244 if (!FileUtils::WriteByteToOutFile(cd->ToBytes(), output)) {
245 return false;
246 }
247 }
248
249 if (!FileUtils::WriteByteToOutFile(m_endOfCentralDirectory->ToBytes(), output)) {
250 return false;
251 }
252
253 SIGNATURE_TOOLS_LOGI("Zip To File end");
254 return true;
255 }
256
Alignment(int alignment)257 void ZipSigner::Alignment(int alignment)
258 {
259 Sort();
260 bool isFirstUnRunnableFile = true;
261 for (const auto& entry : m_zipEntries) {
262 ZipEntryData* zipEntryData = entry->GetZipEntryData();
263 short method = zipEntryData->GetZipEntryHeader()->GetMethod();
264 if (method != FILE_UNCOMPRESS_METHOD_FLAG && !isFirstUnRunnableFile) {
265 /* only align uncompressed entry and the first compress entry. */
266 break;
267 }
268 int alignBytes;
269 if (method == FILE_UNCOMPRESS_METHOD_FLAG &&
270 FileUtils::IsRunnableFile(zipEntryData->GetZipEntryHeader()->GetFileName())) {
271 /* .abc and .so file align 4096 byte. */
272 alignBytes = 4096;
273 } else if (isFirstUnRunnableFile) {
274 /* the first file after runnable file, align 4096 byte. */
275 alignBytes = 4096;
276 isFirstUnRunnableFile = false;
277 } else {
278 /* normal file align 4 byte. */
279 alignBytes = alignment;
280 }
281 int add = entry->Alignment(alignBytes);
282 if (add > 0) {
283 ResetOffset();
284 }
285 }
286 }
287
RemoveSignBlock()288 void ZipSigner::RemoveSignBlock()
289 {
290 m_signingBlock = std::string();
291 ResetOffset();
292 }
293
Sort()294 void ZipSigner::Sort()
295 {
296 /* sort uncompress file (so, abc, an) - other uncompress file - compress file */
297 std::sort(m_zipEntries.begin(), m_zipEntries.end(), [&](ZipEntry* entry1, ZipEntry* entry2) {
298 short entry1Method = entry1->GetZipEntryData()->GetZipEntryHeader()->GetMethod();
299 short entry2Method = entry2->GetZipEntryData()->GetZipEntryHeader()->GetMethod();
300 std::string entry1FileName = entry1->GetZipEntryData()->GetZipEntryHeader()->GetFileName();
301 std::string entry2FileName = entry2->GetZipEntryData()->GetZipEntryHeader()->GetFileName();
302 if (entry1Method == FILE_UNCOMPRESS_METHOD_FLAG && entry2Method == FILE_UNCOMPRESS_METHOD_FLAG) {
303 bool isRunnableFile1 = FileUtils::IsRunnableFile(entry1FileName);
304 bool isRunnableFile2 = FileUtils::IsRunnableFile(entry2FileName);
305 if (isRunnableFile1 && isRunnableFile2) {
306 return entry1FileName < entry2FileName;
307 } else if (isRunnableFile1) {
308 return true;
309 } else if (isRunnableFile2) {
310 return false;
311 }
312 } else if (entry1Method == FILE_UNCOMPRESS_METHOD_FLAG) {
313 return true;
314 } else if (entry2Method == FILE_UNCOMPRESS_METHOD_FLAG) {
315 return false;
316 }
317 return entry1FileName < entry2FileName;
318 });
319 ResetOffset();
320 }
321
ResetOffset()322 void ZipSigner::ResetOffset()
323 {
324 uint32_t offset = 0U;
325 uint32_t cdLength = 0U;
326 for (const auto& entry : m_zipEntries) {
327 entry->GetCentralDirectory()->SetOffset(offset);
328 offset += entry->GetZipEntryData()->GetLength();
329 cdLength += entry->GetCentralDirectory()->GetLength();
330 }
331 if (!m_signingBlock.empty()) {
332 offset += m_signingBlock.size();
333 }
334 m_cDOffset = offset;
335 m_endOfCentralDirectory->SetOffset(offset);
336 m_endOfCentralDirectory->SetcDSize(cdLength);
337 offset += cdLength;
338 m_eOCDOffset = offset;
339 }
340
GetZipEntries()341 std::vector<ZipEntry*>& ZipSigner::GetZipEntries()
342 {
343 return m_zipEntries;
344 }
345
SetZipEntries(const std::vector<ZipEntry * > & zipEntries)346 void ZipSigner::SetZipEntries(const std::vector<ZipEntry*>& zipEntries)
347 {
348 m_zipEntries = zipEntries;
349 }
350
GetSigningOffset()351 uint32_t ZipSigner::GetSigningOffset()
352 {
353 return m_signingOffset;
354 }
355
SetSigningOffset(uint32_t signingOffset)356 void ZipSigner::SetSigningOffset(uint32_t signingOffset)
357 {
358 m_signingOffset = signingOffset;
359 }
360
GetSigningBlock()361 std::string ZipSigner::GetSigningBlock()
362 {
363 return m_signingBlock;
364 }
365
SetSigningBlock(const std::string & signingBlock)366 void ZipSigner::SetSigningBlock(const std::string& signingBlock)
367 {
368 m_signingBlock = signingBlock;
369 }
370
GetCDOffset()371 uint32_t ZipSigner::GetCDOffset()
372 {
373 return m_cDOffset;
374 }
375
SetCDOffset(uint32_t cDOffset)376 void ZipSigner::SetCDOffset(uint32_t cDOffset)
377 {
378 m_cDOffset = cDOffset;
379 }
380
GetEOCDOffset()381 uint32_t ZipSigner::GetEOCDOffset()
382 {
383 return m_eOCDOffset;
384 }
385
SetEOCDOffset(uint32_t eOCDOffset)386 void ZipSigner::SetEOCDOffset(uint32_t eOCDOffset)
387 {
388 m_eOCDOffset = eOCDOffset;
389 }
390
GetEndOfCentralDirectory()391 EndOfCentralDirectory* ZipSigner::GetEndOfCentralDirectory()
392 {
393 return m_endOfCentralDirectory;
394 }
395
SetEndOfCentralDirectory(EndOfCentralDirectory * endOfCentralDirectory)396 void ZipSigner::SetEndOfCentralDirectory(EndOfCentralDirectory* endOfCentralDirectory)
397 {
398 m_endOfCentralDirectory = endOfCentralDirectory;
399 }
400 } // namespace SignatureTools
401 } // namespace OHOS