• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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.phone.utils;
18 
19 import android.annotation.TestApi;
20 import android.content.Context;
21 import android.content.pm.PackageInfo;
22 import android.content.pm.PackageManager;
23 import android.content.pm.Signature;
24 import android.telephony.Rlog;
25 import android.text.TextUtils;
26 import android.util.Log;
27 
28 import com.android.internal.telephony.uicc.IccUtils;
29 
30 import org.json.JSONArray;
31 import org.json.JSONException;
32 import org.json.JSONObject;
33 
34 import java.io.IOException;
35 import java.io.InputStream;
36 import java.security.MessageDigest;
37 import java.security.NoSuchAlgorithmException;
38 import java.util.ArrayList;
39 import java.util.Collections;
40 import java.util.List;
41 import java.util.Set;
42 
43 
44 public class CarrierAllowListInfo {
45     private static final String LOG_TAG = "CarrierAllowListInfo";
46     private JSONObject mDataJSON;
47     private static final String JSON_CHARSET = "UTF-8";
48     private static final String MESSAGE_DIGEST_ALGORITHM = "SHA1";
49     private static final String CALLER_SHA_1_ID = "callerSHA1Id";
50     private static final String CALLER_CARRIER_ID = "carrierId";
51     public static final int INVALID_CARRIER_ID = -1;
52 
53     private static final String CARRIER_RESTRICTION_OPERATOR_REGISTERED_FILE =
54             "CarrierRestrictionOperatorDetails.json";
55 
56     private static CarrierAllowListInfo mInstance = null;
57     private Context mContext;
58 
CarrierAllowListInfo(Context context)59     private CarrierAllowListInfo(Context context) {
60         mContext = context;
61         loadJsonFile(context);
62     }
63 
loadInstance(Context context)64     public static CarrierAllowListInfo loadInstance(Context context) {
65         if (mInstance == null) {
66             mInstance = new CarrierAllowListInfo(context);
67         }
68         return mInstance;
69     }
70 
validateCallerAndGetCarrierId(String packageName)71     public int validateCallerAndGetCarrierId(String packageName) {
72         CarrierInfo carrierInfo = parseJsonForCallerInfo(packageName);
73         boolean isValid = (carrierInfo != null) && validateCallerSignature(mContext, packageName,
74                 carrierInfo.getSHAIdList());
75         return (isValid) ? carrierInfo.getCallerCarrierId() : INVALID_CARRIER_ID;
76     }
77 
loadJsonFile(Context context)78     private void loadJsonFile(Context context) {
79         try {
80             String jsonString = getJsonFromAssets(context,
81                     CARRIER_RESTRICTION_OPERATOR_REGISTERED_FILE, JSON_CHARSET);
82             if (!TextUtils.isEmpty(jsonString)) {
83                 mDataJSON = new JSONObject(jsonString);
84             }
85         } catch (Exception ex) {
86             Rlog.e(LOG_TAG, "CarrierAllowListInfo: JSON file reading exception = " + ex);
87         }
88     }
89 
90     /**
91      * Parse the JSON object to fetch the given caller's SHA-Ids and carrierId.
92      */
parseJsonForCallerInfo(String callerPackage)93     private CarrierInfo parseJsonForCallerInfo(String callerPackage) {
94         try {
95             if (mDataJSON != null && callerPackage != null) {
96                 JSONObject callerJSON = mDataJSON.getJSONObject(callerPackage.trim());
97                 JSONArray callerJSONArray = callerJSON.getJSONArray(CALLER_SHA_1_ID);
98                 int carrierId = callerJSON.getInt(CALLER_CARRIER_ID);
99                 List<String> appSignatures = new ArrayList<>();
100                 for (int index = 0; index < callerJSONArray.length(); index++) {
101                     appSignatures.add((String) callerJSONArray.get(index));
102                 }
103                 return new CarrierInfo(carrierId, appSignatures);
104             }
105         } catch (JSONException ex) {
106             Rlog.e(LOG_TAG, "getCallerSignatureInfo: JSONException = " + ex);
107         }
108         return null;
109     }
110 
111     /**
112      * Read the Json file from the assert folder.
113      *
114      * @param context  context
115      * @param fileName JSON file name in assets folder
116      * @param charset  JSON file data format
117      * @return JSON file content in string format or null in case of IOException
118      */
getJsonFromAssets(Context context, String fileName, String charset)119     private static String getJsonFromAssets(Context context, String fileName, String charset) {
120         String jsonStr;
121         try {
122             InputStream ipStream = context.getAssets().open(fileName);
123             int bufSize = ipStream.available();
124             byte[] fileBuffer = new byte[bufSize];
125             ipStream.read(fileBuffer);
126             ipStream.close();
127             jsonStr = new String(fileBuffer, charset);
128         } catch (IOException ex) {
129             Rlog.e(LOG_TAG, "getJsonFromAssets: Exception = " + ex);
130             return null;
131         }
132         return jsonStr;
133     }
134 
135     /**
136      * API fetches all the related signatures of the given package from the packageManager
137      * and validate all the signatures.
138      *
139      * @param context             context
140      * @param packageName         package name of the caller to validate the signatures.
141      * @param allowListSignatures list of signatures to be validated.
142      * @return {@code true} if all the signatures are available with package manager.
143      * {@code false} if any one of the signatures won't match with package manager.
144      */
validateCallerSignature(Context context, String packageName, List<String> allowListSignatures)145     public static boolean validateCallerSignature(Context context, String packageName,
146             List<String> allowListSignatures) {
147         if (TextUtils.isEmpty(packageName) || allowListSignatures.size() == 0) {
148             // package name is mandatory
149             return false;
150         }
151         final PackageManager packageManager = context.getPackageManager();
152         try {
153             MessageDigest sha1MDigest = MessageDigest.getInstance(MESSAGE_DIGEST_ALGORITHM);
154             final PackageInfo packageInfo = packageManager.getPackageInfo(packageName,
155                     PackageManager.GET_SIGNATURES);
156             for (Signature signature : packageInfo.signatures) {
157                 final byte[] signatureSha1 = sha1MDigest.digest(signature.toByteArray());
158                 final String hexSignatureSha1 = IccUtils.bytesToHexString(signatureSha1);
159                 if (!allowListSignatures.contains(hexSignatureSha1)) {
160                     return false;
161                 }
162             }
163             return true;
164         } catch (NoSuchAlgorithmException | PackageManager.NameNotFoundException ex) {
165             Rlog.e(LOG_TAG, "validateCallerSignature: Exception = " + ex);
166             return false;
167         }
168     }
169 
updateJsonForTest(String callerInfo)170     public int updateJsonForTest(String callerInfo) {
171         try {
172             if (callerInfo == null) {
173                 // reset the Json content after testing
174                 loadJsonFile(mContext);
175             } else {
176                 mDataJSON = new JSONObject(callerInfo);
177             }
178             return 0;
179         } catch (JSONException ex) {
180             Rlog.e(LOG_TAG, "updateJsonForTest: Exception = " + ex);
181         }
182         return -1;
183     }
184 
185     private static class CarrierInfo {
186         final private int mCallerCarrierId;
187         final private List<String> mSHAIdList;
188 
CarrierInfo(int carrierId, List<String> SHAIds)189         public CarrierInfo(int carrierId, List<String> SHAIds) {
190             mCallerCarrierId = carrierId;
191             mSHAIdList = SHAIds;
192         }
193 
getCallerCarrierId()194         public int getCallerCarrierId() {
195             return mCallerCarrierId;
196         }
197 
getSHAIdList()198         public List<String> getSHAIdList() {
199             return mSHAIdList;
200         }
201     }
202 
203     @TestApi
getShaIdList(String srcPkg, int carrierId)204     public List<String> getShaIdList(String srcPkg, int carrierId) {
205         CarrierInfo carrierInfo = parseJsonForCallerInfo(srcPkg);
206         if (carrierInfo != null && carrierInfo.getCallerCarrierId() == carrierId) {
207             return carrierInfo.getSHAIdList();
208         }
209         Rlog.e(LOG_TAG, "getShaIdList carrierId or shaIdList is empty");
210         return Collections.EMPTY_LIST;
211     }
212 }
213