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 
23 using namespace std;
24 using namespace nlohmann;
25 
26 namespace {
27 const string KEY_VERSION_CODE = "version-code";
28 const string KEY_VERSION_NAME = "version-name";
29 const string KEY_UUID = "uuid";
30 const string KEY_TYPE = "type";
31 const string KEY_APP_DIST_TYPE = "app-distribution-type";
32 const string KEY_BUNDLE_INFO = "bundle-info";
33 const string KEY_DEVELOPER_ID = "developer-id";
34 const string KEY_DEVELOPMENT_CERTIFICATE = "development-certificate";
35 const string KEY_DISTRIBUTION_CERTIFICATE = "distribution-certificate";
36 const string KEY_BUNDLE_NAME = "bundle-name";
37 const string KEY_APL = "apl";
38 const string KEY_APP_FEATURE = "app-feature";
39 const string KEY_ACLS = "acls";
40 const string KEY_ALLOWED_ACLS = "allowed-acls";
41 const string KEY_PERMISSIONS = "permissions";
42 const string KEY_DATA_GROUP_IDS = "data-group-ids";
43 const string KEY_RESTRICTED_PERMISSIONS = "restricted-permissions";
44 const string KEY_RESTRICTED_CAPABILITIES = "restricted-capabilities";
45 const string KEY_DEBUG_INFO = "debug-info";
46 const string KEY_DEVICE_ID_TYPE = "device-id-type";
47 const string KEY_DEVICE_IDS = "device-ids";
48 const string KEY_ISSUER = "issuer";
49 const string KEY_APP_PRIVILEGE_CAPABILITIES = "app-privilege-capabilities";
50 const string KEY_APP_SERVICE_CAPABILITIES = "app-service-capabilities";
51 const string VALUE_TYPE_RELEASE = "release";
52 const string VALUE_TYPE_DEBUG = "debug";
53 const string VALUE_DIST_TYPE_APP_GALLERY = "app_gallery";
54 const string VALUE_DIST_TYPE_ENTERPRISE = "enterprise";
55 const string VALUE_DIST_TYPE_ENTERPRISE_NORMAL = "enterprise_normal";
56 const string VALUE_DIST_TYPE_ENTERPRISE_MDM = "enterprise_mdm";
57 const string VALUE_DIST_TYPE_OS_INTEGRATION = "os_integration";
58 const string VALUE_DIST_TYPE_CROWDTESTING = "crowdtesting";
59 const string VALUE_DEVICE_ID_TYPE_UDID = "udid";
60 const string VALUE_VALIDITY = "validity";
61 const string VALUE_NOT_BEFORE = "not-before";
62 const string VALUE_NOT_AFTER = "not-after";
63 // reserved field
64 const string KEY_BASEAPP_INFO = "baseapp-info";
65 const string KEY_PACKAGE_NAME = "package-name";
66 const string KEY_PACKAGE_CERT = "package-cert";
67 const string KEY_APP_IDENTIFIER = "app-identifier";
68 const string GENERIC_BUNDLE_NAME = ".*";
69 
GetStringIfExist(const json& obj, const string& key, string& out)70 inline 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 }
GetInt32IfExist(const json& obj, const string& key, int32_t& out)76 inline 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 }
GetInt64IfExist(const json& obj, const string& key, int64_t& out)82 inline 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 }
GetStringArrayIfExist(const json& obj, const string& key, vector<string>& out)88 inline 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 }
IsObjectExist(const json& obj, const string& key)98 inline 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
103 namespace OHOS {
104 namespace SignatureTools {
105 const 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 
ParseType(const json& obj, ProfileInfo& out)114 void 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 }
ParseAppDistType(const json& obj, ProfileInfo& out)125 void 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 }
ParseBundleInfo(const json& obj, ProfileInfo& out)136 void 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 }
ParseAcls(const json& obj, ProfileInfo& out)152 void 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 }
ParsePermissions(const json& obj, ProfileInfo& out)159 void 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 }
ParseDebugInfo(const json& obj, ProfileInfo& out)169 void 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 }
ParseValidity(const json& obj, Validity& out)176 void 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 }
ParseMetadata(const json& obj, ProfileInfo& out)183 void 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 }
from_json(const json& obj, ProfileInfo& out)196 void 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 
ReturnIfStringIsEmpty(const std::string& str, const std::string& errMsg)217 AppProvisionVerifyResult 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 
ReturnIfIntIsNonPositive(int num, const std::string& errMsg)226 AppProvisionVerifyResult 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 
CheckProfileValidType(ProfileInfo& info)235 static 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 
ParseProvision(const string& appProvision, ProfileInfo& info)259 AppProvisionVerifyResult 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 
ParseAndVerify(const string& appProvision, ProfileInfo& info)271 AppProvisionVerifyResult 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 }
ParseProfile(const std::string& appProvision, ProfileInfo& info)281 AppProvisionVerifyResult 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