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