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 * 279 * @param context Context 280 * @param mPhoneId phoneId 281 * @param forceResetAll to skip the check of the RESET_CARRIER_KEY_RATE_LIMIT. 282 */ resetCarrierKeysForImsiEncryption(Context context, int mPhoneId, boolean forceResetAll)283 public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId, 284 boolean forceResetAll) { 285 Log.i(LOG_TAG, "resetting carrier key, forceResetAll = " +forceResetAll); 286 // Check rate limit. 287 long now = System.currentTimeMillis(); 288 if (!forceResetAll) { 289 if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) { 290 Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded"); 291 return; 292 } 293 mLastAccessResetCarrierKey = now; 294 } 295 296 int subId = SubscriptionManager.getSubscriptionId(mPhoneId); 297 if (!SubscriptionManager.isValidSubscriptionId(subId)) { 298 Log.e(LOG_TAG, "Could not reset carrier keys, subscription for mPhoneId=" + mPhoneId); 299 return; 300 } 301 302 final TelephonyManager telephonyManager = context.getSystemService(TelephonyManager.class) 303 .createForSubscriptionId(subId); 304 int carrierId = telephonyManager.getSimCarrierId(); 305 if (Flags.imsiKeyRetryDownloadOnPhoneUnlock()) { 306 String simOperator = telephonyManager.getSimOperator(); 307 deleteCarrierInfoForImsiEncryption(context, subId, carrierId, simOperator); 308 } else { 309 deleteCarrierInfoForImsiEncryption(context, subId, carrierId); 310 } 311 Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); 312 SubscriptionManager.putPhoneIdAndSubIdExtra(resetIntent, mPhoneId); 313 context.sendBroadcastAsUser(resetIntent, UserHandle.ALL); 314 } 315 316 /** 317 * Deletes all the keys for a given Carrier from the device keystore. 318 * @param context Context 319 * @param subId 320 * @param carrierId delete the key which matches the carrierId 321 * 322 */ deleteCarrierInfoForImsiEncryption(Context context, int subId, int carrierId)323 public static void deleteCarrierInfoForImsiEncryption(Context context, int subId, 324 int carrierId) { 325 deleteCarrierInfoForImsiEncryption(context, subId, carrierId, null); 326 } 327 328 /** 329 * Deletes all the keys for a given Carrier from the device keystore. 330 * @param context Context 331 * @param subId SubscriptionId 332 * @param carrierId delete the key which matches the carrierId 333 * @param simOperator delete the key which matches the MCCMNC 334 * 335 */ deleteCarrierInfoForImsiEncryption(Context context, int subId, int carrierId, String simOperator)336 public static void deleteCarrierInfoForImsiEncryption(Context context, int subId, 337 int carrierId, String simOperator) { 338 Log.i(LOG_TAG, "deleting carrier key from db for subId=" + subId); 339 String mcc = ""; 340 String mnc = ""; 341 342 if (TextUtils.isEmpty(simOperator)) { 343 final TelephonyManager telephonyManager = context.getSystemService( 344 TelephonyManager.class) 345 .createForSubscriptionId(subId); 346 simOperator = telephonyManager.getSimOperator(); 347 } 348 if (!TextUtils.isEmpty(simOperator)) { 349 mcc = simOperator.substring(0, 3); 350 mnc = simOperator.substring(3); 351 } else { 352 Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator); 353 return; 354 } 355 String carriedIdStr = String.valueOf(carrierId); 356 ContentResolver mContentResolver = context.getContentResolver(); 357 try { 358 String whereClause = "mcc=? and mnc=? and carrier_id=?"; 359 String[] whereArgs = new String[] { mcc, mnc, carriedIdStr }; 360 int count = mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, whereClause, 361 whereArgs); 362 Log.i(LOG_TAG, "Deleting the number of entries = " + count + " for carrierId = " 363 + carriedIdStr); 364 } catch (Exception e) { 365 Log.e(LOG_TAG, "Delete failed" + e); 366 } 367 } 368 369 /** 370 * Deletes all the keys from the device keystore. 371 * @param context Context 372 */ deleteAllCarrierKeysForImsiEncryption(Context context)373 public static void deleteAllCarrierKeysForImsiEncryption(Context context) { 374 Log.i(LOG_TAG, "deleting ALL carrier keys from db"); 375 ContentResolver mContentResolver = context.getContentResolver(); 376 try { 377 mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, null, null); 378 } catch (Exception e) { 379 Log.e(LOG_TAG, "Delete failed" + e); 380 } 381 } 382 } 383