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