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 23namespace OHOS { 24namespace SignatureTools { 25bool 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 63EndOfCentralDirectory* 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 116bool 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 158std::string ZipSigner::GetSigningBlock(std::ifstream& file) 159{ 160 uint64_t size = m_cDOffset - 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 178bool 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 201bool 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 257void 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 288void ZipSigner::RemoveSignBlock() 289{ 290 m_signingBlock = std::string(); 291 ResetOffset(); 292} 293 294void 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 322void 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 341std::vector<ZipEntry*>& ZipSigner::GetZipEntries() 342{ 343 return m_zipEntries; 344} 345 346void ZipSigner::SetZipEntries(const std::vector<ZipEntry*>& zipEntries) 347{ 348 m_zipEntries = zipEntries; 349} 350 351uint32_t ZipSigner::GetSigningOffset() 352{ 353 return m_signingOffset; 354} 355 356void ZipSigner::SetSigningOffset(uint32_t signingOffset) 357{ 358 m_signingOffset = signingOffset; 359} 360 361std::string ZipSigner::GetSigningBlock() 362{ 363 return m_signingBlock; 364} 365 366void ZipSigner::SetSigningBlock(const std::string& signingBlock) 367{ 368 m_signingBlock = signingBlock; 369} 370 371uint32_t ZipSigner::GetCDOffset() 372{ 373 return m_cDOffset; 374} 375 376void ZipSigner::SetCDOffset(uint32_t cDOffset) 377{ 378 m_cDOffset = cDOffset; 379} 380 381uint32_t ZipSigner::GetEOCDOffset() 382{ 383 return m_eOCDOffset; 384} 385 386void ZipSigner::SetEOCDOffset(uint32_t eOCDOffset) 387{ 388 m_eOCDOffset = eOCDOffset; 389} 390 391EndOfCentralDirectory* ZipSigner::GetEndOfCentralDirectory() 392{ 393 return m_endOfCentralDirectory; 394} 395 396void ZipSigner::SetEndOfCentralDirectory(EndOfCentralDirectory* endOfCentralDirectory) 397{ 398 m_endOfCentralDirectory = endOfCentralDirectory; 399} 400} // namespace SignatureTools 401} // namespace OHOS