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 16#include <algorithm> 17 18#include "nlohmann/json.hpp" 19#include "signature_tools_log.h" 20#include "signature_tools_errno.h" 21#include "profile_verify.h" 22 23using namespace std; 24using namespace nlohmann; 25 26namespace { 27const string KEY_VERSION_CODE = "version-code"; 28const string KEY_VERSION_NAME = "version-name"; 29const string KEY_UUID = "uuid"; 30const string KEY_TYPE = "type"; 31const string KEY_APP_DIST_TYPE = "app-distribution-type"; 32const string KEY_BUNDLE_INFO = "bundle-info"; 33const string KEY_DEVELOPER_ID = "developer-id"; 34const string KEY_DEVELOPMENT_CERTIFICATE = "development-certificate"; 35const string KEY_DISTRIBUTION_CERTIFICATE = "distribution-certificate"; 36const string KEY_BUNDLE_NAME = "bundle-name"; 37const string KEY_APL = "apl"; 38const string KEY_APP_FEATURE = "app-feature"; 39const string KEY_ACLS = "acls"; 40const string KEY_ALLOWED_ACLS = "allowed-acls"; 41const string KEY_PERMISSIONS = "permissions"; 42const string KEY_DATA_GROUP_IDS = "data-group-ids"; 43const string KEY_RESTRICTED_PERMISSIONS = "restricted-permissions"; 44const string KEY_RESTRICTED_CAPABILITIES = "restricted-capabilities"; 45const string KEY_DEBUG_INFO = "debug-info"; 46const string KEY_DEVICE_ID_TYPE = "device-id-type"; 47const string KEY_DEVICE_IDS = "device-ids"; 48const string KEY_ISSUER = "issuer"; 49const string KEY_APP_PRIVILEGE_CAPABILITIES = "app-privilege-capabilities"; 50const string KEY_APP_SERVICE_CAPABILITIES = "app-service-capabilities"; 51const string VALUE_TYPE_RELEASE = "release"; 52const string VALUE_TYPE_DEBUG = "debug"; 53const string VALUE_DIST_TYPE_APP_GALLERY = "app_gallery"; 54const string VALUE_DIST_TYPE_ENTERPRISE = "enterprise"; 55const string VALUE_DIST_TYPE_ENTERPRISE_NORMAL = "enterprise_normal"; 56const string VALUE_DIST_TYPE_ENTERPRISE_MDM = "enterprise_mdm"; 57const string VALUE_DIST_TYPE_OS_INTEGRATION = "os_integration"; 58const string VALUE_DIST_TYPE_CROWDTESTING = "crowdtesting"; 59const string VALUE_DEVICE_ID_TYPE_UDID = "udid"; 60const string VALUE_VALIDITY = "validity"; 61const string VALUE_NOT_BEFORE = "not-before"; 62const string VALUE_NOT_AFTER = "not-after"; 63// reserved field 64const string KEY_BASEAPP_INFO = "baseapp-info"; 65const string KEY_PACKAGE_NAME = "package-name"; 66const string KEY_PACKAGE_CERT = "package-cert"; 67const string KEY_APP_IDENTIFIER = "app-identifier"; 68const string GENERIC_BUNDLE_NAME = ".*"; 69 70inline void GetStringIfExist(const json& obj, const string& key, string& out) 71{ 72 if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_string()) { 73 obj[key.c_str()].get_to(out); 74 } 75} 76inline void GetInt32IfExist(const json& obj, const string& key, int32_t& out) 77{ 78 if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_number_integer()) { 79 obj[key.c_str()].get_to(out); 80 } 81} 82inline void GetInt64IfExist(const json& obj, const string& key, int64_t& out) 83{ 84 if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_number_integer()) { 85 obj[key.c_str()].get_to(out); 86 } 87} 88inline void GetStringArrayIfExist(const json& obj, const string& key, vector<string>& out) 89{ 90 if (obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_array()) { 91 for (auto& item : obj[key.c_str()]) { 92 if (item.is_string()) { 93 out.push_back(item.get<string>()); 94 } 95 } 96 } 97} 98inline bool IsObjectExist(const json& obj, const string& key) 99{ 100 return obj.find(key.c_str()) != obj.end() && obj[key.c_str()].is_object(); 101} 102} // namespace 103namespace OHOS { 104namespace SignatureTools { 105const std::map<std::string, int32_t> distTypeMap = { 106 {VALUE_DIST_TYPE_APP_GALLERY, AppDistType::APP_GALLERY}, 107 {VALUE_DIST_TYPE_ENTERPRISE, AppDistType::ENTERPRISE}, 108 {VALUE_DIST_TYPE_ENTERPRISE_NORMAL, AppDistType::ENTERPRISE_NORMAL}, 109 {VALUE_DIST_TYPE_ENTERPRISE_MDM, AppDistType::ENTERPRISE_MDM}, 110 {VALUE_DIST_TYPE_OS_INTEGRATION, AppDistType::OS_INTEGRATION}, 111 {VALUE_DIST_TYPE_CROWDTESTING, AppDistType::CROWDTESTING} 112}; 113 114void ParseType(const json& obj, ProfileInfo& out) 115{ 116 string type; 117 GetStringIfExist(obj, KEY_TYPE, type); 118 /* If not release, then it's debug */ 119 if (type == VALUE_TYPE_RELEASE) 120 out.type = RELEASE; 121 else if (type == VALUE_TYPE_DEBUG) 122 out.type = DEBUG; 123 else out.type = NONE_PROVISION_TYPE; 124} 125void ParseAppDistType(const json& obj, ProfileInfo& out) 126{ 127 string distType; 128 GetStringIfExist(obj, KEY_APP_DIST_TYPE, distType); 129 auto ite = distTypeMap.find(distType); 130 if (ite != distTypeMap.end()) { 131 out.distributionType = static_cast<AppDistType>(distTypeMap.at(distType)); 132 return; 133 } 134 out.distributionType = AppDistType::NONE_TYPE; 135} 136void ParseBundleInfo(const json& obj, ProfileInfo& out) 137{ 138 if (IsObjectExist(obj, KEY_BUNDLE_INFO)) { 139 const auto& bundleInfo = obj[KEY_BUNDLE_INFO]; 140 GetStringIfExist(bundleInfo, KEY_DEVELOPER_ID, out.bundleInfo.developerId); 141 GetStringIfExist(bundleInfo, KEY_DEVELOPMENT_CERTIFICATE, 142 out.bundleInfo.developmentCertificate); 143 GetStringIfExist(bundleInfo, KEY_DISTRIBUTION_CERTIFICATE, 144 out.bundleInfo.distributionCertificate); 145 GetStringIfExist(bundleInfo, KEY_BUNDLE_NAME, out.bundleInfo.bundleName); 146 GetStringIfExist(bundleInfo, KEY_APL, out.bundleInfo.apl); 147 GetStringIfExist(bundleInfo, KEY_APP_FEATURE, out.bundleInfo.appFeature); 148 GetStringIfExist(bundleInfo, KEY_APP_IDENTIFIER, out.bundleInfo.appIdentifier); 149 GetStringArrayIfExist(bundleInfo, KEY_DATA_GROUP_IDS, out.bundleInfo.dataGroupIds); 150 } 151} 152void ParseAcls(const json& obj, ProfileInfo& out) 153{ 154 if (IsObjectExist(obj, KEY_ACLS)) { 155 const auto& acls = obj[KEY_ACLS]; 156 GetStringArrayIfExist(acls, KEY_ALLOWED_ACLS, out.acls.allowedAcls); 157 } 158} 159void ParsePermissions(const json& obj, ProfileInfo& out) 160{ 161 if (IsObjectExist(obj, KEY_PERMISSIONS)) { 162 const auto& permissions = obj[KEY_PERMISSIONS]; 163 GetStringArrayIfExist(permissions, KEY_RESTRICTED_PERMISSIONS, 164 out.permissions.restrictedPermissions); 165 GetStringArrayIfExist(permissions, KEY_RESTRICTED_CAPABILITIES, 166 out.permissions.restrictedCapabilities); 167 } 168} 169void ParseDebugInfo(const json& obj, ProfileInfo& out) 170{ 171 if (IsObjectExist(obj, KEY_DEBUG_INFO)) { 172 GetStringIfExist(obj[KEY_DEBUG_INFO], KEY_DEVICE_ID_TYPE, out.debugInfo.deviceIdType); 173 GetStringArrayIfExist(obj[KEY_DEBUG_INFO], KEY_DEVICE_IDS, out.debugInfo.deviceIds); 174 } 175} 176void ParseValidity(const json& obj, Validity& out) 177{ 178 if (IsObjectExist(obj, VALUE_VALIDITY)) { 179 GetInt64IfExist(obj[VALUE_VALIDITY], VALUE_NOT_BEFORE, out.notBefore); 180 GetInt64IfExist(obj[VALUE_VALIDITY], VALUE_NOT_AFTER, out.notAfter); 181 } 182} 183void ParseMetadata(const json& obj, ProfileInfo& out) 184{ 185 if (IsObjectExist(obj, KEY_BASEAPP_INFO)) { 186 const auto& baseAppInfo = obj[KEY_BASEAPP_INFO]; 187 Metadata metadata; 188 metadata.name = KEY_PACKAGE_NAME; 189 GetStringIfExist(baseAppInfo, KEY_PACKAGE_NAME, metadata.value); 190 out.metadatas.emplace_back(metadata); 191 metadata.name = KEY_PACKAGE_CERT; 192 GetStringIfExist(baseAppInfo, KEY_PACKAGE_CERT, metadata.value); 193 out.metadatas.emplace_back(metadata); 194 } 195} 196void from_json(const json& obj, ProfileInfo& out) 197{ 198 if (!obj.is_object()) { 199 return; 200 } 201 GetInt32IfExist(obj, KEY_VERSION_CODE, out.versionCode); 202 GetStringIfExist(obj, KEY_VERSION_NAME, out.versionName); 203 GetStringIfExist(obj, KEY_UUID, out.uuid); 204 ParseType(obj, out); 205 ParseAppDistType(obj, out); 206 ParseBundleInfo(obj, out); 207 ParseAcls(obj, out); 208 ParsePermissions(obj, out); 209 ParseDebugInfo(obj, out); 210 GetStringIfExist(obj, KEY_ISSUER, out.issuer); 211 GetStringArrayIfExist(obj, KEY_APP_PRIVILEGE_CAPABILITIES, out.appPrivilegeCapabilities); 212 ParseValidity(obj, out.validity); 213 ParseMetadata(obj, out); 214 GetStringIfExist(obj, KEY_APP_SERVICE_CAPABILITIES, out.appServiceCapabilities); 215} 216 217AppProvisionVerifyResult ReturnIfStringIsEmpty(const std::string& str, const std::string& errMsg) 218{ 219 if (str.empty()) { 220 PrintErrorNumberMsg("PROVISION_INVALID_ERROR", PROVISION_INVALID_ERROR, errMsg); 221 return PROVISION_INVALID; 222 } 223 return PROVISION_OK; 224} 225 226AppProvisionVerifyResult ReturnIfIntIsNonPositive(int num, const std::string& errMsg) 227{ 228 if (num <= 0) { 229 PrintErrorNumberMsg("PROVISION_INVALID_ERROR", PROVISION_INVALID_ERROR, errMsg); 230 return PROVISION_INVALID; 231 } 232 return PROVISION_OK; 233} 234 235static AppProvisionVerifyResult CheckProfileValidType(ProfileInfo& info) 236{ 237 if (info.type == ProvisionType::DEBUG) { 238 if (ReturnIfStringIsEmpty(info.bundleInfo.developmentCertificate, 239 "Tag development-certificate is empty.") != PROVISION_OK) { 240 return PROVISION_INVALID; 241 } 242 } else if (info.type == ProvisionType::RELEASE) { 243 if (ReturnIfIntIsNonPositive(info.distributionType, 244 "Tag app-distribution-type is empty.") != PROVISION_OK) { 245 return PROVISION_INVALID; 246 } 247 if (ReturnIfStringIsEmpty(info.bundleInfo.distributionCertificate, 248 "Tag distribution-certificate is empty.") != PROVISION_OK) { 249 return PROVISION_INVALID; 250 } 251 } else { 252 PrintErrorNumberMsg("PROVISION_INVALID_ERROR", PROVISION_INVALID_ERROR, 253 "The type field in the profile file is incorrect"); 254 return PROVISION_INVALID; 255 } 256 return PROVISION_OK; 257} 258 259AppProvisionVerifyResult ParseProvision(const string& appProvision, ProfileInfo& info) 260{ 261 if (ParseProfile(appProvision, info) != PROVISION_OK) { 262 return PROVISION_INVALID; 263 } 264 265 if (CheckProfileValidType(info) != PROVISION_OK) { 266 return PROVISION_INVALID; 267 } 268 return PROVISION_OK; 269} 270 271AppProvisionVerifyResult ParseAndVerify(const string& appProvision, ProfileInfo& info) 272{ 273 SIGNATURE_TOOLS_LOGD("Enter HarmonyAppProvision Verify"); 274 AppProvisionVerifyResult ret = ParseProvision(appProvision, info); 275 if (ret != PROVISION_OK) { 276 return ret; 277 } 278 SIGNATURE_TOOLS_LOGD("Leave HarmonyAppProvision Verify"); 279 return PROVISION_OK; 280} 281AppProvisionVerifyResult ParseProfile(const std::string& appProvision, ProfileInfo& info) 282{ 283 json obj = json::parse(appProvision, nullptr, false); 284 if (obj.is_discarded() || (!obj.is_structured())) { 285 std::string errStr = "invalid json object, parse provision failed, json: " + appProvision; 286 PrintErrorNumberMsg("PROVISION_INVALID_ERROR", PROVISION_INVALID_ERROR, errStr.c_str()); 287 return PROVISION_INVALID; 288 } 289 obj.get_to(info); 290 return PROVISION_OK; 291} 292} // namespace SignatureTools 293} // namespace OHOS