1 /*
2  * Copyright (C) 2017 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.internal.telephony;
18 
19 import android.content.ContentResolver;
20 import android.content.ContentValues;
21 import android.content.Context;
22 import android.content.Intent;
23 import android.database.Cursor;
24 import android.database.sqlite.SQLiteConstraintException;
25 import android.os.PersistableBundle;
26 import android.os.UserHandle;
27 import android.provider.Telephony;
28 import android.telephony.CarrierConfigManager;
29 import android.telephony.ImsiEncryptionInfo;
30 import android.telephony.SubscriptionManager;
31 import android.telephony.TelephonyManager;
32 import android.text.TextUtils;
33 import android.util.Log;
34 import android.util.Pair;
35 
36 import com.android.internal.telephony.flags.Flags;
37 import com.android.internal.telephony.metrics.TelephonyMetrics;
38 
39 import java.security.PublicKey;
40 import java.util.Date;
41 
42 /**
43  * This class provides methods to retreive information from the CarrierKeyProvider.
44  */
45 public class CarrierInfoManager {
46     private static final String LOG_TAG = "CarrierInfoManager";
47     private static final String KEY_TYPE = "KEY_TYPE";
48 
49     /*
50     * Rate limit (in milliseconds) the number of times the Carrier keys can be reset.
51     * Do it at most once every 12 hours.
52     */
53     private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000;
54 
55     // Key ID used with the backup key from carrier config
56     private static final String EPDG_BACKUP_KEY_ID = "backup_key_from_carrier_config_epdg";
57     private static final String WLAN_BACKUP_KEY_ID = "backup_key_from_carrier_config_wlan";
58 
59     // Last time the resetCarrierKeysForImsiEncryption API was called successfully.
60     private long mLastAccessResetCarrierKey = 0;
61 
62     /**
63      * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
64      * @param keyType whether the key is being used for WLAN or ePDG.
65      * @param context
66      * @param fallback whether to fallback to the IMSI key info stored in carrier config
67      * @return ImsiEncryptionInfo which contains the information, including the public key, to be
68      *         used for encryption.
69      */
getCarrierInfoForImsiEncryption(int keyType, Context context, String operatorNumeric, int carrierId, boolean fallback, int subId)70     public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
71                                                                      Context context,
72                                                                      String operatorNumeric,
73                                                                      int carrierId,
74                                                                      boolean fallback,
75                                                                      int subId) {
76         String mcc = "";
77         String mnc = "";
78         if (!TextUtils.isEmpty(operatorNumeric)) {
79             mcc = operatorNumeric.substring(0, 3);
80             mnc = operatorNumeric.substring(3);
81             Log.i(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
82         } else {
83             Log.e(LOG_TAG, "Invalid networkOperator: " + operatorNumeric);
84             return null;
85         }
86         Cursor findCursor = null;
87         try {
88             // In the current design, MVNOs are not supported. If we decide to support them,
89             // we'll need to add to this CL.
90             ContentResolver mContentResolver = context.getContentResolver();
91             String[] columns = {Telephony.CarrierColumns.PUBLIC_KEY,
92                     Telephony.CarrierColumns.EXPIRATION_TIME,
93                     Telephony.CarrierColumns.KEY_IDENTIFIER,
94                     Telephony.CarrierColumns.CARRIER_ID};
95             findCursor = mContentResolver.query(Telephony.CarrierColumns.CONTENT_URI, columns,
96                     "mcc=? and mnc=? and key_type=?",
97                     new String[]{mcc, mnc, String.valueOf(keyType)}, null);
98             if (findCursor == null || !findCursor.moveToFirst()) {
99                 Log.d(LOG_TAG, "No rows found for keyType: " + keyType);
100                 if (!fallback) {
101                     Log.d(LOG_TAG, "Skipping fallback logic");
102                     return null;
103                 }
104                 // return carrier config key as fallback
105                 CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
106                         context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
107                 if (carrierConfigManager == null) {
108                     Log.d(LOG_TAG, "Could not get CarrierConfigManager for backup key");
109                     return null;
110                 }
111                 if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
112                     Log.d(LOG_TAG, "Could not get carrier config with invalid subId");
113                     return null;
114                 }
115                 PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
116                 if (b == null) {
117                     Log.d(LOG_TAG, "Could not get carrier config bundle for backup key");
118                     return null;
119                 }
120                 int keyAvailabilityBitmask = b.getInt(
121                         CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
122                 if (!CarrierKeyDownloadManager.isKeyEnabled(keyType, keyAvailabilityBitmask)) {
123                     Log.d(LOG_TAG, "Backup key does not have matching keyType. keyType=" + keyType
124                             + " keyAvailability=" + keyAvailabilityBitmask);
125                     return null;
126                 }
127                 String keyString = null;
128                 String keyId = null;
129                 if (keyType == TelephonyManager.KEY_TYPE_EPDG) {
130                     keyString = b.getString(
131                             CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING);
132                     keyId = EPDG_BACKUP_KEY_ID;
133                 } else if (keyType == TelephonyManager.KEY_TYPE_WLAN) {
134                     keyString = b.getString(
135                             CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING);
136                     keyId = WLAN_BACKUP_KEY_ID;
137                 }
138                 if (TextUtils.isEmpty(keyString)) {
139                     Log.d(LOG_TAG,
140                             "Could not get carrier config key string for backup key. keyType="
141                                     + keyType);
142                     return null;
143                 }
144                 Pair<PublicKey, Long> keyInfo =
145                         CarrierKeyDownloadManager.getKeyInformation(keyString.getBytes());
146                 return new ImsiEncryptionInfo(mcc, mnc, keyType, keyId,
147                         keyInfo.first, new Date(keyInfo.second), carrierId);
148             }
149             if (findCursor.getCount() > 1) {
150                 Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType);
151                 // Lookup for the carrier_id
152                 String carrierIdStr = "";
153                 while (findCursor.moveToNext()) {
154                     carrierIdStr = findCursor.getString(3);
155                     int cursorCarrierId = (TextUtils.isEmpty(carrierIdStr))
156                             ? TelephonyManager.UNKNOWN_CARRIER_ID : Integer.parseInt(
157                             carrierIdStr);
158                     if (cursorCarrierId != TelephonyManager.UNKNOWN_CARRIER_ID
159                             && cursorCarrierId == carrierId) {
160                         return getImsiEncryptionInfo(findCursor, mcc, mnc, keyType,
161                                 cursorCarrierId);
162                     }
163                 }
164                 findCursor.moveToFirst();
165             }
166             String carrierIdStr = findCursor.getString(3);
167             int cursorCarrierId = (TextUtils.isEmpty(carrierIdStr))
168                     ? TelephonyManager.UNKNOWN_CARRIER_ID : Integer.parseInt(carrierIdStr);
169             return getImsiEncryptionInfo(findCursor, mcc, mnc, keyType, cursorCarrierId);
170         } catch (IllegalArgumentException e) {
171             Log.e(LOG_TAG, "Bad arguments:" + e);
172         } catch (Exception e) {
173             Log.e(LOG_TAG, "Query failed:" + e);
174         } finally {
175             if (findCursor != null) {
176                 findCursor.close();
177             }
178         }
179         return null;
180     }
181 
getImsiEncryptionInfo(Cursor findCursor, String mcc, String mnc, int keyType, int carrierId)182     private static ImsiEncryptionInfo getImsiEncryptionInfo(Cursor findCursor, String mcc,
183             String mnc, int keyType, int carrierId) {
184         byte[] carrier_key = findCursor.getBlob(0);
185         Date expirationTime = new Date(findCursor.getLong(1));
186         String keyIdentifier = findCursor.getString(2);
187         ImsiEncryptionInfo imsiEncryptionInfo = null;
188         try {
189             imsiEncryptionInfo = new ImsiEncryptionInfo(mcc, mnc,
190                     keyType, keyIdentifier, carrier_key,
191                     expirationTime, carrierId);
192         } catch (Exception exp) {
193             Log.e(LOG_TAG, "Exception = " + exp.getMessage());
194         }
195         return imsiEncryptionInfo;
196     }
197 
198     /**
199      * Inserts or update the Carrier Key in the database
200      * @param imsiEncryptionInfo ImsiEncryptionInfo object.
201      * @param context Context.
202      */
updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo, Context context, int phoneId)203     public static void updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo,
204                                                 Context context, int phoneId) {
205         byte[] keyBytes = imsiEncryptionInfo.getPublicKey().getEncoded();
206         ContentResolver mContentResolver = context.getContentResolver();
207         TelephonyMetrics tm = TelephonyMetrics.getInstance();
208         // In the current design, MVNOs are not supported. If we decide to support them,
209         // we'll need to add to this CL.
210         ContentValues contentValues = new ContentValues();
211         contentValues.put(Telephony.CarrierColumns.MCC, imsiEncryptionInfo.getMcc());
212         contentValues.put(Telephony.CarrierColumns.MNC, imsiEncryptionInfo.getMnc());
213         contentValues.put(Telephony.CarrierColumns.CARRIER_ID, imsiEncryptionInfo.getCarrierId());
214         contentValues.put(Telephony.CarrierColumns.KEY_TYPE,
215                 imsiEncryptionInfo.getKeyType());
216         contentValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
217                 imsiEncryptionInfo.getKeyIdentifier());
218         contentValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
219         contentValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
220                 imsiEncryptionInfo.getExpirationTime().getTime());
221         boolean downloadSuccessfull = true;
222         try {
223             Log.i(LOG_TAG, "Inserting imsiEncryptionInfo into db");
224             mContentResolver.insert(Telephony.CarrierColumns.CONTENT_URI, contentValues);
225         } catch (SQLiteConstraintException e) {
226             Log.i(LOG_TAG, "Insert failed, updating imsiEncryptionInfo into db");
227             ContentValues updatedValues = new ContentValues();
228             updatedValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes);
229             updatedValues.put(Telephony.CarrierColumns.EXPIRATION_TIME,
230                     imsiEncryptionInfo.getExpirationTime().getTime());
231             updatedValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER,
232                     imsiEncryptionInfo.getKeyIdentifier());
233             try {
234                 int nRows = mContentResolver.update(Telephony.CarrierColumns.CONTENT_URI,
235                         updatedValues,
236                         "mcc=? and mnc=? and key_type=? and carrier_id=?", new String[]{
237                                 imsiEncryptionInfo.getMcc(),
238                                 imsiEncryptionInfo.getMnc(),
239                                 String.valueOf(imsiEncryptionInfo.getKeyType()),
240                                 String.valueOf(imsiEncryptionInfo.getCarrierId())});
241                 if (nRows == 0) {
242                     Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo);
243                     downloadSuccessfull = false;
244                 }
245             } catch (Exception ex) {
246                 Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo + ex);
247                 downloadSuccessfull = false;
248             }
249         }  catch (Exception e) {
250             Log.d(LOG_TAG, "Error inserting/updating values:" + imsiEncryptionInfo + e);
251             downloadSuccessfull = false;
252         } finally {
253             tm.writeCarrierKeyEvent(phoneId, imsiEncryptionInfo.getKeyType(), downloadSuccessfull);
254         }
255     }
256 
257     /**
258      * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI.
259      * This includes the public key and the key identifier. This information will be stored in the
260      * device keystore.
261      * @param imsiEncryptionInfo which includes the Key Type, the Public Key
262      *        {@link java.security.PublicKey} and the Key Identifier.
263      *        The keyIdentifier Attribute value pair that helps a server locate
264      *        the private key to decrypt the permanent identity.
265      * @param context Context.
266      */
setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo, Context context, int phoneId)267     public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo,
268                                                        Context context, int phoneId) {
269         Log.i(LOG_TAG, "inserting carrier key: " + imsiEncryptionInfo);
270         updateOrInsertCarrierKey(imsiEncryptionInfo, context, phoneId);
271         //todo send key to modem. Will be done in a subsequent CL.
272     }
273 
274     /**
275      * Resets the Carrier Keys in the database. This involves 2 steps:
276      *  1. Delete the keys from the database.
277      *  2. Send an intent to download new Certificates.
278      * @param context Context
279      * @param mPhoneId phoneId
280      *
281      */
resetCarrierKeysForImsiEncryption(Context context, int mPhoneId)282     public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) {
283         Log.i(LOG_TAG, "resetting carrier key");
284         // Check rate limit.
285         long now = System.currentTimeMillis();
286         if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) {
287             Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded");
288             return;
289         }
290         mLastAccessResetCarrierKey = now;
291 
292         int subId = SubscriptionManager.getSubscriptionId(mPhoneId);
293         if (!SubscriptionManager.isValidSubscriptionId(subId)) {
294             Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId);
295             return;
296         }
297 
298         final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class)
299                 .createForSubscriptionId(subId);
300         int carrierId = telephonyManager.getSimCarrierId();
301         if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) {
302             String simOperator = telephonyManager.getSimOperator();
303             deleteCarrierInfoForImsiEncryption(context, subId, carrierId, simOperator);
304         } else {
305             deleteCarrierInfoForImsiEncryption(context, subId, carrierId);
306         }
307         Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD);
308         SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId);
309         context.sendBroadcastAsUser(resetIntent, UserHandle.ALL);
310     }
311 
312     /**
313      * Deletes all the keys for a given Carrier from the device keystore.
314      * @param context Context
315      * @param subId
316      * @param carrierId delete the key which matches the carrierId
317      *
318      */
deleteCarrierInfoForImsiEncryption(Context context, int subId, int carrierId)319     public static void deleteCarrierInfoForImsiEncryption(Context context, int subId,
320             int carrierId) {
321         deleteCarrierInfoForImsiEncryption(context, subId, carrierId, null);
322     }
323 
324     /**
325      * Deletes all the keys for a given Carrier from the device keystore.
326      * @param context Context
327      * @param subId SubscriptionId
328      * @param carrierId delete the key which matches the carrierId
329      * @param simOperator delete the key which matches the MCCMNC
330      *
331      */
deleteCarrierInfoForImsiEncryption(Context context, int subId, int carrierId, String simOperator)332     public static void deleteCarrierInfoForImsiEncryption(Context context, int subId,
333             int carrierId, String simOperator) {
334         Log.i(LOG_TAG, "deleting carrier key from db for subId=" + subId);
335         String mcc = "";
336         String mnc = "";
337 
338         if (TextUtils.isEmpty(simOperator)) {
339             final TelephonyManager telephonyManager = context.getSystemService(
340                             TelephonyManager.class)
341                     .createForSubscriptionId(subId);
342             simOperator = telephonyManager.getSimOperator();
343         }
344         if (!TextUtils.isEmpty(simOperator)) {
345             mcc = simOperator.substring(0, 3);
346             mnc = simOperator.substring(3);
347         } else {
348             Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator);
349             return;
350         }
351         String carriedIdStr = String.valueOf(carrierId);
352         ContentResolver mContentResolver = context.getContentResolver();
353         try {
354             String whereClause = "mcc=? and mnc=? and carrier_id=?";
355             String[] whereArgs = new String[] { mcc, mnc, carriedIdStr };
356             int count = mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, whereClause,
357                     whereArgs);
358             Log.i(LOG_TAG, "Deleting the number of entries = " + count + "   for carrierId = "
359                     + carriedIdStr);
360         } catch (Exception e) {
361             Log.e(LOG_TAG, "Delete failed" + e);
362         }
363     }
364 
365     /**
366      * Deletes all the keys from the device keystore.
367      * @param context Context
368      */
deleteAllCarrierKeysForImsiEncryption(Context context)369     public static void deleteAllCarrierKeysForImsiEncryption(Context context) {
370         Log.i(LOG_TAG, "deleting ALL carrier keys from db");
371         ContentResolver mContentResolver = context.getContentResolver();
372         try {
373             mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, null, null);
374         } catch (Exception e) {
375             Log.e(LOG_TAG, "Delete failed" + e);
376         }
377     }
378 }
379