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