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