• 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 #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 = std::accumulate(certStr.begin(), certStr.end(), std::string(),
132         [](std::string sum, const std::string& certstr) { return sum + certstr; });
133     if (FileUtils::Write(outPutCertChainContent, outPutPath) < 0) {
134         SIGNATURE_TOOLS_LOGE("certChain write to file falied!\n");
135         return false;
136     }
137     return true;
138 }
139 
PrintCertChainToCmd(std::vector<X509 * > & certChain)140 bool VerifyHap::PrintCertChainToCmd(std::vector<X509*>& certChain)
141 {
142     BIO* outFd = BIO_new_fp(stdout, BIO_NOCLOSE);
143     if (!outFd) {
144         PrintErrorNumberMsg("IO_ERROR", IO_ERROR, "The stdout stream may have errors");
145         return false;
146     }
147     uint64_t format = XN_FLAG_SEP_COMMA_PLUS; // Print according to RFC2253
148     uint64_t content = X509_FLAG_NO_EXTENSIONS | X509_FLAG_NO_ATTRIBUTES | X509_FLAG_NO_HEADER | X509_FLAG_NO_SIGDUMP;
149     int num = 0;
150     for (auto& cert : certChain) {
151         PrintMsg("+++++++++++++++++++++++++++++++++certificate #" + std::to_string(num) +
152                  "+++++++++++++++++++++++++++++++++++++");
153         if (!X509_print_ex(outFd, cert, format, content)) {
154             VerifyHapOpensslUtils::GetOpensslErrorMessage();
155             SIGNATURE_TOOLS_LOGE("print x509 cert to cmd failed");
156             BIO_free(outFd);
157             return false;
158         }
159         ++num;
160     }
161     BIO_free(outFd);
162     return true;
163 }
164 
Verify(const std::string & filePath,Options * options)165 int32_t VerifyHap::Verify(const std::string& filePath, Options* options)
166 {
167     SIGNATURE_TOOLS_LOGD("Start Verify");
168     std::string standardFilePath;
169     if (!CheckFilePath(filePath, standardFilePath)) {
170         SIGNATURE_TOOLS_LOGE("Check file path%s failed", filePath.c_str());
171         return IO_ERROR;
172     }
173     RandomAccessFile hapFile;
174     if (!hapFile.Init(standardFilePath)) {
175         SIGNATURE_TOOLS_LOGE("%s init failed", standardFilePath.c_str());
176         return ZIP_ERROR;
177     }
178     int32_t resultCode = Verify(hapFile, options, filePath);
179     if (resultCode != RET_OK) {
180         PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, standardFilePath + " verify failed");
181     }
182     return resultCode;
183 }
184 
CheckFilePath(const std::string & filePath,std::string & standardFilePath)185 bool VerifyHap::CheckFilePath(const std::string& filePath, std::string& standardFilePath)
186 {
187     char path[PATH_MAX] = { 0x00 };
188     if (filePath.size() > PATH_MAX || realpath(filePath.c_str(), path) == nullptr) {
189         PrintErrorNumberMsg("IO_ERROR", IO_ERROR,
190                             filePath + " does not exist or is over " + std::to_string(PATH_MAX) + " chars");
191         return false;
192     }
193     standardFilePath = std::string(path);
194     std::string standardFilePathTmp = std::string(path);
195     std::transform(standardFilePathTmp.begin(), standardFilePathTmp.end(), standardFilePathTmp.begin(),
196                    [](unsigned char c) { return std::tolower(c); });
197     bool ret = (!std::regex_match(standardFilePathTmp, std::regex(HAP_APP_PATTERN)) &&
198                 !std::regex_match(standardFilePathTmp, std::regex(HSP_APP_PATTERN)) &&
199                 !std::regex_match(standardFilePathTmp, std::regex(APP_APP_PATTERN)) &&
200                 !std::regex_match(standardFilePathTmp, std::regex(HQF_APP_PATTERN)));
201     if (ret) {
202         PrintErrorNumberMsg("COMMAND_PARAM_ERROR", COMMAND_PARAM_ERROR,
203                             "only support format is [hap, hqf, hsp, app]");
204         return false;
205     }
206     return true;
207 }
208 
Verify(RandomAccessFile & hapFile,Options * options,const std::string & filePath)209 int32_t VerifyHap::Verify(RandomAccessFile& hapFile, Options* options, const std::string& filePath)
210 {
211     SignatureInfo hapSignInfo;
212     if (!HapSignerBlockUtils::FindHapSignature(hapFile, hapSignInfo)) {
213         return ZIP_ERROR;
214     }
215 
216     if (CheckCodeSign(filePath, hapSignInfo.optionBlocks) == false) {
217         SIGNATURE_TOOLS_LOGE("check coode sign failed\n");
218         return VERIFY_ERROR;
219     }
220 
221     Pkcs7Context pkcs7Context;
222     if (!VerifyAppPkcs7(pkcs7Context, hapSignInfo.hapSignatureBlock)) {
223         return PARSE_ERROR;
224     }
225 
226     if (!GetDigestAndAlgorithm(pkcs7Context)) {
227         SIGNATURE_TOOLS_LOGE("Get digest failed");
228         return PARSE_ERROR;
229     }
230 
231     STACK_OF(X509_CRL)* x509Crl = nullptr;
232     if (!VerifyHapOpensslUtils::GetCrlStack(pkcs7Context.p7, x509Crl)) {
233         SIGNATURE_TOOLS_LOGE("Get Crl stack failed");
234         return PARSE_ERROR;
235     }
236 
237     if (!VerifyCertOpensslUtils::VerifyCrl(pkcs7Context.certChain[0], x509Crl, pkcs7Context)) {
238         SIGNATURE_TOOLS_LOGE("Verify Crl stack failed");
239         return VERIFY_ERROR;
240     }
241 
242     if (!HapSignerBlockUtils::VerifyHapIntegrity(pkcs7Context, hapFile, hapSignInfo)) {
243         SIGNATURE_TOOLS_LOGE("Verify Integrity failed");
244         return VERIFY_ERROR;
245     }
246     if (!HapOutPutCertChain(pkcs7Context.certChain[0],
247         options->GetString(Options::OUT_CERT_CHAIN))) {
248         SIGNATURE_TOOLS_LOGE("out put cert chain failed");
249         return IO_ERROR;
250     }
251 
252     if (!outputOptionalBlocks(options->GetString(ParamConstants::PARAM_VERIFY_PROFILE_FILE),
253                               options->GetString(ParamConstants::PARAM_VERIFY_PROOF_FILE),
254                               options->GetString(ParamConstants::PARAM_VERIFY_PROPERTY_FILE),
255                               hapSignInfo.optionBlocks)) {
256         SIGNATURE_TOOLS_LOGE("output Optional Blocks failed");
257         return IO_ERROR;
258     }
259     return RET_OK;
260 }
261 
CheckCodeSign(const std::string & hapFilePath,const std::vector<OptionalBlock> & optionalBlocks) const262 bool VerifyHap::CheckCodeSign(const std::string& hapFilePath,
263                               const std::vector<OptionalBlock>& optionalBlocks)const
264 {
265     std::unordered_map<int, ByteBuffer> map;
266     for (const OptionalBlock& block : optionalBlocks) {
267         map.emplace(block.optionalType, block.optionalBlockValue);
268     }
269     bool codeSignFlag = map.find(HapUtils::HAP_PROPERTY_BLOCK_ID) != map.end() &&
270         map[HapUtils::HAP_PROPERTY_BLOCK_ID].GetCapacity() > 0;
271     if (codeSignFlag) {
272         ByteBuffer propertyBlockArray = map[HapUtils::HAP_PROPERTY_BLOCK_ID];
273         std::vector<std::string> fileNameArray = StringUtils::SplitString(hapFilePath, '.');
274         if (fileNameArray.size() < ParamConstants::FILE_NAME_MIN_LENGTH) {
275             PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "ZIP64 format not supported.");
276             return false;
277         }
278 
279         if (propertyBlockArray.GetCapacity() < ZIP_HEAD_OF_SUBSIGNING_BLOCK_LENGTH)
280             return false;
281         uint32_t blockType;
282         propertyBlockArray.GetUInt32(OFFSET_ZERO, blockType);
283         uint32_t blockLength;
284         propertyBlockArray.GetUInt32(OFFSET_FOUR, blockLength);
285         uint32_t blockOffset;
286         propertyBlockArray.GetUInt32(OFFSET_EIGHT, blockOffset);
287 
288         if (blockType != HapUtils::HAP_CODE_SIGN_BLOCK_ID) {
289             PrintErrorNumberMsg("VERIFY_ERROR", VERIFY_ERROR, "code sign data not exist in hap " + hapFilePath);
290             return false;
291         }
292         auto ite = map.find(HapUtils::HAP_PROFILE_BLOCK_ID);
293         if (ite == map.end())
294             return false;
295         ByteBuffer profileArray = ite->second;
296         std::string profileArray_(profileArray.GetBufferPtr(), profileArray.GetCapacity());
297         std::string profileContent;
298         if (GetProfileContent(profileArray_, profileContent) < 0) {
299             SIGNATURE_TOOLS_LOGE("get profile content failed, file: %s", hapFilePath.c_str());
300             return false;
301         }
302         std::string suffix = fileNameArray[fileNameArray.size() - 1];
303         bool isCodeSign = VerifyCodeSignature::VerifyHap(hapFilePath, blockOffset, blockLength,
304                                                          suffix, profileContent);
305         if (!isCodeSign) {
306             SIGNATURE_TOOLS_LOGE("verify codesign failed, file: %s", hapFilePath.c_str());
307             return false;
308         }
309         SIGNATURE_TOOLS_LOGI("verify codesign success.");
310         return true;
311     }
312     SIGNATURE_TOOLS_LOGI("can not find codesign block.");
313     return true;
314 }
315 
GetProfileContent(const std::string profile,std::string & ret)316 int VerifyHap::GetProfileContent(const std::string profile, std::string& ret)
317 {
318     json obj = json::parse(profile, nullptr, false);
319     if (!obj.is_discarded() && obj.is_structured()) {
320         ret = profile;
321         return 0;
322     }
323     PKCS7Data p7Data;
324     if (p7Data.Parse(profile) < 0) {
325         ret = profile;
326         return -1;
327     }
328     if (p7Data.Verify() < 0) {
329         PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR,
330                             "Verify profile pkcs7 failed! Profile is invalid");
331         ret = profile;
332         return -1;
333     }
334     if (p7Data.GetContent(ret) < 0) {
335         PrintErrorNumberMsg("PKCS7_VERIFY_ERROR", VERIFY_ERROR,
336                             "Check profile failed, signed profile content is not byte array");
337         ret = profile;
338         return -1;
339     }
340     return 0;
341 }
342 
VerifyAppPkcs7(Pkcs7Context & pkcs7Context,const ByteBuffer & hapSignatureBlock)343 bool VerifyHap::VerifyAppPkcs7(Pkcs7Context& pkcs7Context, const ByteBuffer& hapSignatureBlock)
344 {
345     const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(hapSignatureBlock.GetBufferPtr());
346     uint32_t pkcs7Len = static_cast<unsigned int>(hapSignatureBlock.GetCapacity());
347     if (!VerifyHapOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) {
348         SIGNATURE_TOOLS_LOGE("parse pkcs7 failed");
349         return false;
350     }
351     if (!VerifyHapOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) {
352         SIGNATURE_TOOLS_LOGE("GetCertChains from pkcs7 failed");
353         return false;
354     }
355     if (!VerifyHapOpensslUtils::VerifyPkcs7(pkcs7Context)) {
356         SIGNATURE_TOOLS_LOGE("verify signature failed");
357         return false;
358     }
359     return true;
360 }
361 
GetDigestAndAlgorithm(Pkcs7Context & digest)362 bool VerifyHap::GetDigestAndAlgorithm(Pkcs7Context& digest)
363 {
364     /*
365      * contentinfo format:
366      * int: version
367      * int: block number
368      * digest blocks:
369      * each digest block format:
370      * int: length of sizeof(digestblock) - 4
371      * int: Algorithm ID
372      * int: length of digest
373      * byte[]: digest
374      */
375      /* length of sizeof(digestblock - 4) */
376     int32_t digestBlockLen;
377     if (!digest.content.GetInt32(DIGEST_BLOCK_LEN_OFFSET, digestBlockLen)) {
378         SIGNATURE_TOOLS_LOGE("get digestBlockLen failed");
379         return false;
380     }
381     /* Algorithm ID */
382     if (!digest.content.GetInt32(DIGEST_ALGORITHM_OFFSET, digest.digestAlgorithm)) {
383         SIGNATURE_TOOLS_LOGE("get digestAlgorithm failed");
384         return false;
385     }
386     /* length of digest */
387     int32_t digestlen;
388     if (!digest.content.GetInt32(DIGEST_LEN_OFFSET, digestlen)) {
389         SIGNATURE_TOOLS_LOGE("get digestlen failed");
390         return false;
391     }
392     int32_t sum = sizeof(digestlen) + sizeof(digest.digestAlgorithm) + digestlen;
393     if (sum != digestBlockLen) {
394         SIGNATURE_TOOLS_LOGE("digestBlockLen: %d is not equal to sum: %d",
395                              digestBlockLen, sum);
396         return false;
397     }
398     /* set position to the digest start point */
399     digest.content.SetPosition(DIGEST_OFFSET_IN_CONTENT);
400     /* set limit to the digest end point */
401     digest.content.SetLimit(DIGEST_OFFSET_IN_CONTENT + digestlen);
402     digest.content.Slice();
403     return true;
404 }
405 
WriteVerifyOutput(Pkcs7Context & pkcs7Context,std::vector<int8_t> & profile,Options * options)406 int32_t VerifyHap::WriteVerifyOutput(Pkcs7Context& pkcs7Context, std::vector<int8_t>& profile, Options* options)
407 {
408     if (pkcs7Context.certChain.size() > 0) {
409         bool flag = VerifyHap::HapOutPutCertChain(pkcs7Context.certChain[0],
410             options->GetString(Options::OUT_CERT_CHAIN));
411         if (!flag) {
412             SIGNATURE_TOOLS_LOGE("out put cert chain failed");
413             return IO_ERROR;
414         }
415     }
416     if (pkcs7Context.p7 == nullptr) {
417         std::string p7bContent(profile.begin(), profile.end());
418         bool writeFlag = FileUtils::Write(p7bContent, options->GetString(Options::OUT_PROFILE)) < 0;
419         if (writeFlag) {
420             SIGNATURE_TOOLS_LOGE("p7b write to file falied!\n");
421             return IO_ERROR;
422         }
423         return RET_OK;
424     }
425     bool pkcs7flag = VerifyHap::HapOutPutPkcs7(pkcs7Context.p7, options->GetString(Options::OUT_PROFILE));
426     if (!pkcs7flag) {
427         SIGNATURE_TOOLS_LOGE("out put p7b failed");
428         return IO_ERROR;
429     }
430     return RET_OK;
431 }
432 } // namespace SignatureTools
433 } // namespace OHOS