1 /*
2 * Copyright (C) 2021-2022 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 "verify/hap_verify_v2.h"
17
18 #include <climits>
19 #include <cstdlib>
20 #include <regex>
21
22 #include "securec.h"
23
24 #include "common/hap_verify_log.h"
25 #include "init/hap_crl_manager.h"
26 #include "init/trusted_source_manager.h"
27 #include "ticket/ticket_verify.h"
28 #include "util/hap_profile_verify_utils.h"
29 #include "util/hap_signing_block_utils.h"
30 #include "util/signature_info.h"
31
32 namespace OHOS {
33 namespace Security {
34 namespace Verify {
35 const int HapVerifyV2::HEX_PRINT_LENGTH = 3;
36 const int HapVerifyV2::DIGEST_BLOCK_LEN_OFFSET = 8;
37 const int HapVerifyV2::DIGEST_ALGORITHM_OFFSET = 12;
38 const int HapVerifyV2::DIGEST_LEN_OFFSET = 16;
39 const int HapVerifyV2::DIGEST_OFFSET_IN_CONTENT = 20;
40 const std::string HapVerifyV2::HAP_APP_PATTERN = "[^]*.hap$";
41 const std::string HapVerifyV2::HQF_APP_PATTERN = "[^]*.hqf$";
42 const std::string HapVerifyV2::HSP_APP_PATTERN = "[^]*.hsp$";
43
Verify(const std::string & filePath,HapVerifyResult & hapVerifyV1Result)44 int HapVerifyV2::Verify(const std::string& filePath, HapVerifyResult& hapVerifyV1Result)
45 {
46 HAPVERIFY_LOG_DEBUG(LABEL, "Start Verify");
47 std::string standardFilePath;
48 if (!CheckFilePath(filePath, standardFilePath)) {
49 return FILE_PATH_INVALID;
50 }
51
52 RandomAccessFile hapFile;
53 if (!hapFile.Init(standardFilePath)) {
54 HAPVERIFY_LOG_ERROR(LABEL, "open standard file failed");
55 return OPEN_FILE_ERROR;
56 }
57
58 int resultCode = Verify(hapFile, hapVerifyV1Result);
59 return resultCode;
60 }
61
CheckFilePath(const std::string & filePath,std::string & standardFilePath)62 bool HapVerifyV2::CheckFilePath(const std::string& filePath, std::string& standardFilePath)
63 {
64 char path[PATH_MAX + 1] = { 0x00 };
65 if (filePath.size() > PATH_MAX || realpath(filePath.c_str(), path) == nullptr) {
66 HAPVERIFY_LOG_ERROR(LABEL, "filePath is not a standard path");
67 return false;
68 }
69 standardFilePath = std::string(path);
70 if (!std::regex_match(standardFilePath, std::regex(HAP_APP_PATTERN)) &&
71 !std::regex_match(standardFilePath, std::regex(HSP_APP_PATTERN)) &&
72 !std::regex_match(standardFilePath, std::regex(HQF_APP_PATTERN))) {
73 HAPVERIFY_LOG_ERROR(LABEL, "file is not hap, hsp or hqf package");
74 return false;
75 }
76 return true;
77 }
78
Verify(RandomAccessFile & hapFile,HapVerifyResult & hapVerifyV1Result)79 int HapVerifyV2::Verify(RandomAccessFile& hapFile, HapVerifyResult& hapVerifyV1Result)
80 {
81 SignatureInfo hapSignInfo;
82 if (!HapSigningBlockUtils::FindHapSignature(hapFile, hapSignInfo)) {
83 return SIGNATURE_NOT_FOUND;
84 }
85 hapVerifyV1Result.SetVersion(hapSignInfo.version);
86 hapVerifyV1Result.SetPkcs7SignBlock(hapSignInfo.hapSignatureBlock);
87 hapVerifyV1Result.SetPkcs7ProfileBlock(hapSignInfo.hapSignatureBlock);
88 hapVerifyV1Result.SetOptionalBlocks(hapSignInfo.optionBlocks);
89 Pkcs7Context pkcs7Context;
90 if (!VerifyAppPkcs7(pkcs7Context, hapSignInfo.hapSignatureBlock)) {
91 return VERIFY_APP_PKCS7_FAIL;
92 }
93 int profileIndex = 0;
94 if (!HapSigningBlockUtils::GetOptionalBlockIndex(hapSignInfo.optionBlocks, PROFILE_BLOB, profileIndex)) {
95 return NO_PROFILE_BLOCK_FAIL;
96 }
97 bool profileNeedWriteCrl = false;
98 if (!VerifyAppSourceAndParseProfile(pkcs7Context, hapSignInfo.optionBlocks[profileIndex].optionalBlockValue,
99 hapVerifyV1Result, profileNeedWriteCrl)) {
100 HAPVERIFY_LOG_ERROR(LABEL, "APP source is not trusted");
101 return APP_SOURCE_NOT_TRUSTED;
102 }
103 if (!GetDigestAndAlgorithm(pkcs7Context)) {
104 HAPVERIFY_LOG_ERROR(LABEL, "Get digest failed");
105 return GET_DIGEST_FAIL;
106 }
107 std::vector<std::string> publicKeys;
108 if (!HapVerifyOpensslUtils::GetPublickeys(pkcs7Context.certChains[0], publicKeys)) {
109 HAPVERIFY_LOG_ERROR(LABEL, "Get publicKeys failed");
110 return GET_PUBLICKEY_FAIL;
111 }
112 hapVerifyV1Result.SetPublicKey(publicKeys);
113 std::vector<std::string> certSignatures;
114 if (!HapVerifyOpensslUtils::GetSignatures(pkcs7Context.certChains[0], certSignatures)) {
115 HAPVERIFY_LOG_ERROR(LABEL, "Get sianatures failed");
116 return GET_SIGNATURE_FAIL;
117 }
118 hapVerifyV1Result.SetSignature(certSignatures);
119 if (!HapSigningBlockUtils::VerifyHapIntegrity(pkcs7Context, hapFile, hapSignInfo)) {
120 HAPVERIFY_LOG_ERROR(LABEL, "Verify Integrity failed");
121 return VERIFY_INTEGRITY_FAIL;
122 }
123 WriteCrlIfNeed(pkcs7Context, profileNeedWriteCrl);
124 return VERIFY_SUCCESS;
125 }
126
VerifyAppPkcs7(Pkcs7Context & pkcs7Context,const HapByteBuffer & hapSignatureBlock)127 bool HapVerifyV2::VerifyAppPkcs7(Pkcs7Context& pkcs7Context, const HapByteBuffer& hapSignatureBlock)
128 {
129 const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(hapSignatureBlock.GetBufferPtr());
130 unsigned int pkcs7Len = static_cast<unsigned int>(hapSignatureBlock.GetCapacity());
131 if (!HapVerifyOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) {
132 HAPVERIFY_LOG_ERROR(LABEL, "parse pkcs7 failed");
133 return false;
134 }
135 if (!HapVerifyOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) {
136 HAPVERIFY_LOG_ERROR(LABEL, "GetCertChains from pkcs7 failed");
137 return false;
138 }
139 if (!HapVerifyOpensslUtils::VerifyPkcs7(pkcs7Context)) {
140 HAPVERIFY_LOG_ERROR(LABEL, "verify signature failed");
141 return false;
142 }
143 return true;
144 }
145
VerifyAppSourceAndParseProfile(Pkcs7Context & pkcs7Context,const HapByteBuffer & hapProfileBlock,HapVerifyResult & hapVerifyV1Result,bool & profileNeadWriteCrl)146 bool HapVerifyV2::VerifyAppSourceAndParseProfile(Pkcs7Context& pkcs7Context,
147 const HapByteBuffer& hapProfileBlock, HapVerifyResult& hapVerifyV1Result, bool& profileNeadWriteCrl)
148 {
149 std::string certSubject;
150 if (!HapCertVerifyOpensslUtils::GetSubjectFromX509(pkcs7Context.certChains[0][0], certSubject)) {
151 HAPVERIFY_LOG_ERROR(LABEL, "Get info of sign cert failed");
152 return false;
153 }
154 HAPVERIFY_LOG_INFO(LABEL, "App signature subject: %{public}s, issuer: %{public}s",
155 certSubject.c_str(), pkcs7Context.certIssuer.c_str());
156
157 TrustedSourceManager& trustedSourceManager = TrustedSourceManager::GetInstance();
158 pkcs7Context.matchResult = trustedSourceManager.IsTrustedSource(certSubject, pkcs7Context.certIssuer,
159 HAP_SIGN_BLOB, pkcs7Context.certChains[0].size());
160
161 Pkcs7Context profileContext;
162 std::string profile;
163 if (!HapProfileVerifyUtils::ParseProfile(profileContext, pkcs7Context, hapProfileBlock, profile)) {
164 HAPVERIFY_LOG_ERROR(LABEL, "Parse profile pkcs7 failed");
165 return false;
166 }
167
168 if (!VerifyProfileSignature(pkcs7Context, profileContext)) {
169 HAPVERIFY_LOG_ERROR(LABEL, "VerifyProfileSignature failed");
170 return false;
171 }
172 /*
173 * If app source is not trusted, verify profile.
174 * If profile is debug, check whether app signed cert is same as the debug cert in profile.
175 * If profile is release, do not allow installation of this app.
176 */
177 bool isCallParseAndVerify = false;
178 ProvisionInfo provisionInfo;
179 if (pkcs7Context.matchResult.matchState == DO_NOT_MATCH) {
180 if (!HapProfileVerifyUtils::VerifyProfile(profileContext)) {
181 HAPVERIFY_LOG_ERROR(LABEL, "profile verify failed");
182 return false;
183 }
184 AppProvisionVerifyResult profileRet = ParseAndVerify(profile, provisionInfo);
185 if (profileRet != PROVISION_OK) {
186 HAPVERIFY_LOG_ERROR(LABEL, "profile parsing failed, error: %{public}d", static_cast<int>(profileRet));
187 return false;
188 }
189 if (!VerifyProfileInfo(pkcs7Context, profileContext, provisionInfo)) {
190 HAPVERIFY_LOG_ERROR(LABEL, "VerifyProfileInfo failed");
191 return false;
192 }
193 isCallParseAndVerify = true;
194 }
195
196 if (!ParseAndVerifyProfileIfNeed(profile, provisionInfo, isCallParseAndVerify)) {
197 return false;
198 }
199
200 if (!GenerateAppId(provisionInfo) || !GenerateFingerprint(provisionInfo)) {
201 HAPVERIFY_LOG_ERROR(LABEL, "Generate appId or generate fingerprint failed");
202 return false;
203 }
204
205 hapVerifyV1Result.SetProvisionInfo(provisionInfo);
206 profileNeadWriteCrl = profileContext.needWriteCrl;
207 return true;
208 }
209
VerifyProfileSignature(const Pkcs7Context & pkcs7Context,Pkcs7Context & profileContext)210 bool HapVerifyV2::VerifyProfileSignature(const Pkcs7Context& pkcs7Context, Pkcs7Context& profileContext)
211 {
212 if (pkcs7Context.matchResult.matchState == MATCH_WITH_SIGN &&
213 pkcs7Context.matchResult.source == APP_THIRD_PARTY_PRELOAD) {
214 if (!HapProfileVerifyUtils::VerifyProfile(profileContext)) {
215 HAPVERIFY_LOG_ERROR(LABEL, "profile verify failed");
216 return false;
217 }
218 }
219 return true;
220 }
221
GenerateAppId(ProvisionInfo & provisionInfo)222 bool HapVerifyV2::GenerateAppId(ProvisionInfo& provisionInfo)
223 {
224 std::string& certInProfile = provisionInfo.bundleInfo.distributionCertificate;
225 if (provisionInfo.bundleInfo.distributionCertificate.empty()) {
226 certInProfile = provisionInfo.bundleInfo.developmentCertificate;
227 HAPVERIFY_LOG_DEBUG(LABEL, "use development Certificate");
228 }
229 std::string publicKey;
230 if (!HapCertVerifyOpensslUtils::GetPublickeyBase64FromPemCert(certInProfile, publicKey)) {
231 return false;
232 }
233 provisionInfo.appId = publicKey;
234 HAPVERIFY_LOG_DEBUG(LABEL, "provisionInfo.appId: %{public}s", provisionInfo.appId.c_str());
235 return true;
236 }
237
GenerateFingerprint(ProvisionInfo & provisionInfo)238 bool HapVerifyV2::GenerateFingerprint(ProvisionInfo& provisionInfo)
239 {
240 std::string& certInProfile = provisionInfo.bundleInfo.distributionCertificate;
241 if (provisionInfo.bundleInfo.distributionCertificate.empty()) {
242 certInProfile = provisionInfo.bundleInfo.developmentCertificate;
243 HAPVERIFY_LOG_DEBUG(LABEL, "use development Certificate");
244 }
245 std::string fingerprint;
246 if (!HapCertVerifyOpensslUtils::GetFingerprintBase64FromPemCert(certInProfile, fingerprint)) {
247 HAPVERIFY_LOG_ERROR(LABEL, "Generate fingerprint from pem certificate failed");
248 return false;
249 }
250 provisionInfo.fingerprint = fingerprint;
251 HAPVERIFY_LOG_DEBUG(LABEL, "fingerprint is : %{public}s", fingerprint.c_str());
252 return true;
253 }
254
VerifyProfileInfo(const Pkcs7Context & pkcs7Context,const Pkcs7Context & profileContext,ProvisionInfo & provisionInfo)255 bool HapVerifyV2::VerifyProfileInfo(const Pkcs7Context& pkcs7Context, const Pkcs7Context& profileContext,
256 ProvisionInfo& provisionInfo)
257 {
258 if (!CheckProfileSignatureIsRight(profileContext.matchResult.matchState, provisionInfo.type)) {
259 return false;
260 }
261 std::string& certInProfile = provisionInfo.bundleInfo.developmentCertificate;
262 if (provisionInfo.type == ProvisionType::RELEASE) {
263 if (!IsAppDistributedTypeAllowInstall(provisionInfo.distributionType, provisionInfo)) {
264 HAPVERIFY_LOG_ERROR(LABEL, "untrusted source app with release profile distributionType: %{public}d",
265 static_cast<int>(provisionInfo.distributionType));
266 return false;
267 }
268 certInProfile = provisionInfo.bundleInfo.distributionCertificate;
269 HAPVERIFY_LOG_DEBUG(LABEL, "allow install app with release profile distributionType: %{public}d",
270 static_cast<int>(provisionInfo.distributionType));
271 }
272 HAPVERIFY_LOG_DEBUG(LABEL, "provisionInfo.type: %{public}d", static_cast<int>(provisionInfo.type));
273 if (!HapCertVerifyOpensslUtils::CompareX509Cert(pkcs7Context.certChains[0][0], certInProfile)) {
274 HAPVERIFY_LOG_ERROR(LABEL, "developed cert is not same as signed cert");
275 return false;
276 }
277 return true;
278 }
279
IsAppDistributedTypeAllowInstall(const AppDistType & type,const ProvisionInfo & provisionInfo) const280 bool HapVerifyV2::IsAppDistributedTypeAllowInstall(const AppDistType& type, const ProvisionInfo& provisionInfo) const
281 {
282 switch (type) {
283 case AppDistType::NONE_TYPE:
284 return false;
285 case AppDistType::APP_GALLERY:
286 if (CheckTicketSource(provisionInfo)) {
287 HAPVERIFY_LOG_INFO(LABEL, "current device is allowed to install opentest application");
288 return true;
289 }
290 return false;
291 case AppDistType::ENTERPRISE:
292 case AppDistType::OS_INTEGRATION:
293 case AppDistType::CROWDTESTING:
294 return true;
295 default:
296 return false;
297 }
298 }
299
CheckProfileSignatureIsRight(const MatchingStates & matchState,const ProvisionType & type)300 bool HapVerifyV2::CheckProfileSignatureIsRight(const MatchingStates& matchState, const ProvisionType& type)
301 {
302 if (matchState == MATCH_WITH_PROFILE && type == ProvisionType::RELEASE) {
303 return true;
304 } else if (matchState == MATCH_WITH_PROFILE_DEBUG && type == ProvisionType::DEBUG) {
305 return true;
306 }
307 HAPVERIFY_LOG_ERROR(LABEL, "isTrustedSource: %{public}d is not match with profile type: %{public}d",
308 static_cast<int>(matchState), static_cast<int>(type));
309 return false;
310 }
311
WriteCrlIfNeed(const Pkcs7Context & pkcs7Context,const bool & profileNeedWriteCrl)312 void HapVerifyV2::WriteCrlIfNeed(const Pkcs7Context& pkcs7Context, const bool& profileNeedWriteCrl)
313 {
314 if (!pkcs7Context.needWriteCrl && !profileNeedWriteCrl) {
315 return;
316 }
317 HapCrlManager& hapCrlManager = HapCrlManager::GetInstance();
318 hapCrlManager.WriteCrlsToFile();
319 }
320
ParseAndVerifyProfileIfNeed(const std::string & profile,ProvisionInfo & provisionInfo,bool isCallParseAndVerify)321 bool HapVerifyV2::ParseAndVerifyProfileIfNeed(const std::string& profile,
322 ProvisionInfo& provisionInfo, bool isCallParseAndVerify)
323 {
324 if (isCallParseAndVerify) {
325 return isCallParseAndVerify;
326 }
327 AppProvisionVerifyResult profileRet = ParseAndVerify(profile, provisionInfo);
328 if (profileRet != PROVISION_OK) {
329 HAPVERIFY_LOG_ERROR(LABEL, "profile parse failed, error: %{public}d", static_cast<int>(profileRet));
330 return false;
331 }
332 return true;
333 }
334
GetDigestAndAlgorithm(Pkcs7Context & digest)335 bool HapVerifyV2::GetDigestAndAlgorithm(Pkcs7Context& digest)
336 {
337 /*
338 * contentinfo format:
339 * int: version
340 * int: block number
341 * digest blocks:
342 * each digest block format:
343 * int: length of sizeof(digestblock) - 4
344 * int: Algorithm ID
345 * int: length of digest
346 * byte[]: digest
347 */
348 /* length of sizeof(digestblock - 4) */
349 int digestBlockLen;
350 if (!digest.content.GetInt32(DIGEST_BLOCK_LEN_OFFSET, digestBlockLen)) {
351 HAPVERIFY_LOG_ERROR(LABEL, "get digestBlockLen failed");
352 return false;
353 }
354 /* Algorithm ID */
355 if (!digest.content.GetInt32(DIGEST_ALGORITHM_OFFSET, digest.digestAlgorithm)) {
356 HAPVERIFY_LOG_ERROR(LABEL, "get digestAlgorithm failed");
357 return false;
358 }
359 /* length of digest */
360 int digestlen;
361 if (!digest.content.GetInt32(DIGEST_LEN_OFFSET, digestlen)) {
362 HAPVERIFY_LOG_ERROR(LABEL, "get digestlen failed");
363 return false;
364 }
365
366 int sum = sizeof(digestlen) + sizeof(digest.digestAlgorithm) + digestlen;
367 if (sum != digestBlockLen) {
368 HAPVERIFY_LOG_ERROR(LABEL, "digestBlockLen: %{public}d is not equal to sum: %{public}d",
369 digestBlockLen, sum);
370 return false;
371 }
372 /* set position to the digest start point */
373 digest.content.SetPosition(DIGEST_OFFSET_IN_CONTENT);
374 /* set limit to the digest end point */
375 digest.content.SetLimit(DIGEST_OFFSET_IN_CONTENT + digestlen);
376 digest.content.Slice();
377 return true;
378 }
379
ParseHapProfile(const std::string & filePath,HapVerifyResult & hapVerifyV1Result)380 int HapVerifyV2::ParseHapProfile(const std::string& filePath, HapVerifyResult& hapVerifyV1Result)
381 {
382 HAPVERIFY_LOG_INFO(LABEL, "start to ParseHapProfile");
383 std::string standardFilePath;
384 if (!CheckFilePath(filePath, standardFilePath)) {
385 return FILE_PATH_INVALID;
386 }
387
388 RandomAccessFile hapFile;
389 if (!hapFile.Init(standardFilePath)) {
390 HAPVERIFY_LOG_ERROR(LABEL, "open standard file failed");
391 return OPEN_FILE_ERROR;
392 }
393
394 SignatureInfo hapSignInfo;
395 if (!HapSigningBlockUtils::FindHapSignature(hapFile, hapSignInfo)) {
396 return SIGNATURE_NOT_FOUND;
397 }
398
399 int profileIndex = 0;
400 if (!HapSigningBlockUtils::GetOptionalBlockIndex(hapSignInfo.optionBlocks, PROFILE_BLOB, profileIndex)) {
401 return NO_PROFILE_BLOCK_FAIL;
402 }
403 auto pkcs7ProfileBlock = hapSignInfo.optionBlocks[profileIndex].optionalBlockValue;
404 const unsigned char* pkcs7Block = reinterpret_cast<const unsigned char*>(pkcs7ProfileBlock.GetBufferPtr());
405 unsigned int pkcs7Len = static_cast<unsigned int>(pkcs7ProfileBlock.GetCapacity());
406 Pkcs7Context profileContext;
407 if (!HapVerifyOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, profileContext)) {
408 HAPVERIFY_LOG_ERROR(LABEL, "parse pkcs7 failed");
409 return false;
410 }
411 std::string profile = std::string(profileContext.content.GetBufferPtr(), profileContext.content.GetCapacity());
412 HAPVERIFY_LOG_DEBUG(LABEL, "profile is %{public}s", profile.c_str());
413 ProvisionInfo info;
414 auto ret = ParseProfile(profile, info);
415 if (ret != PROVISION_OK) {
416 return PROFILE_PARSE_FAIL;
417 }
418
419 if (!GenerateFingerprint(info)) {
420 HAPVERIFY_LOG_ERROR(LABEL, "Generate appId or generate fingerprint failed");
421 return PROFILE_PARSE_FAIL;
422 }
423 hapVerifyV1Result.SetProvisionInfo(info);
424 return VERIFY_SUCCESS;
425 }
426 } // namespace Verify
427 } // namespace Security
428 } // namespace OHOS
429