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