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