diff --git a/interfaces/innerkits/appverify/BUILD.gn b/interfaces/innerkits/appverify/BUILD.gn index 65a1fb4a8ef59f7821749abf7c8a6ade9b06e2a4..69e3a5bb069b68760c4ff99f3ca556c8fcc677e2 100644 --- a/interfaces/innerkits/appverify/BUILD.gn +++ b/interfaces/innerkits/appverify/BUILD.gn @@ -44,6 +44,8 @@ ohos_shared_library("libhapverify") { "src/util/hap_signing_block_utils.cpp", "src/util/hap_verify_openssl_utils.cpp", "src/verify/hap_verify_v2.cpp", + "src/init/trusted_ticket_manager.cpp", + "src/ticket/ticket_verify.cpp", ] public_configs = [ ":libhapverify_config" ] diff --git a/interfaces/innerkits/appverify/config/BUILD.gn b/interfaces/innerkits/appverify/config/BUILD.gn index 32e72dd126362d76dba56b01e4c7c2a49c3c07de..f77311428a2b1338cc5312f4b75284c5ec547c63 100644 --- a/interfaces/innerkits/appverify/config/BUILD.gn +++ b/interfaces/innerkits/appverify/config/BUILD.gn @@ -48,3 +48,14 @@ ohos_prebuilt_etc("trusted_root_ca_test") { subsystem_name = "security" relative_install_dir = "security" } + +ohos_prebuilt_etc("trusted_tickets_sources") { + if (build_public_version) { + source = "OpenHarmony/trusted_tickets_sources.json" + } else { + source = "trusted_tickets_sources.json" + } + part_name = "appverify" + subsystem_name = "security" + relative_install_dir = "security" +} diff --git a/interfaces/innerkits/appverify/config/OpenHarmony/trusted_tickets_sources.json b/interfaces/innerkits/appverify/config/OpenHarmony/trusted_tickets_sources.json new file mode 100644 index 0000000000000000000000000000000000000000..aa378506ce3474491e667ef5861e8ff35730a978 --- /dev/null +++ b/interfaces/innerkits/appverify/config/OpenHarmony/trusted_tickets_sources.json @@ -0,0 +1,13 @@ +{ + "version": "1.0.1", + "release-time":"2021-06-03 10:06:00", + "trust-app-source":[ + { + "name":"huawei member center", + "ticket-signing-cert":"C=CN, O=Huawei, OU=MemberCenter, CN=MemberShip ticket Release V1", + "issuer-ca":"C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA", + "max-certs-path":3, + "critialcal-cert-extension":["keyusage","huawei-signing-capability"] + } + ] +} \ No newline at end of file diff --git a/interfaces/innerkits/appverify/config/trusted_tickets_sources.json b/interfaces/innerkits/appverify/config/trusted_tickets_sources.json new file mode 100644 index 0000000000000000000000000000000000000000..aa378506ce3474491e667ef5861e8ff35730a978 --- /dev/null +++ b/interfaces/innerkits/appverify/config/trusted_tickets_sources.json @@ -0,0 +1,13 @@ +{ + "version": "1.0.1", + "release-time":"2021-06-03 10:06:00", + "trust-app-source":[ + { + "name":"huawei member center", + "ticket-signing-cert":"C=CN, O=Huawei, OU=MemberCenter, CN=MemberShip ticket Release V1", + "issuer-ca":"C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA", + "max-certs-path":3, + "critialcal-cert-extension":["keyusage","huawei-signing-capability"] + } + ] +} \ No newline at end of file diff --git a/interfaces/innerkits/appverify/include/init/matching_result.h b/interfaces/innerkits/appverify/include/init/matching_result.h index 97f90546ebd779cd568c13d45721182fc0e42472..70578e2e5ca48e74396778aad2eb056d0e657300 100644 --- a/interfaces/innerkits/appverify/include/init/matching_result.h +++ b/interfaces/innerkits/appverify/include/init/matching_result.h @@ -30,6 +30,7 @@ enum MatchingStates { MATCH_WITH_SIGN, MATCH_WITH_PROFILE, MATCH_WITH_PROFILE_DEBUG, + MATCH_WITH_TICKET, }; struct MatchingResult { diff --git a/interfaces/innerkits/appverify/include/init/trusted_ticket_manager.h b/interfaces/innerkits/appverify/include/init/trusted_ticket_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..9fe887726521f1fdf16c52c9bf4ad12b1d98089c --- /dev/null +++ b/interfaces/innerkits/appverify/include/init/trusted_ticket_manager.h @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HAP_TRUSTED_TICKET_MANAGER_H +#define HAP_TRUSTED_TICKET_MANAGER_H + +#include +#include + +#include "common/export_define.h" +#include "init/json_parser_utils.h" +#include "init/matching_result.h" + +namespace OHOS { +namespace Security { +namespace Verify { +struct HapTicketSourceInfo { + TrustedSources source; + std::string sourceName; + std::string ticketSigningCert; + std::string issuer; + int maxCertsPath; + StringVec critialcalCertExtension; +}; + +using TicketSourceInfoVec = std::vector; + +class TrustedTicketManager { +public: + DLL_EXPORT static TrustedTicketManager& GetInstance(); + DLL_EXPORT bool Init(); + DLL_EXPORT void Recovery(); + DLL_EXPORT MatchingResult IsTrustedSource(const std::string& certSubject, const std::string& certIssuer, + int certListPath) const; + +public: + bool isInit; + +private: + TrustedTicketManager(); + ~TrustedTicketManager(); + + /* Forbid external replication constructs and external replication */ + TrustedTicketManager(const TrustedTicketManager& trustedSource) = delete; + TrustedTicketManager& operator = (const TrustedTicketManager& trustedSource) = delete; + + bool GetTicketTrustedSources(TicketSourceInfoVec& trustedTicketSources, std::string& sourcesVersion, + std::string& sourcesReleaseTime, const std::string& filePath); + bool ParseTrustedTicketSourceJson(TicketSourceInfoVec& trustedTicketSources, + const JsonObjVec& trustedTicketSourceJson); + std::string EncapTrustedTicketSourceString(const HapTicketSourceInfo& ticketSourceInfo); + MatchingResult MatchTrustedSource(const TicketSourceInfoVec& trustedTicketSources, const std::string& certSubject, + const std::string& certIssuer, int certListPath) const; + MatchingStates TrustedSourceListCompare(const std::string& certSubject, const std::string& certIssuer, + const HapTicketSourceInfo& TicketSource) const; + TrustedSources GetTrustedSource(std::string& sourceName); + bool MatchSubject(const std::string& trustedSource, const std::string& certSubject) const; + bool MatchIssuer(const std::string& trustedSource, const std::string& certIssuer) const; + +private: + static const std::string TICKET_TRUSTED_SOURCE_FILE_PATH; + static const std::string KEY_OF_TICKET_TRUSTED_SOURCE; + static const std::string KEY_OF_TICKET_TRUSTED_SOURCE_VERSION; + static const std::string KEY_OF_TICKET_TRUSTED_SOURCE_RELEASETIME; + static const std::string KEY_OF_SOURCE_NAME; + static const std::string KEY_OF_TICKET_SIGNING_CERT; + static const std::string KEY_OF_ISSUER; + static const std::string KEY_OF_MAX_CERTS_PATH; + static const std::string KEY_OF_CRITIALCAL_CERT_EXTENSION; + static const std::string TICKET_PATTERN; + static const std::string APP_GALLARY_SOURCE_NAME; + static const std::string APP_SYSTEM_SOURCE_NAME; + static const std::string APP_THIRD_PARTY_PRELOAD_SOURCE_NAME; + TicketSourceInfoVec TicketTrustedSources; + std::string version; + std::string releaseTime; +}; +} // namespace Verify +} // namespace Security +} // namespace OHOS +#endif // HAP_TRUSTED_TICKET_MANAGER_H \ No newline at end of file diff --git a/interfaces/innerkits/appverify/include/interfaces/hap_verify_result.h b/interfaces/innerkits/appverify/include/interfaces/hap_verify_result.h index 89647954fedb770451c50464de7837fdefe9eaef..b7af4b66a7215b3314115591537aa165f91330f0 100644 --- a/interfaces/innerkits/appverify/include/interfaces/hap_verify_result.h +++ b/interfaces/innerkits/appverify/include/interfaces/hap_verify_result.h @@ -41,6 +41,8 @@ enum HapVerifyResultCode { NO_PROFILE_BLOCK_FAIL = -12, VERIFY_SIGNATURE_FAIL = -13, VERIFY_SOURCE_INIT_FAIL = -14, + TICKET_READ_FAIL = -15, + TICKET_PARSE_FAIL = -16, }; enum GetOptionalBlockResultCode { diff --git a/interfaces/innerkits/appverify/include/provision/provision_verify.h b/interfaces/innerkits/appverify/include/provision/provision_verify.h index 200d70d9722194d34703eaaf99adc22cf7858173..11238bd5e756c47d3b3de08c370f8f1cedf28841 100644 --- a/interfaces/innerkits/appverify/include/provision/provision_verify.h +++ b/interfaces/innerkits/appverify/include/provision/provision_verify.h @@ -28,7 +28,10 @@ enum AppProvisionVerifyResult { PROVISION_OK, // Passed PROVISION_UNSUPPORTED_DEVICE_TYPE, // Failed to get device id PROVISION_NUM_DEVICE_EXCEEDED, // No. of device exceeds maximum number - PROVISION_DEVICE_UNAUTHORIZED // Device id not included + PROVISION_DEVICE_UNAUTHORIZED, // Device id not included + TICKET_OK, // Passed + TICKET_NOT_MATCH, // Not match + TICKET_PERMISSION_ERROR, // Permission not match }; /** @@ -38,6 +41,7 @@ enum AppProvisionVerifyResult { * @return AppProvisionVerifyResult Verification result. */ DLL_EXPORT AppProvisionVerifyResult ParseAndVerify(const std::string& appProvision, ProvisionInfo& info); +DLL_EXPORT AppProvisionVerifyResult ParseProvision(const std::string& appProvision, ProvisionInfo& info); } // namespace Verify } // namespace Security } // namespace OHOS diff --git a/interfaces/innerkits/appverify/include/ticket/ticket_verify.h b/interfaces/innerkits/appverify/include/ticket/ticket_verify.h new file mode 100644 index 0000000000000000000000000000000000000000..870a060364e951b641c3b876c53d5eee306be810 --- /dev/null +++ b/interfaces/innerkits/appverify/include/ticket/ticket_verify.h @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef TICKET_VERIFY_H +#define TICKET_VERIFY_H + +#include "common/export_define.h" +#include "common/hap_byte_buffer.h" +#include "common/random_access_file.h" + +#include "interfaces/hap_verify_result.h" +#include "provision/provision_info.h" +#include "provision/provision_verify.h" + +namespace OHOS { +namespace Security { +namespace Verify { +/** + * @brief Parse and verify the app ticket + * @param info Out param, the parsed app ticket structure. + * @return AppTicketVerifyResult Verification result. + */ +DLL_EXPORT bool CheckTicketSource(const ProvisionInfo& profileInfo); +} // namespace Verify +} // namespace Security +} // namespace OHOS +#endif // TICKET_VERIFY_H \ No newline at end of file diff --git a/interfaces/innerkits/appverify/include/verify/hap_verify_v2.h b/interfaces/innerkits/appverify/include/verify/hap_verify_v2.h index cb1950cccedf986baae75eabbfecf9c6a78e06c9..0ef5c5f087439f4922fab2917ad1ce424b564c38 100644 --- a/interfaces/innerkits/appverify/include/verify/hap_verify_v2.h +++ b/interfaces/innerkits/appverify/include/verify/hap_verify_v2.h @@ -43,7 +43,7 @@ private: void WriteCrlIfNeed(const Pkcs7Context& pkcs7Context, const bool& profileNeedWriteCrl); DLL_EXPORT bool ParseAndVerifyProfileIfNeed(const std::string& profile, ProvisionInfo& provisionInfo, bool isCallParseAndVerify); - bool IsAppDistributedTypeAllowInstall(const AppDistType& type) const; + bool IsAppDistributedTypeAllowInstall(const AppDistType& type, const ProvisionInfo& provisionInfo) const; DLL_EXPORT bool VerifyProfileInfo(const Pkcs7Context& pkcs7Context, const Pkcs7Context& profileContext, ProvisionInfo& provisionInfo); bool CheckProfileSignatureIsRight(const MatchingStates& matchState, const ProvisionType& type); diff --git a/interfaces/innerkits/appverify/src/init/trusted_ticket_manager.cpp b/interfaces/innerkits/appverify/src/init/trusted_ticket_manager.cpp new file mode 100644 index 0000000000000000000000000000000000000000..95e45c2ddbf1923062de22278962915567b420ed --- /dev/null +++ b/interfaces/innerkits/appverify/src/init/trusted_ticket_manager.cpp @@ -0,0 +1,235 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "init/trusted_ticket_manager.h" + +#include "nlohmann/json.hpp" + +#include "common/hap_verify_log.h" + +#include + +namespace OHOS { +namespace Security { +namespace Verify { +const std::string TrustedTicketManager::TICKET_TRUSTED_SOURCE_FILE_PATH = + "/system/etc/security/trusted_tickets_sources.json"; +const std::string TrustedTicketManager::KEY_OF_TICKET_TRUSTED_SOURCE = "trust-app-source"; +const std::string TrustedTicketManager::KEY_OF_TICKET_TRUSTED_SOURCE_VERSION = "version"; +const std::string TrustedTicketManager::KEY_OF_TICKET_TRUSTED_SOURCE_RELEASETIME = "release-time"; +const std::string TrustedTicketManager::KEY_OF_SOURCE_NAME = "name"; +const std::string TrustedTicketManager::KEY_OF_TICKET_SIGNING_CERT = "ticket-signing-cert"; +const std::string TrustedTicketManager::KEY_OF_ISSUER = "issuer-ca"; +const std::string TrustedTicketManager::KEY_OF_MAX_CERTS_PATH = "max-certs-path"; +const std::string TrustedTicketManager::KEY_OF_CRITIALCAL_CERT_EXTENSION = "critialcal-cert-extension"; +const std::string TrustedTicketManager::TICKET_PATTERN = + "C=CN, O=Huawei, OU=MemberCenter, CN=MemberShip ticket Release V[1-9]*"; +const std::string TrustedTicketManager::APP_GALLARY_SOURCE_NAME = "huawei app gallery"; +const std::string TrustedTicketManager::APP_SYSTEM_SOURCE_NAME = "huawei system apps"; +const std::string TrustedTicketManager::APP_THIRD_PARTY_PRELOAD_SOURCE_NAME = "third_party app preload"; + +TrustedTicketManager& TrustedTicketManager::GetInstance() +{ + static TrustedTicketManager singleTrustedTicketManager; + return singleTrustedTicketManager; +} + +TrustedTicketManager::TrustedTicketManager() + : isInit(false), TicketTrustedSources(), version(), releaseTime() +{ +} + +TrustedTicketManager::~TrustedTicketManager() +{ +} + +bool TrustedTicketManager::Init() +{ + if (isInit) { + return true; + } + + isInit = GetTicketTrustedSources(TicketTrustedSources, version, releaseTime, TICKET_TRUSTED_SOURCE_FILE_PATH); + if (isInit) { + HAPVERIFY_LOG_INFO(LABEL, "trusted ticket source version: %{public}s, releaseTime: %{public}s, Size:" + " %{public}zu", version.c_str(), releaseTime.c_str(), TicketTrustedSources.size()); + } + return isInit; +} + +void TrustedTicketManager::Recovery() +{ + TicketTrustedSources.clear(); + isInit = false; +} + +bool TrustedTicketManager::GetTicketTrustedSources(TicketSourceInfoVec& trustedTicketSources, + std::string& sourcesVersion, std::string& sourcesReleaseTime, const std::string& filePath) +{ + nlohmann::json trustedSourceJson; + std::string errorInfo; + if (!JsonParserUtils::ReadTrustedRootCAFromJson(trustedSourceJson, filePath, errorInfo)) { + HAPVERIFY_LOG_ERROR(LABEL, "get jsonObj from %{public}s failed, because %{public}s", + filePath.c_str(), errorInfo.c_str()); + return false; + } + if (!JsonParserUtils::GetJsonString(trustedSourceJson, KEY_OF_TICKET_TRUSTED_SOURCE_VERSION, sourcesVersion)) { + HAPVERIFY_LOG_ERROR(LABEL, "get version failed"); + return false; + } + if (!JsonParserUtils::GetJsonString(trustedSourceJson, + KEY_OF_TICKET_TRUSTED_SOURCE_RELEASETIME, sourcesReleaseTime)) { + HAPVERIFY_LOG_ERROR(LABEL, "get releaseTime failed"); + return false; + } + JsonObjVec trustedTicketSourceJson; + if (!JsonParserUtils::ParseJsonToObjVec(trustedSourceJson, KEY_OF_TICKET_TRUSTED_SOURCE, trustedTicketSourceJson)) { + HAPVERIFY_LOG_ERROR(LABEL, "get JsonObjVec failed"); + return false; + } + if (!ParseTrustedTicketSourceJson(trustedTicketSources, trustedTicketSourceJson)) { + HAPVERIFY_LOG_ERROR(LABEL, "parse JsonObjVec failed"); + return false; + } + if (trustedTicketSources.empty()) { + HAPVERIFY_LOG_ERROR(LABEL, "no ticket trusted source"); + return false; + } + return true; +} + +bool TrustedTicketManager::ParseTrustedTicketSourceJson(TicketSourceInfoVec& trustedTicketSources, + const JsonObjVec& trustedTicketSourceJson) +{ + for (auto TicketSource : trustedTicketSourceJson) { + HapTicketSourceInfo hapTicketSource; + if (!JsonParserUtils::GetJsonString(TicketSource, KEY_OF_SOURCE_NAME, hapTicketSource.sourceName)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get sourceName Failed"); + return false; + } + hapTicketSource.source = GetTrustedSource(hapTicketSource.sourceName); + if (!JsonParserUtils::GetJsonString(TicketSource, KEY_OF_TICKET_SIGNING_CERT, + hapTicketSource.ticketSigningCert)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get ticketSigningCert Failed"); + return false; + } + if (!JsonParserUtils::GetJsonString(TicketSource, KEY_OF_ISSUER, hapTicketSource.issuer)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get issuer Failed"); + return false; + } + if (!JsonParserUtils::GetJsonInt(TicketSource, KEY_OF_MAX_CERTS_PATH, hapTicketSource.maxCertsPath)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get maxCertsPath Failed"); + return false; + } + if (!JsonParserUtils::GetJsonStringVec(TicketSource, KEY_OF_CRITIALCAL_CERT_EXTENSION, + hapTicketSource.critialcalCertExtension)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get critialcalCertExtension Failed"); + return false; + } + HAPVERIFY_LOG_INFO(LABEL, "trusted ticket source: %{public}s", + EncapTrustedTicketSourceString(hapTicketSource).c_str()); + trustedTicketSources.push_back(hapTicketSource); + } + return true; +} + +std::string TrustedTicketManager::EncapTrustedTicketSourceString(const HapTicketSourceInfo& ticketSourceInfo) +{ + std::string info = "sourceName: " + ticketSourceInfo.sourceName + "\n" + + "sourceNumber: " + std::to_string(static_cast(ticketSourceInfo.source)) + "\n" + + "ticketSigningCert: " + ticketSourceInfo.ticketSigningCert + "\n" + + "issuer: " + ticketSourceInfo.issuer + "\n" + + "maxCertsPath: " + std::to_string(ticketSourceInfo.maxCertsPath) + "\n" + + "critialcalCertExtension: "; + for (auto extension : ticketSourceInfo.critialcalCertExtension) { + info += extension + ", "; + } + return info; +} + +MatchingResult TrustedTicketManager::IsTrustedSource(const std::string& certSubject, + const std::string& certIssuer, int certListPath) const +{ + MatchingResult ret = MatchTrustedSource(TicketTrustedSources, certSubject, certIssuer, certListPath); + if (ret.matchState != DO_NOT_MATCH) { + return ret; + } + return ret; +} + +MatchingResult TrustedTicketManager::MatchTrustedSource(const TicketSourceInfoVec& trustedTicketSources, + const std::string& certSubject, const std::string& certIssuer, int certListPath) const +{ + MatchingResult ret; + ret.matchState = DO_NOT_MATCH; + for (auto TicketSource : trustedTicketSources) { + if (certListPath == TicketSource.maxCertsPath) { + ret.matchState = TrustedSourceListCompare(certSubject, certIssuer, TicketSource); + if (ret.matchState != DO_NOT_MATCH) { + ret.source = TicketSource.source; + break; + } + } + } + return ret; +} + +MatchingStates TrustedTicketManager::TrustedSourceListCompare(const std::string& certSubject, + const std::string& certIssuer, const HapTicketSourceInfo& TicketSource) const +{ + MatchingStates ret = DO_NOT_MATCH; + if (MatchSubject(TicketSource.ticketSigningCert, certSubject) && + MatchIssuer(TicketSource.issuer, certIssuer)) { + ret = MATCH_WITH_TICKET; + } + return ret; +} + +TrustedSources TrustedTicketManager::GetTrustedSource(std::string& sourceName) +{ + if (APP_GALLARY_SOURCE_NAME == sourceName) { + return APP_GALLARY; + } + + if (APP_SYSTEM_SOURCE_NAME == sourceName) { + return APP_SYSTEM; + } + + if (APP_THIRD_PARTY_PRELOAD_SOURCE_NAME == sourceName) { + return APP_THIRD_PARTY_PRELOAD; + } + return OTHER_TRUSTED_SOURCE; +} + +bool TrustedTicketManager::MatchSubject(const std::string& trustedSource, + const std::string& certSubject) const +{ + if (trustedSource.empty()) { + return false; + } + return std::regex_match(certSubject, std::regex(TICKET_PATTERN)); +} + +bool TrustedTicketManager::MatchIssuer(const std::string& trustedSource, + const std::string& certIssuer) const +{ + if (trustedSource.empty()) { + return false; + } + return trustedSource == certIssuer; +} +} // namespace Verify +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/innerkits/appverify/src/interfaces/hap_verify.cpp b/interfaces/innerkits/appverify/src/interfaces/hap_verify.cpp index 754cc6e197d2f91f421e485952156bfc2e169583..1f31642fb5cbe973b6248428bb6817bcf96d1b7a 100644 --- a/interfaces/innerkits/appverify/src/interfaces/hap_verify.cpp +++ b/interfaces/innerkits/appverify/src/interfaces/hap_verify.cpp @@ -21,6 +21,7 @@ #include "init/hap_crl_manager.h" #include "init/trusted_root_ca.h" #include "init/trusted_source_manager.h" +#include "init/trusted_ticket_manager.h" #include "verify/hap_verify_v2.h" namespace OHOS { @@ -35,12 +36,14 @@ bool HapVerifyInit() TrustedSourceManager& trustedAppSourceManager = TrustedSourceManager::GetInstance(); HapCrlManager& hapCrlManager = HapCrlManager::GetInstance(); DeviceTypeManager& deviceTypeManager = DeviceTypeManager::GetInstance(); + TrustedTicketManager& trustedTicketSourceManager = TrustedTicketManager::GetInstance(); g_mtx.lock(); g_isInit = rootCertsObj.Init() && trustedAppSourceManager.Init(); if (!g_isInit) { rootCertsObj.Recovery(); trustedAppSourceManager.Recovery(); } + trustedTicketSourceManager.Init(); hapCrlManager.Init(); deviceTypeManager.GetDeviceTypeInfo(); g_mtx.unlock(); diff --git a/interfaces/innerkits/appverify/src/provision/provision_verify.cpp b/interfaces/innerkits/appverify/src/provision/provision_verify.cpp index 4869b98d1aeecdf2bc51d3b2ebcbd130e734862f..f6609f36e1e6b657565f21513a9203b5c291a790 100644 --- a/interfaces/innerkits/appverify/src/provision/provision_verify.cpp +++ b/interfaces/innerkits/appverify/src/provision/provision_verify.cpp @@ -28,7 +28,9 @@ #include "common/hap_verify_log.h" #include "init/device_type_manager.h" - +#include "init/trusted_ticket_manager.h" +#include "ticket/ticket_verify.h" +#include "interfaces/hap_verify_result.h" using namespace std; using namespace nlohmann; @@ -203,10 +205,16 @@ AppProvisionVerifyResult ParseProvision(const string& appProvision, ProvisionInf return PROVISION_OK; } -inline bool CheckDeviceID(const std::vector& deviceIds, const string& deviceId) +inline bool CheckDeviceID(const std::vector& deviceIds, const string& deviceId, ProvisionInfo& profileInfo) { auto iter = find(deviceIds.begin(), deviceIds.end(), deviceId); if (iter == deviceIds.end()) { + /* check ticketsource and verify ticket */ + if (CheckTicketSource(profileInfo)) { + HAPVERIFY_LOG_INFO(LABEL, "current device is a z-debug device"); + return true; + } + DeviceTypeManager& deviceTypeManager = DeviceTypeManager::GetInstance(); if (!deviceTypeManager.GetDeviceTypeInfo()) { HAPVERIFY_LOG_ERROR(LABEL, "current device is not authorized"); @@ -237,6 +245,7 @@ AppProvisionVerifyResult CheckDeviceID(ProvisionInfo& info) string deviceId; #ifndef STANDARD_SYSTEM + int32_t ret = OHOS::AccountSA::OhosAccountKits::GetInstance().GetUdid(deviceId); if (ret != 0) { HAPVERIFY_LOG_ERROR(LABEL, "obtaining current device id failed (%{public}d).", ret); @@ -258,7 +267,7 @@ AppProvisionVerifyResult CheckDeviceID(ProvisionInfo& info) return PROVISION_DEVICE_UNAUTHORIZED; } - if (!CheckDeviceID(info.debugInfo.deviceIds, deviceId)) { + if (!CheckDeviceID(info.debugInfo.deviceIds, deviceId, info)) { return PROVISION_DEVICE_UNAUTHORIZED; } return PROVISION_OK; diff --git a/interfaces/innerkits/appverify/src/ticket/ticket_verify.cpp b/interfaces/innerkits/appverify/src/ticket/ticket_verify.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e54ba21617dfbc5e93451e8628290402548815dc --- /dev/null +++ b/interfaces/innerkits/appverify/src/ticket/ticket_verify.cpp @@ -0,0 +1,267 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "ticket/ticket_verify.h" + +#include + +#include "nlohmann/json.hpp" + +#ifndef STANDARD_SYSTEM +#include "ohos_account_kits.h" +#else +#include "parameter.h" +#include "sysparam_errno.h" +#endif // STANDARD_SYSTEM + +#include "common/hap_verify_log.h" +#include "common/random_access_file.h" +#include "common/hap_byte_buffer.h" + +#include "util/hap_verify_openssl_utils.h" +#include "util/hap_cert_verify_openssl_utils.h" +#include "util/pkcs7_context.h" + +#include "init/device_type_manager.h" +#include "init/trusted_ticket_manager.h" + +namespace { +const std::string TICKET_FILE_PATH = "/data/misc/ticket/"; +const std::string VALUE_DEVICE_TYPE_UDID = "udid"; +const int MAXIMUM_DEVICES = 100; +} // namespace + +namespace OHOS { +namespace Security { +namespace Verify { +bool CheckPermissions(std::vector ticketPermissions, std::vector profilePermissions) +{ + for (auto ticket : ticketPermissions) { + auto iter = find(profilePermissions.begin(), profilePermissions.end(), ticket); + if (iter == profilePermissions.end()) { + return false; + } + } + return true; +} + +inline bool CheckDevice(const std::vector& deviceIds, const std::string& deviceId, ProvisionInfo& profileInfo) +{ + auto iter = find(deviceIds.begin(), deviceIds.end(), deviceId); + if (iter == deviceIds.end()) { + DeviceTypeManager& deviceTypeManager = DeviceTypeManager::GetInstance(); + if (!deviceTypeManager.GetDeviceTypeInfo()) { + HAPVERIFY_LOG_ERROR(LABEL, "current device is not authorized"); + return false; + } + HAPVERIFY_LOG_INFO(LABEL, "current device is a debug device"); + } + return true; +} + +AppProvisionVerifyResult CheckDevice(ProvisionInfo& info) +{ + // Checking device ids + if (info.debugInfo.deviceIds.empty()) { + HAPVERIFY_LOG_ERROR(LABEL, "device-id list is empty."); + return PROVISION_DEVICE_UNAUTHORIZED; + } + + if (info.debugInfo.deviceIds.size() > MAXIMUM_DEVICES) { + HAPVERIFY_LOG_ERROR(LABEL, "No. of device IDs in list exceed maximum number %{public}d", MAXIMUM_DEVICES); + return PROVISION_NUM_DEVICE_EXCEEDED; + } + + if (info.debugInfo.deviceIdType != VALUE_DEVICE_TYPE_UDID) { + HAPVERIFY_LOG_ERROR(LABEL, "type of device ID is not supported."); + return PROVISION_UNSUPPORTED_DEVICE_TYPE; + } + + std::string deviceId; +#ifndef STANDARD_SYSTEM + + int32_t ret = OHOS::AccountSA::OhosAccountKits::GetInstance().GetUdid(deviceId); + if (ret != 0) { + HAPVERIFY_LOG_ERROR(LABEL, "obtaining current device id failed (%{public}d).", ret); + return PROVISION_DEVICE_UNAUTHORIZED; + } +#else + char udid[DEV_UUID_LEN] = {0}; + int ret = GetDevUdid(udid, sizeof(udid)); + if (ret != EC_SUCCESS) { + HAPVERIFY_LOG_ERROR(LABEL, "obtaining current device id failed (%{public}d).", static_cast(ret)); + return PROVISION_DEVICE_UNAUTHORIZED; + } + deviceId = std::string(udid, sizeof(udid) - 1); + HAPVERIFY_LOG_INFO(LABEL, "L2 UDID:%{public}s, len:%{public}d.", deviceId.c_str(), + static_cast(deviceId.size())); +#endif // STANDARD_SYSTEM + if (deviceId.empty()) { + HAPVERIFY_LOG_ERROR(LABEL, "device-id of current device is empty."); + return PROVISION_DEVICE_UNAUTHORIZED; + } + + if (!CheckDevice(info.debugInfo.deviceIds, deviceId, info)) { + return PROVISION_DEVICE_UNAUTHORIZED; + } + return PROVISION_OK; +} + +AppProvisionVerifyResult CompareTicketAndProfile(const ProvisionInfo& ticketInfo, const ProvisionInfo& profileInfo) +{ + if (ticketInfo.bundleInfo.bundleName != profileInfo.bundleInfo.bundleName) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket bundlename doesn't match"); + return TICKET_NOT_MATCH; + } + + if (ticketInfo.type == DEBUG) { + if (ticketInfo.bundleInfo.developmentCertificate != profileInfo.bundleInfo.developmentCertificate) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket developmentCertificate doesn't match"); + return TICKET_NOT_MATCH; + } + } else { + if (ticketInfo.bundleInfo.distributionCertificate != profileInfo.bundleInfo.distributionCertificate) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket distributionCertificate doesn't match"); + return TICKET_NOT_MATCH; + } + } + + if (!ticketInfo.permissions.restrictedCapabilities.empty()) { + if (!CheckPermissions(ticketInfo.permissions.restrictedCapabilities, + profileInfo.permissions.restrictedCapabilities)) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket restrictedCapabilities doesn't match"); + return TICKET_PERMISSION_ERROR; + } + } + + if (!ticketInfo.permissions.restrictedPermissions.empty()) { + if (!CheckPermissions(ticketInfo.permissions.restrictedPermissions, + profileInfo.permissions.restrictedPermissions)) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket restrictedPermissions doesn't match"); + return TICKET_PERMISSION_ERROR; + } + } + return TICKET_OK; +} + +bool VerifyTicketSignature(HapByteBuffer& ticketBlock, Pkcs7Context& pkcs7Context, std::string& ticket) +{ + const unsigned char* pkcs7Block = reinterpret_cast(ticketBlock.GetBufferPtr()); + unsigned int pkcs7Len = ticketBlock.GetCapacity(); + if (!HapVerifyOpensslUtils::ParsePkcs7Package(pkcs7Block, pkcs7Len, pkcs7Context)) { + HAPVERIFY_LOG_ERROR(LABEL, "Parse ticket pkcs7 failed"); + return false; + } + ticket = std::string(pkcs7Context.content.GetBufferPtr(), pkcs7Context.content.GetCapacity()); + + if (!HapVerifyOpensslUtils::GetCertChains(pkcs7Context.p7, pkcs7Context)) { + HAPVERIFY_LOG_ERROR(LABEL, "GetCertChains from ticket pkcs7 failed"); + return false; + } + + if (!HapVerifyOpensslUtils::VerifyPkcs7(pkcs7Context)) { + HAPVERIFY_LOG_ERROR(LABEL, "verify ticket signature failed"); + return false; + } + + std::string certSubject; + std::string certIssuer; + if (!HapCertVerifyOpensslUtils::GetSubjectFromX509(pkcs7Context.certChains[0][0], certSubject) || + !HapCertVerifyOpensslUtils::GetIssuerFromX509(pkcs7Context.certChains[0][0], certIssuer)) { + HAPVERIFY_LOG_ERROR(LABEL, "Get info of sign cert from ticket failed"); + return false; + } + + TrustedTicketManager& trustedTicketSourceManager = TrustedTicketManager::GetInstance(); + pkcs7Context.matchResult = trustedTicketSourceManager.IsTrustedSource(certSubject, pkcs7Context.certIssuer, + pkcs7Context.certChains[0].size()); + if (pkcs7Context.matchResult.matchState == DO_NOT_MATCH) { + HAPVERIFY_LOG_ERROR(LABEL, "Ticket signature is not trusted source, subject: %{public}s, issuer: %{public}s", + certSubject.c_str(), certIssuer.c_str()); + return false; + } + HAPVERIFY_LOG_INFO(LABEL, "Ticket subject: %{public}s, issuer: %{public}s", + certSubject.c_str(), certIssuer.c_str()); + return true; +} + +AppProvisionVerifyResult TicketParseAndVerify(const std::string& Ticket, ProvisionInfo& ticketInfo, + const ProvisionInfo& profileInfo) +{ + AppProvisionVerifyResult ret = ParseProvision(Ticket, ticketInfo); + if (ret != PROVISION_OK) { + return ret; + } + ret = CompareTicketAndProfile(ticketInfo, profileInfo); + if (ret != TICKET_OK) { + return ret; + } + ret = CheckDevice(ticketInfo); + if (ret != PROVISION_OK) { + return ret; + } + return TICKET_OK; +} + +int VerifyTicket(const std::string& filePath, const ProvisionInfo& profileInfo) +{ + HAPVERIFY_LOG_INFO(LABEL, "Enter Ticket Verify"); + RandomAccessFile TicketFile; + if (!TicketFile.Init(filePath)) { + HAPVERIFY_LOG_ERROR(LABEL, "open %{public}s failed", filePath.c_str()); + return OPEN_FILE_ERROR; + } + long long fileLength = TicketFile.GetLength(); + int fileLen = static_cast(fileLength); + HapByteBuffer ticketBlock(fileLen); + long long ret = TicketFile.ReadFileFullyFromOffset(ticketBlock, 0); + if (ret < 0) { + HAPVERIFY_LOG_ERROR(LABEL, "read data from ticket error: %{public}lld", ret); + return TICKET_READ_FAIL; + } + + Pkcs7Context pkcs7Context; + std::string ticket; + if (!VerifyTicketSignature(ticketBlock, pkcs7Context, ticket)) { + HAPVERIFY_LOG_ERROR(LABEL, "verify ticket signature failed"); + return VERIFY_SIGNATURE_FAIL; + } + + ProvisionInfo ticketInfo; + AppProvisionVerifyResult ticketRet = TicketParseAndVerify(ticket, ticketInfo, profileInfo); + if (ticketRet != TICKET_OK) { + HAPVERIFY_LOG_ERROR(LABEL, "ticket parse failed, error: %{public}d", static_cast(ticketRet)); + return TICKET_PARSE_FAIL; + } + HAPVERIFY_LOG_DEBUG(LABEL, "Leave Ticket Verify"); + return VERIFY_SUCCESS; +} + +bool CheckTicketSource(const ProvisionInfo& profileInfo) +{ + HAPVERIFY_LOG_INFO(LABEL, "Verify Ticket source"); + TrustedTicketManager& trustedTicketSourceManager = TrustedTicketManager::GetInstance(); + if (trustedTicketSourceManager.isInit) { + std::string ticketfilepath = TICKET_FILE_PATH + profileInfo.bundleInfo.bundleName + ".p7b"; + if (VerifyTicket(ticketfilepath, profileInfo) == VERIFY_SUCCESS) { + HAPVERIFY_LOG_INFO(LABEL, "ticket verify success"); + return true; + } + } + return false; +} +} // namespace Verify +} // namespace Security +} // namespace OHOS \ No newline at end of file diff --git a/interfaces/innerkits/appverify/src/verify/hap_verify_v2.cpp b/interfaces/innerkits/appverify/src/verify/hap_verify_v2.cpp index 12b6ae546db9e82e74d9b35b2397f70a93df497a..ceedbbcd01093778f04e218c4efa32e28e81a894 100644 --- a/interfaces/innerkits/appverify/src/verify/hap_verify_v2.cpp +++ b/interfaces/innerkits/appverify/src/verify/hap_verify_v2.cpp @@ -27,6 +27,7 @@ #include "util/hap_profile_verify_utils.h" #include "util/hap_signing_block_utils.h" #include "util/signature_info.h" +#include "ticket/ticket_verify.h" namespace OHOS { namespace Security { @@ -238,7 +239,7 @@ bool HapVerifyV2::VerifyProfileInfo(const Pkcs7Context& pkcs7Context, const Pkcs } std::string& certInProfile = provisionInfo.bundleInfo.developmentCertificate; if (provisionInfo.type == ProvisionType::RELEASE) { - if (!IsAppDistributedTypeAllowInstall(provisionInfo.distributionType)) { + if (!IsAppDistributedTypeAllowInstall(provisionInfo.distributionType, provisionInfo)) { HAPVERIFY_LOG_ERROR(LABEL, "untrusted source app with release profile distributionType: %{public}d", static_cast(provisionInfo.distributionType)); return false; @@ -255,11 +256,15 @@ bool HapVerifyV2::VerifyProfileInfo(const Pkcs7Context& pkcs7Context, const Pkcs return true; } -bool HapVerifyV2::IsAppDistributedTypeAllowInstall(const AppDistType& type) const +bool HapVerifyV2::IsAppDistributedTypeAllowInstall(const AppDistType& type, const ProvisionInfo& provisionInfo) const { switch (type) { case AppDistType::NONE_TYPE: case AppDistType::APP_GALLERY: + if (CheckTicketSource(provisionInfo)) { + HAPVERIFY_LOG_INFO(LABEL, "current device is a z-debug device"); + return true; + } return false; case AppDistType::ENTERPRISE: case AppDistType::OS_INTEGRATION: diff --git a/interfaces/innerkits/appverify/test/BUILD.gn b/interfaces/innerkits/appverify/test/BUILD.gn index 302dcd7bee8be77e9997382e17dd79f677344002..edeca09d16d5196595cd96d35ad25baa7bf91b30 100644 --- a/interfaces/innerkits/appverify/test/BUILD.gn +++ b/interfaces/innerkits/appverify/test/BUILD.gn @@ -43,6 +43,7 @@ ohos_unittest("verify_test") { "unittest/src/provision_verify_test.cpp", "unittest/src/random_access_file_test.cpp", "unittest/src/trusted_root_ca_test.cpp", + "unittest/src/trusted_ticket_test.cpp", ] configs = [ ":verify_test_config" ] diff --git a/interfaces/innerkits/appverify/test/unittest/include/trusted_ticket_test.h b/interfaces/innerkits/appverify/test/unittest/include/trusted_ticket_test.h new file mode 100644 index 0000000000000000000000000000000000000000..4ace28ce13263476415d15c14db0cd03c130cd90 --- /dev/null +++ b/interfaces/innerkits/appverify/test/unittest/include/trusted_ticket_test.h @@ -0,0 +1,22 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +#ifndef HAP_TRUSTED_TICKET_TEST_H +#define HAP_TRUSTED_TICKET_TEST_H + +#include "test_const.h" + +int CreatTrustedTicketCA001(void); +int IsTrustedSourceTest001(void); +#endif // HAP_TRUSTED_TICKET_TEST_H \ No newline at end of file diff --git a/interfaces/innerkits/appverify/test/unittest/src/trusted_ticket_test.cpp b/interfaces/innerkits/appverify/test/unittest/src/trusted_ticket_test.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c0f9e7d4d166e0f6b00f02677d1bf1aca62494d7 --- /dev/null +++ b/interfaces/innerkits/appverify/test/unittest/src/trusted_ticket_test.cpp @@ -0,0 +1,114 @@ +/* + * Copyright (C) 2021 Huawei Device Co., Ltd. + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include "trusted_ticket_test.h" + +#include + +#include "init/trusted_ticket_manager.h" + +using namespace testing::ext; +using namespace OHOS::Security::Verify; + +namespace { +class TrustedTicketTest : public testing::Test { +public: + static void SetUpTestCase(void); + + static void TearDownTestCase(void); + + void SetUp(); + + void TearDown(); +}; + +void TrustedTicketTest::SetUpTestCase(void) +{ +} + +void TrustedTicketTest::TearDownTestCase(void) +{ +} + +void TrustedTicketTest::SetUp() +{ +} + +void TrustedTicketTest::TearDown() +{ +} + +/** + * @tc.name: Test trusted root init and debug mode + * @tc.desc: The static function will return true; + * @tc.type: FUNC + */ +HWTEST_F (TrustedTicketTest, CreatTrustedTicketCA001, TestSize.Level1) +{ + /* + * @tc.steps: step1. run init and recovery. + * @tc.expected: step1. the return will true. + */ + TrustedTicketManager& trustedticketsource = TrustedTicketManager::GetInstance(); + ASSERT_TRUE(trustedticketsource.Init()); + trustedticketsource.Recovery(); + ASSERT_TRUE(trustedticketsource.Init()); +} + +/** + * @tc.name: Test IsTrustedSource function + * @tc.desc: The static function will return object of matched cert; + * @tc.type: FUNC + */ +HWTEST_F (TrustedTicketTest, IsTrustedSourceTest001, TestSize.Level1) +{ + /* + * @tc.steps: step1. input nullptr. + * @tc.expected: step1. the return will be DO_NOT_MATCH, because certSubject and certIssuer are not matched. + */ + TrustedTicketManager& trustedticketsource = TrustedTicketManager::GetInstance(); + std::string certSubject = ""; + std::string certIssuer = ""; + ASSERT_TRUE(trustedticketsource.IsTrustedSource(certSubject, certIssuer, 1).matchState == DO_NOT_MATCH); + /* + * @tc.steps: step2. input invalid certSubject and valid certIssuer. + * @tc.expected: step2. the return will be DO_NOT_MATCH, because invalid certSubject. + */ + certSubject = "C=CN, O=Huawei, OU=MemberCenter, CN=MemberCenter Ticket Release V1"; + certIssuer = "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA"; + ASSERT_TRUE(trustedticketsource.IsTrustedSource(certSubject, certIssuer, 1).matchState == DO_NOT_MATCH); + /* + * @tc.steps: step3. input valid certSubject and invalid certIssuer. + * @tc.expected: step3. the return will be DO_NOT_MATCH, because invalid certIssuer. + */ + certSubject = "C=CN, O=Huawei, OU=MemberCenter, CN=MemberCenter ticket Release V1"; + certIssuer = "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service"; + ASSERT_TRUE(trustedticketsource.IsTrustedSource(certSubject, certIssuer, 1).matchState == DO_NOT_MATCH); + /* + * @tc.steps: step4. input valid certSubject and certIssuer. + * @tc.expected: step4. the return will be MATCH_WITH_TICKET. + */ + certSubject = "C=CN, O=Huawei, OU=MemberCenter, CN=MemberCenter ticket Release V1"; + certIssuer = "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA"; + ASSERT_TRUE(trustedticketsource.IsTrustedSource(certSubject, certIssuer, 3).matchState == MATCH_WITH_TICKET); + /* + * @tc.steps: step5. input invalid certListpath. + * @tc.expected: step5. the return will be DO_NOT_MATCH, because certListpath is not matched. + */ + certSubject = "C=CN, O=Huawei, OU=MemberCenter, CN=MemberCenter ticket Release V1"; + certIssuer = "C=CN, O=Huawei, OU=Huawei CBG, CN=Huawei CBG Software Signing Service CA"; + ASSERT_TRUE(trustedticketsource.IsTrustedSource(certSubject, certIssuer, 2).matchState == DO_NOT_MATCH); +} +}