1 /* 2 * Copyright (C) 2021 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.se.security; 18 19 import android.os.Build; 20 import android.os.SystemProperties; 21 import android.util.Log; 22 23 import org.xmlpull.v1.XmlPullParser; 24 import org.xmlpull.v1.XmlPullParserException; 25 import org.xmlpull.v1.XmlPullParserFactory; 26 27 import java.io.File; 28 import java.io.FileInputStream; 29 import java.io.IOException; 30 import java.io.InputStream; 31 import java.util.ArrayList; 32 import java.util.Arrays; 33 import java.util.HashMap; 34 import java.util.List; 35 import java.util.Map; 36 37 /** 38 * Parser for HAL_UID_MAP.XML 39 * Parses the xml file and collects HAL references (UUID) to identify the corresponding 40 * access rules for the HAL services. 41 */ 42 public class HalRefDoParser { 43 44 private static final boolean DEBUG = Build.isDebuggable(); 45 private final String mTag = "SecureElement-HalRefDoParser"; 46 47 private static final String PROP_PRODUCT_HARDWARE_SKU = "ro.boot.product.hardware.sku"; 48 private static final String UUID_MAPPING_CONFIG_PREFIX = "hal_uuid_map_"; 49 private static final String UUID_MAPPING_CONFIG_EXT = ".xml"; 50 private static final String[] UUID_MAPPING_CONFIG_PATHS = {"/odm/etc/", "/vendor/etc/", 51 "/etc/"}; 52 53 // Holds UUID to UIDs mapping 54 private final Map<Integer, byte[]> mUUIDMap = new HashMap<Integer, byte[]>(); 55 56 private static final String REF_DO = "ref_do"; 57 private static final String UUID_REF_DO = "uuid_ref_do"; 58 private static final String UUID = "uuid"; 59 private static final String UIDS = "uids"; 60 private static final String UID = "uid"; 61 62 private static final byte[] PADDING_BYTES = { 63 (byte) 0xff, (byte) 0xff, (byte) 0xff, (byte) 0xff}; 64 HalRefDoParser()65 private HalRefDoParser() { 66 parseUuidMappings(); 67 } 68 69 private static class HalRefParserSingleton { 70 private static final HalRefDoParser INSTANCE = new HalRefDoParser(); 71 } 72 getInstance()73 public static HalRefDoParser getInstance() { 74 return HalRefParserSingleton.INSTANCE; 75 } 76 getUuidMapConfigFile()77 private File getUuidMapConfigFile() { 78 // default file name: hal_uuid_map_config.xml 79 String uuid_map_config_file_name = UUID_MAPPING_CONFIG_PREFIX 80 + SystemProperties.get(PROP_PRODUCT_HARDWARE_SKU, "config") 81 + UUID_MAPPING_CONFIG_EXT; 82 String uuid_map_config_path = null; 83 84 try { 85 // Search in predefined folders 86 for (String path : UUID_MAPPING_CONFIG_PATHS) { 87 uuid_map_config_path = path + uuid_map_config_file_name; 88 File confFile = new File(uuid_map_config_path); 89 if (confFile.exists()) { 90 Log.d(mTag, "UUID mapping config file path: " + uuid_map_config_path); 91 return confFile; 92 } 93 } 94 } catch (Exception e) { 95 Log.e(mTag, "Error in finding UUID mapping config file path: " + uuid_map_config_path); 96 } 97 98 return null; 99 } 100 101 /** 102 * Parses the below mapping structure - 103 * 104 * <ref_do> 105 * <uuid_ref_do> 106 * <uids> 107 * <uid>1000</uid> 108 * </uids> 109 * <uuid>a9b7ba70783b317e9998dc4dd82eb3c5</uuid> 110 * </uuid_ref_do> 111 * </ref_do> 112 */ parse(InputStream is)113 private void parse(InputStream is) { 114 try { 115 XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); 116 factory.setNamespaceAware(true); 117 XmlPullParser parser = factory.newPullParser(); 118 String text = null; 119 List<Integer> uids = null; 120 byte[] uuid = null; 121 122 parser.setInput(is, null); 123 124 int eventType = parser.getEventType(); 125 while (eventType != XmlPullParser.END_DOCUMENT) { 126 String tagname = parser.getName(); 127 switch (eventType) { 128 case XmlPullParser.START_TAG: 129 if (tagname.equalsIgnoreCase(UIDS)) { 130 uids = new ArrayList<Integer>(); 131 } else if (tagname.equalsIgnoreCase(UUID)) { 132 uuid = null; 133 } 134 break; 135 136 case XmlPullParser.TEXT: 137 text = parser.getText(); 138 break; 139 140 case XmlPullParser.END_TAG: 141 if (tagname.equalsIgnoreCase(UUID_REF_DO)) { 142 if (uuid != null) { 143 for (int uid : uids) { 144 mUUIDMap.put(uid, uuid); 145 } 146 } 147 } else if (tagname.equalsIgnoreCase(UID)) { 148 uids.add(Integer.parseInt(text)); 149 } else if (tagname.equalsIgnoreCase(UUID)) { 150 byte[] uuidValue = decodeHexUUID(text); 151 uuid = new byte[uuidValue.length + PADDING_BYTES.length]; 152 System.arraycopy(PADDING_BYTES, 0, uuid, 0, PADDING_BYTES.length); 153 System.arraycopy(uuidValue, 0, uuid, PADDING_BYTES.length, 154 uuidValue.length); 155 156 } 157 break; 158 159 default: 160 break; 161 } 162 eventType = parser.next(); 163 } 164 165 } catch (XmlPullParserException e) { 166 Log.e(mTag, "Error while parsing hal uuid mappings"); 167 Log.e(mTag, e.getMessage()); 168 } catch (IOException e) { 169 Log.e(mTag, "IO error while parsing hal uuid mappings"); 170 Log.e(mTag, e.getMessage()); 171 } 172 } 173 174 /** 175 * Finds the uuid mapping config file path from predefined folders 176 * Parses the uuid mapping config file 177 */ parseUuidMappings()178 private void parseUuidMappings() { 179 try { 180 File uuid_map_file = getUuidMapConfigFile(); 181 if (uuid_map_file == null) { 182 Log.e(mTag, "Unable to determine UUID mapping config file path"); 183 return; 184 } 185 186 parse(new FileInputStream(uuid_map_file)); 187 } catch (Exception e) { 188 Log.e(mTag, "Unable to parse hal uuid mappings"); 189 Log.e(mTag, e.getMessage()); 190 } 191 192 if (DEBUG) { 193 for (Map.Entry<Integer, byte[]> entry : mUUIDMap.entrySet()) { 194 Log.d(mTag, "UID: " + entry.getKey()); 195 Log.d(mTag, "UUID: " + Arrays.toString(entry.getValue())); 196 } 197 } 198 } 199 200 /** 201 * Finds UUID for the give UID 202 */ findUUID(int uid)203 public byte[] findUUID(int uid) { 204 return mUUIDMap.get(uid); 205 } 206 207 /** 208 * Convert char to hex digit 209 * @param hexChar 210 * @return hex digit 211 */ toDigit(char hexChar)212 private int toDigit(char hexChar) { 213 int digit = Character.digit(hexChar, 16); 214 if (digit == -1) { 215 throw new IllegalArgumentException( 216 "Invalid Hexadecimal Character: " + hexChar); 217 } 218 return digit; 219 } 220 221 /** 222 * Convert hex digits string to bytes 223 * @param hextText 224 * @return hex byte 225 */ hexToByte(char ch1, char ch2)226 private byte hexToByte(char ch1, char ch2) { 227 int firstDigit = toDigit(ch1); 228 int secondDigit = toDigit(ch2); 229 return (byte) ((firstDigit << 4) + secondDigit); 230 } 231 232 /** 233 * Convert hex string to hex byte array 234 * @param hextText 235 * @return hex bytes 236 */ decodeHexUUID(String hextText)237 private byte[] decodeHexUUID(String hextText) { 238 if (hextText == null || hextText.length() != 32) { 239 throw new IllegalArgumentException( 240 "Invalid UUID supplied"); 241 } 242 243 byte[] bytes = new byte[hextText.length() / 2]; 244 for (int i = 0; i < hextText.length(); i += 2) { 245 bytes[i / 2] = hexToByte(hextText.charAt(i), hextText.charAt(i + 1)); 246 } 247 return bytes; 248 } 249 250 } 251