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 <fstream>
17#include <filesystem>
18
19#include "constant.h"
20#include "file_utils.h"
21#include "sign_head.h"
22#include "block_head.h"
23#include "verify_hap.h"
24#include "verify_code_signature.h"
25#include "hash_utils.h"
26#include "sign_content_info.h"
27#include "signature_block_tags.h"
28#include "verify_elf.h"
29
30namespace OHOS {
31namespace SignatureTools {
32
33const int8_t VerifyElf::SIGNATURE_BLOCK = 0;
34const int8_t VerifyElf::PROFILE_NOSIGNED_BLOCK = 1;
35const int8_t VerifyElf::PROFILE_SIGNED_BLOCK = 2;
36const int8_t VerifyElf::KEY_ROTATION_BLOCK = 3;
37const int8_t VerifyElf::CODESIGNING_BLOCK_TYPE = 3;
38
39bool VerifyElf::Verify(Options* options)
40{
41    // check param
42    if (options == nullptr) {
43        PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "Param options is null.");
44        return false;
45    }
46    if (!CheckParams(options)) {
47        SIGNATURE_TOOLS_LOGE("verify elf check params failed!");
48        return false;
49    }
50    std::string filePath = options->GetString(Options::IN_FILE);
51    bool checkSignFileFlag = CheckSignFile(filePath);
52    if (!checkSignFileFlag) {
53        SIGNATURE_TOOLS_LOGE("check input elf file %s failed!", filePath.c_str());
54        return false;
55    }
56    // verify elf
57    std::vector<int8_t> profileVec;
58    Pkcs7Context pkcs7Context;
59    bool verifyElfFileFlag = VerifyElfFile(filePath, profileVec, options, pkcs7Context);
60    if (!verifyElfFileFlag) {
61        SIGNATURE_TOOLS_LOGE("verify elf file %s failed!", filePath.c_str());
62        return false;
63    }
64    // write certificate and p7b file
65    VerifyHap hapVerify(false);
66    int32_t writeVerifyOutputFlag = hapVerify.WriteVerifyOutput(pkcs7Context, profileVec, options);
67    if (writeVerifyOutputFlag != RET_OK) {
68        SIGNATURE_TOOLS_LOGE("write elf output failed on verify elf!");
69        return false;
70    }
71    return true;
72}
73
74bool VerifyElf::VerifyElfFile(const std::string& elfFile, std::vector<int8_t>& profileVec,
75                              Options* options, Pkcs7Context& pkcs7Context)
76{
77    SignBlockInfo signBlockInfo(false);
78    bool getSignBlockInfoFlag = GetSignBlockInfo(elfFile, signBlockInfo, ELF);
79    if (!getSignBlockInfoFlag) {
80        SIGNATURE_TOOLS_LOGE("get signBlockInfo failed on verify elf %s", elfFile.c_str());
81        return false;
82    }
83    // verify profile
84    std::string profileJson;
85    bool verifyP7b = VerifyP7b(signBlockInfo.GetSignBlockMap(), options, pkcs7Context, profileVec, profileJson);
86    if (!verifyP7b) {
87        SIGNATURE_TOOLS_LOGE("verify profile failed on verify elf %s", elfFile.c_str());
88        return false;
89    }
90    // verify code sign
91    bool findFlag =
92        signBlockInfo.GetSignBlockMap().find(CODESIGNING_BLOCK_TYPE) != signBlockInfo.GetSignBlockMap().end();
93    if (findFlag) {
94        SigningBlock codesign = signBlockInfo.GetSignBlockMap().find(CODESIGNING_BLOCK_TYPE)->second;
95        bool verifyElfFlag = VerifyCodeSignature::VerifyElf(elfFile, codesign.GetOffset(), codesign.GetLength(),
96                                                            ELF, profileJson);
97        if (!verifyElfFlag) {
98            SIGNATURE_TOOLS_LOGE("code signing failed on verify elf %s", elfFile.c_str());
99            return false;
100        }
101    }
102    return true;
103}
104
105bool VerifyElf::VerifyP7b(std::unordered_map<int8_t, SigningBlock>& signBlockMap,
106                          Options* options, Pkcs7Context& pkcs7Context,
107                          std::vector<int8_t>& profileVec, std::string& profileJson)
108{
109    if (signBlockMap.find(PROFILE_NOSIGNED_BLOCK) != signBlockMap.end()) {
110        // verify unsigned profile
111        const std::vector<int8_t>& profileByte = signBlockMap.find(PROFILE_NOSIGNED_BLOCK)->second.GetValue();
112        std::string fromByteStr(profileByte.begin(), profileByte.end());
113        profileJson = fromByteStr;
114        profileVec = profileByte;
115        SIGNATURE_TOOLS_LOGW("profile is not signed.");
116    } else if (signBlockMap.find(PROFILE_SIGNED_BLOCK) != signBlockMap.end()) {
117        // verify signed profile
118        SigningBlock profileSign = signBlockMap.find(PROFILE_SIGNED_BLOCK)->second;
119        const std::vector<int8_t>& profileByte = profileSign.GetValue();
120        bool getRawContentFlag = GetRawContent(profileByte, profileJson);
121        if (!getRawContentFlag) {
122            SIGNATURE_TOOLS_LOGE("get profile content failed on verify elf!");
123            return false;
124        }
125        VerifyHap hapVerify(false);
126        std::unique_ptr<ByteBuffer> profileBuffer =
127            std::make_unique<ByteBuffer>((char*)profileByte.data(), profileByte.size());
128        bool resultFlag = hapVerify.VerifyAppPkcs7(pkcs7Context, *profileBuffer);
129        if (!resultFlag) {
130            SIGNATURE_TOOLS_LOGE("verify elf profile failed on verify elf!");
131            return false;
132        }
133        profileVec = profileByte;
134        SIGNATURE_TOOLS_LOGI("verify profile success.");
135    } else {
136        SIGNATURE_TOOLS_LOGW("can not found profile sign block.");
137    }
138    return true;
139}
140
141bool VerifyElf::GetSignBlockInfo(const std::string& file, SignBlockInfo& signBlockInfo,
142                                 const std::string fileType)
143{
144    // read file
145    std::uintmax_t fileSize = std::filesystem::file_size(file);
146    std::ifstream fileStream(file, std::ios::binary);
147    if (!fileStream.is_open()) {
148        PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "open file: " + file + "failed");
149        return false;
150    }
151    std::vector<char>* fileBytes = new std::vector<char>(fileSize, 0);
152    fileStream.read(fileBytes->data(), fileBytes->size());
153    if (fileStream.fail() && !fileStream.eof()) {
154        PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "Error occurred while reading data");
155        fileStream.close();
156        delete fileBytes;
157        return false;
158    }
159    fileStream.close();
160    // get BlockData
161    BlockData blockData(0, 0);
162    bool getSignBlockData = GetSignBlockData(*((std::vector<int8_t>*)fileBytes), blockData, fileType);
163    if (!getSignBlockData) {
164        SIGNATURE_TOOLS_LOGE("get signBlockData failed on verify elf/bin file %s", file.c_str());
165        delete fileBytes;
166        return false;
167    }
168    // get SignBlockMap
169    if (fileType == ELF) {
170        GetElfSignBlock(*((std::vector<int8_t>*)fileBytes), blockData, signBlockInfo.GetSignBlockMap());
171    } else {
172        GetBinSignBlock(*((std::vector<int8_t>*)fileBytes), blockData, signBlockInfo.GetSignBlockMap());
173    }
174    // get bin file digest
175    bool needGenerateDigest = signBlockInfo.GetNeedGenerateDigest();
176    if (needGenerateDigest) {
177        const std::vector<int8_t>& signatrue = signBlockInfo.GetSignBlockMap().find(0)->second.GetValue();
178        bool getFileDigest = GetFileDigest(*((std::vector<int8_t>*)fileBytes), signatrue, signBlockInfo);
179        if (!getFileDigest) {
180            SIGNATURE_TOOLS_LOGE("getFileDigest failed on verify bin file %s", file.c_str());
181            delete fileBytes;
182            return false;
183        }
184    }
185    delete fileBytes;
186    return true;
187}
188
189bool VerifyElf::GetFileDigest(std::vector<int8_t>& fileBytes, const std::vector<int8_t>& signatrue,
190                              SignBlockInfo& signBlockInfo)
191{
192    std::string binDigest;
193    bool getRawContentFlag = GetRawContent(signatrue, binDigest);
194    if (!getRawContentFlag) {
195        SIGNATURE_TOOLS_LOGE("getBinDigest failed on verify bin digest!");
196        return false;
197    }
198    std::vector<int8_t> rawDigest(binDigest.begin(), binDigest.end());
199    signBlockInfo.SetRawDigest(rawDigest);
200    GenerateFileDigest(fileBytes, signBlockInfo);
201    return true;
202}
203
204bool VerifyElf::GenerateFileDigest(std::vector<int8_t>& fileBytes, SignBlockInfo& signBlockInfo)
205{
206    // get algId
207    std::vector<int8_t>& rawDigest = signBlockInfo.GetRawDigest();
208    std::unique_ptr<ByteBuffer> digBuffer = std::make_unique<ByteBuffer>(rawDigest.size());
209    digBuffer->PutData(rawDigest.data(), rawDigest.size());
210    digBuffer->Flip();
211    int32_t algOffset = 10;
212    int16_t algId = 0;
213    const char* bufferPtr = digBuffer->GetBufferPtr();
214    algId = static_cast<int16_t>(be16toh(*reinterpret_cast<const int16_t*>(bufferPtr + algOffset)));
215    // generate digest
216    int32_t fileLength = signBlockInfo.GetSignBlockMap().find(0)->second.GetOffset();
217    std::string digAlg = HashUtils::GetHashAlgName(algId);
218    std::vector<int8_t> generatedDig = HashUtils::GetDigestFromBytes(fileBytes, fileLength, digAlg);
219    if (generatedDig.empty()) {
220        SIGNATURE_TOOLS_LOGE("generate bin file digest failed on verify bin");
221        return false;
222    }
223    SignContentInfo contentInfo;
224    contentInfo.AddContentHashData(0, SignatureBlockTags::HASH_ROOT_4K, algId, generatedDig.size(), generatedDig);
225    std::vector<int8_t> dig = contentInfo.GetByteContent();
226    if (dig.empty()) {
227        SIGNATURE_TOOLS_LOGE("generate file digest is null on verify bin");
228        return false;
229    }
230    signBlockInfo.SetFileDigest(dig);
231    return true;
232}
233
234bool VerifyElf::GetSignBlockData(std::vector<int8_t>& bytes, BlockData& blockData,
235                                 const std::string fileType)
236{
237    int64_t offset = 0;
238    bool checkMagicAndVersionFlag = CheckMagicAndVersion(bytes, offset, fileType);
239    if (!checkMagicAndVersionFlag) {
240        SIGNATURE_TOOLS_LOGE("check magic and version failed, file type: %s", fileType.c_str());
241        return false;
242    }
243    int32_t intByteLength = 4;
244    std::vector<int8_t> blockSizeByte(bytes.begin() + offset, bytes.begin() + offset + intByteLength);
245    offset += intByteLength;
246    std::vector<int8_t> blockNumByte(bytes.begin() + offset, bytes.begin() + offset + intByteLength);
247    if (fileType == BIN) {
248        std::reverse(blockSizeByte.begin(), blockSizeByte.end());
249        std::reverse(blockNumByte.begin(), blockNumByte.end());
250    }
251    std::unique_ptr<ByteBuffer> blockNumBf = std::make_unique<ByteBuffer>(blockNumByte.size());
252    blockNumBf->PutData(blockNumByte.data(), blockNumByte.size());
253    blockNumBf->Flip();
254    int32_t blockNum = 0;
255    blockNumBf->GetInt32(blockNum);
256    std::unique_ptr<ByteBuffer> blockSizeBf = std::make_unique<ByteBuffer>(blockSizeByte.size());
257    blockSizeBf->PutData(blockSizeByte.data(), blockSizeByte.size());
258    blockSizeBf->Flip();
259    int32_t blockSize = 0;
260    blockSizeBf->GetInt32(blockSize);
261    int64_t blockStart = 0;
262    if (fileType == BIN) {
263        blockStart = bytes.size() - blockSize;
264    } else {
265        blockStart = bytes.size() - SignHead::SIGN_HEAD_LEN - blockSize;
266    }
267    blockData.SetBlockNum(blockNum);
268    blockData.SetBlockStart(blockStart);
269    return true;
270}
271
272bool VerifyElf::CheckMagicAndVersion(std::vector<int8_t>& bytes, int64_t& offset, const std::string fileType)
273{
274    std::string magicStr = (fileType == ELF ? SignHead::ELF_MAGIC : SignHead::MAGIC);
275    offset = bytes.size() - SignHead::SIGN_HEAD_LEN;
276    std::vector<int8_t> magicByte(bytes.begin() + offset, bytes.begin() + offset + magicStr.size());
277    offset += magicStr.size();
278    std::vector<int8_t> versionByte(bytes.begin() + offset, bytes.begin() + offset + SignHead::VERSION.size());
279    offset += SignHead::VERSION.size();
280    std::vector<int8_t> magicVec(magicStr.begin(), magicStr.end());
281    for (int i = 0; i < magicStr.size(); i++) {
282        if (magicVec[i] != magicByte[i]) {
283            PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "magic verify failed!");
284            return false;
285        }
286    }
287    std::vector<int8_t> versionVec(SignHead::VERSION.begin(), SignHead::VERSION.end());
288    for (int i = 0; i < SignHead::VERSION.size(); i++) {
289        if (versionVec[i] != versionByte[i]) {
290            PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "sign version verify failed!");
291            return false;
292        }
293    }
294    return true;
295}
296
297void VerifyElf::GetElfSignBlock(std::vector<int8_t>& bytes, BlockData& blockData,
298                                std::unordered_map<int8_t, SigningBlock>& signBlockMap)
299{
300    int32_t headBlockLen = SignHead::ELF_BLOCK_LEN;
301    int64_t offset = blockData.GetBlockStart();
302    for (int i = 0; i < blockData.GetBlockNum(); i++) {
303        std::vector<int8_t> blockByte(bytes.begin() + offset, bytes.begin() + offset + headBlockLen);
304        std::unique_ptr<ByteBuffer> blockBuffer = std::make_unique<ByteBuffer>(blockByte.size());
305        blockBuffer->PutData(blockByte.data(), blockByte.size());
306        blockBuffer->Flip();
307        int8_t type = 0;
308        int8_t tag = 0;
309        int16_t empValue = 0;
310        int32_t length = 0;
311        int32_t blockOffset = 0;
312        blockBuffer->GetByte((int8_t*)&type, sizeof(int8_t));
313        blockBuffer->GetByte((int8_t*)&tag, sizeof(int8_t));
314        blockBuffer->GetInt16(empValue);
315        blockBuffer->GetInt32(length);
316        blockBuffer->GetInt32(blockOffset);
317        std::vector<int8_t> value(bytes.begin() + blockData.GetBlockStart() + blockOffset,
318                                  bytes.begin() + blockData.GetBlockStart() + blockOffset + length);
319        SigningBlock signingBlock(type, value, blockData.GetBlockStart() + blockOffset);
320        signBlockMap.insert(std::make_pair(type, signingBlock));
321        offset += headBlockLen;
322    }
323}
324
325void VerifyElf::GetBinSignBlock(std::vector<int8_t>& bytes, BlockData& blockData,
326                                std::unordered_map<int8_t, SigningBlock>& signBlockMap)
327{
328    int32_t headBlockLen = SignHead::BIN_BLOCK_LEN;
329    int32_t offset = blockData.GetBlockStart();
330    for (int i = 0; i < blockData.GetBlockNum(); i++) {
331        std::vector<int8_t> blockByte(bytes.begin() + offset, bytes.begin() + offset + headBlockLen);
332        std::unique_ptr<ByteBuffer> blockBuffer = std::make_unique<ByteBuffer>(blockByte.size());
333        blockBuffer->PutData(blockByte.data(), blockByte.size());
334        blockBuffer->Flip();
335        int8_t type = 0;
336        int8_t tag = 0;
337        int16_t length = 0;
338        int32_t blockOffset = 0;
339        blockBuffer->GetByte((int8_t*)&type, sizeof(int8_t));
340        blockBuffer->GetByte((int8_t*)&tag, sizeof(int8_t));
341        const char* bufferPtr = blockBuffer->GetBufferPtr();
342        int bfLengthIdx = 2;
343        int bfBlockIdx = 4;
344        length = static_cast<int16_t>(be16toh(*reinterpret_cast<const int16_t*>(bufferPtr + bfLengthIdx)));
345        blockOffset = static_cast<int32_t>(be32toh(*reinterpret_cast<const int32_t*>(bufferPtr + bfBlockIdx)));
346        if (length == 0) {
347            length = bytes.size() - SignHead::SIGN_HEAD_LEN - blockOffset;
348        }
349        std::vector<int8_t> value(bytes.begin() + blockOffset, bytes.begin() + blockOffset + length);
350        SigningBlock signingBlock(type, value, blockOffset);
351        signBlockMap.insert(std::make_pair(type, signingBlock));
352        offset += headBlockLen;
353    }
354}
355
356bool VerifyElf::CheckParams(Options* options)
357{
358    bool certEmpty = options->GetString(Options::OUT_CERT_CHAIN).empty();
359    if (certEmpty) {
360        PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "Missing parameter: " + Options::OUT_CERT_CHAIN + "s.");
361        return false;
362    }
363    bool profileEmpty = options->GetString(Options::OUT_PROFILE).empty();
364    if (profileEmpty) {
365        PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "Missing parameter: " + Options::OUT_PROFILE + "s.");
366        return false;
367    }
368    bool proofEmpty = options->GetString(Options::PROOF_FILE).empty();
369    if (proofEmpty) {
370        SIGNATURE_TOOLS_LOGW("Missing parameter: %s.",
371                             Options::PROOF_FILE.c_str());
372    }
373    return true;
374}
375
376bool VerifyElf::CheckSignFile(const std::string& signedFile)
377{
378    if (signedFile.empty()) {
379        PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "Not found verify file path " + signedFile);
380        return false;
381    }
382    bool validFlag = FileUtils::IsValidFile(signedFile);
383    if (!validFlag) {
384        SIGNATURE_TOOLS_LOGE("signed file is invalid.");
385        return false;
386    }
387    return true;
388}
389
390bool VerifyElf::GetRawContent(const std::vector<int8_t>& contentVec, std::string& rawContent)
391{
392    PKCS7Data p7Data;
393    int parseFlag = p7Data.Parse(contentVec);
394    if (parseFlag < 0) {
395        SIGNATURE_TOOLS_LOGE("parse content failed!");
396        return false;
397    }
398    int verifyFlag = p7Data.Verify();
399    if (verifyFlag < 0) {
400        SIGNATURE_TOOLS_LOGE("verify content failed!");
401        return false;
402    }
403    int getContentFlag = p7Data.GetContent(rawContent);
404    if (getContentFlag < 0) {
405        SIGNATURE_TOOLS_LOGE("get p7Data raw content failed!");
406        return false;
407    }
408    return true;
409}
410
411} // namespace SignatureTools
412} // namespace OHOS