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