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