• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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