• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (c) 2023-2023 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 package com.ohos.hapsigntool.codesigning.utils;
17 
18 import com.google.gson.JsonArray;
19 import com.google.gson.JsonElement;
20 import com.google.gson.JsonObject;
21 import com.google.gson.JsonParseException;
22 import com.google.gson.JsonParser;
23 import com.google.gson.JsonPrimitive;
24 import com.google.gson.JsonSyntaxException;
25 import com.google.gson.stream.JsonReader;
26 import com.ohos.hapsigntool.codesigning.exception.CodeSignErrMsg;
27 import com.ohos.hapsigntool.entity.Pair;
28 import com.ohos.hapsigntool.error.ProfileException;
29 import com.ohos.hapsigntool.utils.LogUtils;
30 
31 import java.io.IOException;
32 import java.io.InputStreamReader;
33 import java.nio.charset.StandardCharsets;
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.HashMap;
37 import java.util.List;
38 import java.util.Map;
39 import java.util.jar.JarEntry;
40 import java.util.jar.JarFile;
41 
42 /**
43  * utility for check hap configs
44  *
45  * @since 2023/06/05
46  */
47 public class HapUtils {
48     /**
49      * DEBUG_LIB_ID
50      */
51     public static final String HAP_DEBUG_OWNER_ID = "DEBUG_LIB_ID";
52 
53     /**
54      * SHARED_LIB_ID
55      */
56     public static final String HAP_SHARED_OWNER_ID = "SHARED_LIB_ID";
57 
58     private static final LogUtils LOGGER = new LogUtils(HapUtils.class);
59 
60     private static final String COMPRESS_NATIVE_LIBS_OPTION = "compressNativeLibs";
61 
62     private static final List<String> HAP_CONFIG_FILES = new ArrayList<>();
63 
64     private static final String HAP_FA_CONFIG_JSON_FILE = "config.json";
65 
66     private static final String HAP_STAGE_MODULE_JSON_FILE = "module.json";
67 
68     private static final int MAX_APP_ID_LEN = 32; // max app-identifier in profile
69 
70     static {
71         HAP_CONFIG_FILES.add(HAP_FA_CONFIG_JSON_FILE);
72         HAP_CONFIG_FILES.add(HAP_STAGE_MODULE_JSON_FILE);
73     }
74 
HapUtils()75     private HapUtils() {
76     }
77 
78     /**
79      * get app-id from profile
80      *
81      * @param profileContent the content of profile
82      * @return string value of app-id
83      * @throws ProfileException profile is invalid
84      */
getAppIdentifier(String profileContent)85     public static String getAppIdentifier(String profileContent) throws ProfileException {
86         Pair<String, String> resultPair = parseAppIdentifier(profileContent);
87         String ownerID = resultPair.getFirst();
88         String profileType = resultPair.getSecond();
89         if ("debug".equals(profileType)) {
90             return HAP_DEBUG_OWNER_ID;
91         } else if ("release".equals(profileType)) {
92             return ownerID;
93         } else {
94             throw new ProfileException(CodeSignErrMsg.PROFILE_TYPE_UNSUPPORTED_ERROR.toString());
95         }
96     }
97 
98     /**
99      * parse app-id and profileType from profile
100      *
101      * @param profileContent the content of profile
102      * @return Pair value of app-id and profileType
103      * @throws ProfileException profile is invalid
104      */
parseAppIdentifier(String profileContent)105     public static Pair<String, String> parseAppIdentifier(String profileContent) throws ProfileException {
106         String ownerID = null;
107         String profileType = null;
108         try {
109             JsonElement parser = JsonParser.parseString(profileContent);
110             JsonObject profileJson = parser.getAsJsonObject();
111             String profileTypeKey = "type";
112             if (!profileJson.has(profileTypeKey)) {
113                 throw new ProfileException(CodeSignErrMsg.PROFILE_TYPE_NOT_EXISTED_ERROR.toString());
114             }
115 
116             profileType = profileJson.get(profileTypeKey).getAsString();
117             if (profileType == null || profileType.isEmpty()) {
118                 throw new ProfileException(CodeSignErrMsg.PROFILE_TYPE_NOT_EXISTED_ERROR.toString());
119             }
120 
121             String appIdentifier = "app-identifier";
122             String buildInfoMember = "bundle-info";
123             JsonObject buildInfoObject = profileJson.getAsJsonObject(buildInfoMember);
124             if (buildInfoObject == null) {
125                 throw new ProfileException(CodeSignErrMsg.PROFILE_BUNDLE_INFO_NOT_EXISTED_ERROR.toString());
126             }
127             if (buildInfoObject.has(appIdentifier)) {
128                 JsonElement ownerIDElement = buildInfoObject.get(appIdentifier);
129                 if (!ownerIDElement.getAsJsonPrimitive().isString()) {
130                     throw new ProfileException(CodeSignErrMsg.PROFILE_APPID_VALUE_TYPE_ERROR.toString());
131                 }
132                 ownerID = ownerIDElement.getAsString();
133                 if (ownerID.isEmpty() || ownerID.length() > MAX_APP_ID_LEN) {
134                     throw new ProfileException(CodeSignErrMsg.PROFILE_APPID_VALUE_LENGTH_ERROR.toString());
135                 }
136             }
137         } catch (JsonSyntaxException | UnsupportedOperationException e) {
138             throw new ProfileException(CodeSignErrMsg.PROFILE_JSON_PARSE_ERROR.toString(), e);
139         }
140         LOGGER.info("profile type is: {}", profileType);
141         return Pair.create(ownerID, profileType);
142     }
143 
144     /**
145      * get hnp app-id from profile when type is public
146      *
147      * @param profileContent the content of profile
148      * @return ownerid
149      */
getPublicHnpOwnerId(String profileContent)150     public static String getPublicHnpOwnerId(String profileContent) {
151         // property type
152         String publicOwnerID = "";
153         JsonElement parser = JsonParser.parseString(profileContent);
154         JsonObject profileJson = parser.getAsJsonObject();
155         String profileTypeKey = "type";
156         JsonPrimitive profileType = profileJson.getAsJsonPrimitive(profileTypeKey);
157         if (profileType != null) {
158             if ("debug".equals(profileType.getAsString())) {
159                 publicOwnerID = HAP_DEBUG_OWNER_ID;
160             } else if ("release".equals(profileType.getAsString())) {
161                 publicOwnerID = HAP_SHARED_OWNER_ID;
162             }
163         }
164         return publicOwnerID;
165     }
166 
167     /**
168      * get hnp path behind "hnp/abi/"
169      *
170      * @param path filepath
171      * @return hnp path behind "hnp/abi/"
172      */
parseHnpPath(String path)173     public static String parseHnpPath(String path) {
174         if (path == null || path.isEmpty()) {
175             return "";
176         }
177         String[] strings = path.split("/");
178         if (strings.length < 3) {
179             return "";
180         }
181         // get hnp path behind "hnp/abi/"
182         strings = Arrays.copyOfRange(strings, 2, strings.length);
183         return String.join("/", strings);
184     }
185 
186     /**
187      * get map of hnp name and type from module.json
188      *
189      * @param inputJar hap file
190      * @return packageName-type map
191      * @throws IOException when IO error occurred
192      * @throws ProfileException profile is invalid
193      */
getHnpsFromJson(JarFile inputJar)194     public static Map<String, String> getHnpsFromJson(JarFile inputJar) throws IOException, ProfileException {
195         // get module.json
196         Map<String, String> hnpNameMap = new HashMap<>();
197         JarEntry moduleEntry = inputJar.getJarEntry("module.json");
198         if (moduleEntry == null) {
199             return hnpNameMap;
200         }
201         try (JsonReader reader = new JsonReader(
202             new InputStreamReader(inputJar.getInputStream(moduleEntry), StandardCharsets.UTF_8))) {
203             JsonElement jsonElement = JsonParser.parseReader(reader);
204             JsonObject jsonObject = jsonElement.getAsJsonObject();
205             JsonObject moduleObject = jsonObject.getAsJsonObject("module");
206             JsonArray hnpPackageArr = moduleObject.getAsJsonArray("hnpPackages");
207             if (hnpPackageArr == null || hnpPackageArr.isEmpty()) {
208                 LOGGER.debug("module.json has no hnpPackages key or hnpPackages value is empty");
209                 return hnpNameMap;
210             }
211             hnpPackageArr.iterator().forEachRemaining((element) -> {
212                 JsonObject hnpPackage = element.getAsJsonObject();
213                 JsonPrimitive hnpName = hnpPackage.getAsJsonPrimitive("package");
214                 if (hnpName == null || hnpName.getAsString().isEmpty()) {
215                     return;
216                 }
217                 hnpNameMap.put(hnpName.getAsString(), "private");
218                 JsonPrimitive type = hnpPackage.getAsJsonPrimitive("type");
219                 if (type != null && !type.getAsString().isEmpty()) {
220                     hnpNameMap.put(hnpName.getAsString(), type.getAsString());
221                 }
222             });
223         } catch (JsonParseException e) {
224             throw new ProfileException(CodeSignErrMsg.MODULE_JSON_PARSE_ERROR.toString(), e);
225         }
226         return hnpNameMap;
227     }
228 
229 }
230