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 #include <climits>
16 #include <cstdlib>
17 #include <regex>
18 #include <unordered_map>
19 #include <vector>
20 #include <algorithm>
21 
22 #include "securec.h"
23 #include "hap_signer_block_utils.h"
24 #include "signature_info.h"
25 #include "options.h"
26 #include "openssl/pem.h"
27 #include "pkcs7_data.h"
28 #include "hap_utils.h"
29 #include "string_utils.h"
30 #include "verify_code_signature.h"
31 #include "param_constants.h"
32 #include "file_utils.h"
33 #include "nlohmann/json.hpp"
34 #include "verify_hap.h"
35 
36 using namespace nlohmann;
37 namespace OHOS {
38 namespace SignatureTools {
39 const int32_t VerifyHap::HEX_PRINT_LENGTH = 3;
40 const int32_t VerifyHap::DIGEST_BLOCK_LEN_OFFSET = 8;
41 const int32_t VerifyHap::DIGEST_ALGORITHM_OFFSET = 12;
42 const int32_t VerifyHap::DIGEST_LEN_OFFSET = 16;
43 const int32_t VerifyHap::DIGEST_OFFSET_IN_CONTENT = 20;
44 const std::string VerifyHap::HAP_APP_PATTERN = "[^]*.hap$";
45 const std::string VerifyHap::HQF_APP_PATTERN = "[^]*.hqf$";
46 const std::string VerifyHap::HSP_APP_PATTERN = "[^]*.hsp$";
47 const std::string VerifyHap::APP_APP_PATTERN = "[^]*.app$";
48 static constexpr int ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH = 12;
49 
VerifyHap()50 VerifyHap::VerifyHap() : isPrintCert(true)
51 {
52 }
53 
VerifyHap(bool printCert)54 VerifyHap::VerifyHap(bool printCert)
55 {
56     isPrintCert = printCert;
57 }
58 
setIsPrintCert(bool printCert)59 void VerifyHap::setIsPrintCert(bool printCert)
60 {
61     isPrintCert = printCert;
62 }
63 
HapOutPutPkcs7(PKCS7* p7, const std::string& outPutPath)64 bool VerifyHap::HapOutPutPkcs7(PKCS7* p7, const std::string& outPutPath)
65 {
66     std::string p7bContent = StringUtils::Pkcs7ToString(p7);
67     if (p7bContent.empty()) {
68         SIGNATURE_TOOLS_LOGE("p7b to string failed!\n");
69         return false;
70     }
71     if (FileUtils::Write(p7bContent, outPutPath) < 0) {
72         SIGNATURE_TOOLS_LOGE("p7b write to file falied!\n");
73         return false;
74     }
75     return true;
76 }
77 
outputOptionalBlocks(const std::string& outputProfileFile, const std::string& outputProofFile, const std::string& outputPropertyFile, const std::vector<OptionalBlock>& optionBlocks)78 bool VerifyHap::outputOptionalBlocks(const std::string& outputProfileFile, const std::string& outputProofFile,
79                                      const std::string& outputPropertyFile,
80                                      const std::vector<OptionalBlock>& optionBlocks)
81 {
82     for (auto& optionBlock : optionBlocks) {
83         if (optionBlock.optionalType == HapUtils::HAP_PROFILE_BLOCK_ID) {
84             if (!writeOptionalBytesToFile(optionBlock, outputProfileFile)) {
85                 return false;
86             }
87         } else if (optionBlock.optionalType == HapUtils::HAP_PROPERTY_BLOCK_ID) {
88             if (!writeOptionalBytesToFile(optionBlock, outputPropertyFile)) {
89                 return false;
90             }
91         } else if (optionBlock.optionalType == HapUtils::HAP_PROOF_OF_ROTATION_BLOCK_ID) {
92             if (!writeOptionalBytesToFile(optionBlock, outputProofFile)) {
93                 return false;
94             }
95         } else {
96             SIGNATURE_TOOLS_LOGE("Unsupported Block Id: %d", optionBlock.optionalType);
97             return false;
98         }
99     }
100     return true;
101 }
writeOptionalBytesToFile(const OptionalBlock& optionalBlock, const std::string& path)102 bool VerifyHap::writeOptionalBytesToFile(const OptionalBlock& optionalBlock, const std::string& path)
103 {
104     if (path.empty()) {
105         return true;
106     }
107     std::string optionBlockString(optionalBlock.optionalBlockValue.GetBufferPtr(),
108                           optionalBlock.optionalBlockValue.GetCapacity());
109     if (FileUtils::Write(optionBlockString, path) < 0) {
110         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "write optional bytes to file:" + path + " falied!");
111         return false;
112     }
113     return true;
114 }
115 
HapOutPutCertChain(std::vector<X509*>& certs, const std::string& outPutPath)116 bool VerifyHap::HapOutPutCertChain(std::vector<X509*>& certs, const std::string& outPutPath)
117 {
118     if (isPrintCert) {
119         if (!PrintCertChainToCmd(certs)) {
120             SIGNATURE_TOOLS_LOGE("print cert chain to cmd failed\n");
121             return false;
122         }
123     }
124     VerifyHapOpensslUtils::GetOpensslErrorMessage();
125     SIGNATURE_TOOLS_LOGD("outPutPath = %s", outPutPath.c_str());
126     std::vector<std::string> certStr;
127     for (auto& cert : certs) {
128         certStr.emplace_back(StringUtils::SubjectToString(cert));
129         certStr.emplace_back(StringUtils::x509CertToString(cert));
130     }
131     std::string outPutCertChainContent;
132     for (auto& certstr : certStr) {
133         outPutCertChainContent += certstr;
134     }
135     if (FileUtils::Write(outPutCertChainContent, outPutPath) < 0) {
136         SIGNATURE_TOOLS_LOGE("certChain write to file falied!\n");
137         return false;
138     }
139     return true;
140 }
141 
Verify(const std::string& filePath, Options* options)142 int32_t VerifyHap::Verify(const std::string& filePath, Options* options)
143 {
144     SIGNATURE_TOOLS_LOGD("Start Verify");
145     std::string standardFilePath;
146     if (!CheckFilePath(filePath, standardFilePath)) {
147         SIGNATURE_TOOLS_LOGE("Check file path%s failed", filePath.c_str());
148         return IO_ERROR;
149     }
150     RandomAccessFile hapFile;
151     if (!hapFile.Init(standardFilePath)) {
152         SIGNATURE_TOOLS_LOGE("%s init failed", standardFilePath.c_str());
153         return ZIP_ERROR;
154     }
155     int32_t resultCode = Verify(hapFile, options, filePath);
156     if (resultCode != RET_OK) {
157         PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, standardFilePath + " verify failed");
158     }
159     return resultCode;
160 }
161 
CheckFilePath(const std::string& filePath, std::string& standardFilePath)162 bool VerifyHap::CheckFilePath(const std::string& filePath, std::string& standardFilePath)
163 {
164     char path[PATH_MAX] = { 0x00 };
165     if (filePath.size() > PATH_MAX || realpath(filePath.c_str(), path) == nullptr) {
166         PrintErrorNumberMsg("IO_ERROR", IO_ERROR,
167                             filePath + " does not exist or is over " + std::to_string(PATH_MAX) + " chars");
168         return false;
169     }
170     standardFilePath = std::string(path);
171     std::string standardFilePathTmp = std::string(path);
172     std::transform(standardFilePathTmp.begin(), standardFilePathTmp.end(), standardFilePathTmp.begin(),
173                    [](unsigned char c) { return std::tolower(c); });
174     bool ret = (!std::regex_match(standardFilePathTmp, std::regex(HAP_APP_PATTERN)) &&
175                 !std::regex_match(standardFilePathTmp, std::regex(HSP_APP_PATTERN)) &&
176                 !std::regex_match(standardFilePathTmp, std::regex(APP_APP_PATTERN)) &&
177                 !std::regex_match(standardFilePathTmp, std::regex(HQF_APP_PATTERN)));
178     if (ret) {
179         PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
180                             "only support format is [hap, hqf, hsp, app]");
181         return false;
182     }
183     return true;
184 }
185 
Verify(RandomAccessFile& hapFile, Options* options, const std::string& filePath)186 int32_t VerifyHap::Verify(RandomAccessFile& hapFile, Options* options, const std::string& filePath)
187 {
188     SignatureInfo hapSignInfo;
189     if (!HapSignerBlockUtils::FindHapSignature(hapFile, hapSignInfo)) {
190         return ZIP_ERROR;
191     }
192 
193     if (CheckCodeSign(filePath, hapSignInfo.optionBlocks) == false) {
194         SIGNATURE_TOOLS_LOGE("check coode sign failed\n");
195         return VERIFY_ERROR;
196     }
197 
198     Pkcs7Context pkcs7Context;
199     if (!VerifyAppPkcs7(pkcs7Context, hapSignInfo.hapSignatureBlock)) {
200         return PARSE_ERROR;
201     }
202 
203     if (!GetDigestAndAlgorithm(pkcs7Context)) {
204         SIGNATURE_TOOLS_LOGE("Get digest failed");
205         return PARSE_ERROR;
206     }
207 
208     STACK_OF(X509_CRL)* x509Crl = nullptr;
209     if (!VerifyHapOpensslUtils::GetCrlStack(pkcs7Context.p7, x509Crl)) {
210         SIGNATURE_TOOLS_LOGE("Get Crl stack failed");
211         return PARSE_ERROR;
212     }
213 
214     if (!VerifyCertOpensslUtils::VerifyCrl(pkcs7Context.certChain[0], x509Crl, pkcs7Context)) {
215         SIGNATURE_TOOLS_LOGE("Verify Crl stack failed");
216         return VERIFY_ERROR;
217     }
218 
219     if (!HapSignerBlockUtils::VerifyHapIntegrity(pkcs7Context, hapFile, hapSignInfo)) {
220         SIGNATURE_TOOLS_LOGE("Verify Integrity failed");
221         return VERIFY_ERROR;
222     }
223     if (!HapOutPutCertChain(pkcs7Context.certChain[0],
224         options->GetString(Options::OUT_CERT_CHAIN))) {
225         SIGNATURE_TOOLS_LOGE("out put cert chain failed");
226         return IO_ERROR;
227     }
228 
229     if (!outputOptionalBlocks(options->GetString(ParamConstants::PARAM_VERIFY_PROFILE_FILE),
230                               options->GetString(ParamConstants::PARAM_VERIFY_PROOF_FILE),
231                               options->GetString(ParamConstants::PARAM_VERIFY_PROPERTY_FILE),
232                               hapSignInfo.optionBlocks)) {
233         SIGNATURE_TOOLS_LOGE("output Optional Blocks failed");
234         return IO_ERROR;
235     }
236     return RET_OK;
237 }
238 
CheckCodeSign(const std::string& hapFilePath, const std::vector<OptionalBlock>& optionalBlocks) const239 bool VerifyHap::CheckCodeSign(const std::string& hapFilePath,
240                               const std::vector<OptionalBlock>& optionalBlocks)const
241 {
242     std::unordered_map<int, ByteBuffer> map;
243     for (const OptionalBlock& block : optionalBlocks) {
244         map.emplace(block.optionalType, block.optionalBlockValue);
245     }
246     bool codeSignFlag = map.find(HapUtils::HAP_PROPERTY_BLOCK_ID) != map.end() &&
247         map[HapUtils::HAP_PROPERTY_BLOCK_ID].GetCapacity() > 0;
248     if (codeSignFlag) {
249         ByteBuffer propertyBlockArray = map[HapUtils::HAP_PROPERTY_BLOCK_ID];
250         std::vector<std::string> fileNameArray = StringUtils::SplitString(hapFilePath, '.');
251         if (fileNameArray.size() < ParamConstants::FILE_NAME_MIN_LENGTH) {
252             PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "ZIP64 format not supported.");
253             return false;
254         }
255 
256         if (propertyBlockArray.GetCapacity() < ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH)
257             return false;
258         uint32_t blockType;
259         propertyBlockArray.GetUInt32(OFFSET_ZERO, blockType);
260         uint32_t blockLength;
261         propertyBlockArray.GetUInt32(OFFSET_FOUR, blockLength);
262         uint32_t blockOffset;
263         propertyBlockArray.GetUInt32(OFFSET_EIGHT, blockOffset);
264 
265         if (blockType != HapUtils::HAP_CODE_SIGN_BLOCK_ID) {
266             PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "code sign data not exist in hap " + hapFilePath);
267             return false;
268         }
269         auto ite = map.find(HapUtils::HAP_PROFILE_BLOCK_ID);
270         if (ite == map.end())
271             return false;
272         ByteBuffer profileArray = ite->second;
273         std::string profileArray_(profileArray.GetBufferPtr(), profileArray.GetCapacity());
274         std::string profileContent;
275         if (GetProfileContent(profileArray_, profileContent) < 0) {
276             SIGNATURE_TOOLS_LOGE("get profile content failed, file: %s", hapFilePath.c_str());
277             return false;
278         }
279         std::string suffix = fileNameArray[fileNameArray.size() - 1];
280         bool isCodeSign = VerifyCodeSignature::VerifyHap(hapFilePath, blockOffset, blockLength,
281                                                          suffix, profileContent);
282         if (!isCodeSign) {
283             SIGNATURE_TOOLS_LOGE("verify codesign failed, file: %s", hapFilePath.c_str());
284             return false;
285         }
286         SIGNATURE_TOOLS_LOGI("verify codesign success.");
287         return true;
288     }
289     SIGNATURE_TOOLS_LOGI("can not find codesign block.");
290     return true;
291 }
292 
GetProfileContent(const std::string profile, std::string& ret)293 int VerifyHap::GetProfileContent(const std::string profile, std::string& ret)
294 {
295     json obj = json::parse(profile, nullptr, false);
296     if (!obj.is_discarded() && obj.is_structured()) {
297         ret = profile;
298         return 0;
299     }
300     PKCS7Data p7Data;
301     if (p7Data.Parse(profile) < 0) {
302         ret = profile;
303         return -1;
304     }
305     if (p7Data.Verify() < 0) {
306         PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR,
307                             "Verify profile pkcs7 failed! Profile is invalid");
308         ret = profile;
309         return -1;
310     }
311     if (p7Data.GetContent(ret) < 0) {
312         PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR,
313                             "Check profile failed, signed profile content is not byte array");
314         ret = profile;
315         return -1;
316     }
317     return 0;
318 }
319 
VerifyAppPkcs7(Pkcs7Context& pkcs7Context, const ByteBuffer& hapSignatureBlock)320 bool VerifyHap::VerifyAppPkcs7(Pkcs7Context& pkcs7Context, const ByteBuffer& hapSignatureBlock)
321 {
322     const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(hapSignatureBlock.GetBufferPtr());
323     uint32_t pkcs7Len = static_cast<unsigned int>(hapSignatureBlock.GetCapacity());
324     if (!VerifyHapOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) {
325         SIGNATURE_TOOLS_LOGE("parse pkcs7 failed");
326         return false;
327     }
328     if (!VerifyHapOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) {
329         SIGNATURE_TOOLS_LOGE("GetCertChains from pkcs7 failed");
330         return false;
331     }
332     if (!VerifyHapOpensslUtils::VerifyPkcs7(pkcs7Context)) {
333         SIGNATURE_TOOLS_LOGE("verify signature failed");
334         return false;
335     }
336     return true;
337 }
338 
GetDigestAndAlgorithm(Pkcs7Context& digest)339 bool VerifyHap::GetDigestAndAlgorithm(Pkcs7Context& digest)
340 {
341     /*
342      * contentinfo format:
343      * int: version
344      * int: block number
345      * digest blocks:
346      * each digest block format:
347      * int: length of sizeof(digestblock) - 4
348      * int: Algorithm ID
349      * int: length of digest
350      * byte[]: digest
351      */
352      /* length of sizeof(digestblock - 4) */
353     int32_t digestBlockLen;
354     if (!digest.content.GetInt32(DIGEST_BLOCK_LEN_OFFSET, digestBlockLen)) {
355         SIGNATURE_TOOLS_LOGE("get digestBlockLen failed");
356         return false;
357     }
358     /* Algorithm ID */
359     if (!digest.content.GetInt32(DIGEST_ALGORITHM_OFFSET, digest.digestAlgorithm)) {
360         SIGNATURE_TOOLS_LOGE("get digestAlgorithm failed");
361         return false;
362     }
363     /* length of digest */
364     int32_t digestlen;
365     if (!digest.content.GetInt32(DIGEST_LEN_OFFSET, digestlen)) {
366         SIGNATURE_TOOLS_LOGE("get digestlen failed");
367         return false;
368     }
369     int32_t sum = sizeof(digestlen) + sizeof(digest.digestAlgorithm) + digestlen;
370     if (sum != digestBlockLen) {
371         SIGNATURE_TOOLS_LOGE("digestBlockLen: %d is not equal to sum: %d",
372                              digestBlockLen, sum);
373         return false;
374     }
375     /* set position to the digest start point */
376     digest.content.SetPosition(DIGEST_OFFSET_IN_CONTENT);
377     /* set limit to the digest end point */
378     digest.content.SetLimit(DIGEST_OFFSET_IN_CONTENT + digestlen);
379     digest.content.Slice();
380     return true;
381 }
382 
WriteVerifyOutput(Pkcs7Context& pkcs7Context, std::vector<int8_t>& profile, Options* options)383 int32_t VerifyHap::WriteVerifyOutput(Pkcs7Context& pkcs7Context, std::vector<int8_t>& profile, Options* options)
384 {
385     if (pkcs7Context.certChain.size() > 0) {
386         bool flag = VerifyHap::HapOutPutCertChain(pkcs7Context.certChain[0],
387             options->GetString(Options::OUT_CERT_CHAIN));
388         if (!flag) {
389             SIGNATURE_TOOLS_LOGE("out put cert chain failed");
390             return IO_ERROR;
391         }
392     }
393     if (pkcs7Context.p7 == nullptr) {
394         std::string p7bContent(profile.begin(), profile.end());
395         bool writeFlag = FileUtils::Write(p7bContent, options->GetString(Options::OUT_PROFILE)) < 0;
396         if (writeFlag) {
397             SIGNATURE_TOOLS_LOGE("p7b write to file falied!\n");
398             return IO_ERROR;
399         }
400         return RET_OK;
401     }
402     bool pkcs7flag = VerifyHap::HapOutPutPkcs7(pkcs7Context.p7, options->GetString(Options::OUT_PROFILE));
403     if (!pkcs7flag) {
404         SIGNATURE_TOOLS_LOGE("out put p7b failed");
405         return IO_ERROR;
406     }
407     return RET_OK;
408 }
409 
PrintCertChainToCmd(std::vector<X509*>& certChain)410 bool VerifyHap::PrintCertChainToCmd(std::vector<X509*>& certChain)
411 {
412     BIO* outFd = BIO_new_fp(stdout, BIO_NOCLOSE);
413     if (!outFd) {
414         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "The stdout stream may have errors");
415         return false;
416     }
417     uint64_t format = XN_FLAG_SEP_COMMA_PLUS; // Print according to RFC2253
418     uint64_t content = X509_FLAG_NO_EXTENSIONS | X509_FLAG_NO_ATTRIBUTES | X509_FLAG_NO_HEADER | X509_FLAG_NO_SIGDUMP;
419     int num = 0;
420     for (auto& cert : certChain) {
421         PrintMsg("+++++++++++++++++++++++++++++++++certificate #" + std::to_string(num) +
422                  "+++++++++++++++++++++++++++++++++++++");
423         if (!X509_print_ex(outFd, cert, format, content)) {
424             VerifyHapOpensslUtils::GetOpensslErrorMessage();
425             SIGNATURE_TOOLS_LOGE("print x509 cert to cmd failed");
426             BIO_free(outFd);
427             return false;
428         }
429         ++num;
430     }
431     BIO_free(outFd);
432     return true;
433 }
434 } // namespace SignatureTools
435 } // namespace OHOS