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.UserHandle; 26 import android.provider.Telephony; 27 import android.telephony.ImsiEncryptionInfo; 28 import android.telephony.TelephonyManager; 29 import android.text.TextUtils; 30 import android.util.Log; 31 32 import com.android.internal.telephony.metrics.TelephonyMetrics; 33 34 import java.util.Date; 35 36 /** 37 * This class provides methods to retreive information from the CarrierKeyProvider. 38 */ 39 public class CarrierInfoManager { 40 private static final String LOG_TAG = "CarrierInfoManager"; 41 private static final String KEY_TYPE = "KEY_TYPE"; 42 43 /* 44 * Rate limit (in milliseconds) the number of times the Carrier keys can be reset. 45 * Do it at most once every 12 hours. 46 */ 47 private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000; 48 49 // Last time the resetCarrierKeysForImsiEncryption API was called successfully. 50 private long mLastAccessResetCarrierKey = 0; 51 52 /** 53 * Returns Carrier specific information that will be used to encrypt the IMSI and IMPI. 54 * @param keyType whether the key is being used for WLAN or ePDG. 55 * @param context 56 * @return ImsiEncryptionInfo which contains the information, including the public key, to be 57 * used for encryption. 58 */ getCarrierInfoForImsiEncryption(int keyType, Context context)59 public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, 60 Context context) { 61 String mcc = ""; 62 String mnc = ""; 63 final TelephonyManager telephonyManager = 64 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 65 String simOperator = telephonyManager.getSimOperator(); 66 if (!TextUtils.isEmpty(simOperator)) { 67 mcc = simOperator.substring(0, 3); 68 mnc = simOperator.substring(3); 69 Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc); 70 } else { 71 Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator); 72 return null; 73 } 74 Cursor findCursor = null; 75 try { 76 // In the current design, MVNOs are not supported. If we decide to support them, 77 // we'll need to add to this CL. 78 ContentResolver mContentResolver = context.getContentResolver(); 79 String[] columns = {Telephony.CarrierColumns.PUBLIC_KEY, 80 Telephony.CarrierColumns.EXPIRATION_TIME, 81 Telephony.CarrierColumns.KEY_IDENTIFIER}; 82 findCursor = mContentResolver.query(Telephony.CarrierColumns.CONTENT_URI, columns, 83 "mcc=? and mnc=? and key_type=?", 84 new String[]{mcc, mnc, String.valueOf(keyType)}, null); 85 if (findCursor == null || !findCursor.moveToFirst()) { 86 Log.d(LOG_TAG, "No rows found for keyType: " + keyType); 87 return null; 88 } 89 if (findCursor.getCount() > 1) { 90 Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType); 91 } 92 byte[] carrier_key = findCursor.getBlob(0); 93 Date expirationTime = new Date(findCursor.getLong(1)); 94 String keyIdentifier = findCursor.getString(2); 95 return new ImsiEncryptionInfo(mcc, mnc, keyType, keyIdentifier, carrier_key, 96 expirationTime); 97 } catch (IllegalArgumentException e) { 98 Log.e(LOG_TAG, "Bad arguments:" + e); 99 } catch (Exception e) { 100 Log.e(LOG_TAG, "Query failed:" + e); 101 } finally { 102 if (findCursor != null) { 103 findCursor.close(); 104 } 105 } 106 return null; 107 } 108 109 /** 110 * Inserts or update the Carrier Key in the database 111 * @param imsiEncryptionInfo ImsiEncryptionInfo object. 112 * @param context Context. 113 */ updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo, Context context, int phoneId)114 public static void updateOrInsertCarrierKey(ImsiEncryptionInfo imsiEncryptionInfo, 115 Context context, int phoneId) { 116 byte[] keyBytes = imsiEncryptionInfo.getPublicKey().getEncoded(); 117 ContentResolver mContentResolver = context.getContentResolver(); 118 TelephonyMetrics tm = TelephonyMetrics.getInstance(); 119 // In the current design, MVNOs are not supported. If we decide to support them, 120 // we'll need to add to this CL. 121 ContentValues contentValues = new ContentValues(); 122 contentValues.put(Telephony.CarrierColumns.MCC, imsiEncryptionInfo.getMcc()); 123 contentValues.put(Telephony.CarrierColumns.MNC, imsiEncryptionInfo.getMnc()); 124 contentValues.put(Telephony.CarrierColumns.KEY_TYPE, 125 imsiEncryptionInfo.getKeyType()); 126 contentValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER, 127 imsiEncryptionInfo.getKeyIdentifier()); 128 contentValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes); 129 contentValues.put(Telephony.CarrierColumns.EXPIRATION_TIME, 130 imsiEncryptionInfo.getExpirationTime().getTime()); 131 boolean downloadSuccessfull = true; 132 try { 133 Log.i(LOG_TAG, "Inserting imsiEncryptionInfo into db"); 134 mContentResolver.insert(Telephony.CarrierColumns.CONTENT_URI, contentValues); 135 } catch (SQLiteConstraintException e) { 136 Log.i(LOG_TAG, "Insert failed, updating imsiEncryptionInfo into db"); 137 ContentValues updatedValues = new ContentValues(); 138 updatedValues.put(Telephony.CarrierColumns.PUBLIC_KEY, keyBytes); 139 updatedValues.put(Telephony.CarrierColumns.EXPIRATION_TIME, 140 imsiEncryptionInfo.getExpirationTime().getTime()); 141 updatedValues.put(Telephony.CarrierColumns.KEY_IDENTIFIER, 142 imsiEncryptionInfo.getKeyIdentifier()); 143 try { 144 int nRows = mContentResolver.update(Telephony.CarrierColumns.CONTENT_URI, 145 updatedValues, 146 "mcc=? and mnc=? and key_type=?", new String[]{ 147 imsiEncryptionInfo.getMcc(), 148 imsiEncryptionInfo.getMnc(), 149 String.valueOf(imsiEncryptionInfo.getKeyType())}); 150 if (nRows == 0) { 151 Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo); 152 downloadSuccessfull = false; 153 } 154 } catch (Exception ex) { 155 Log.d(LOG_TAG, "Error updating values:" + imsiEncryptionInfo + ex); 156 downloadSuccessfull = false; 157 } 158 } catch (Exception e) { 159 Log.d(LOG_TAG, "Error inserting/updating values:" + imsiEncryptionInfo + e); 160 downloadSuccessfull = false; 161 } finally { 162 tm.writeCarrierKeyEvent(phoneId, imsiEncryptionInfo.getKeyType(), downloadSuccessfull); 163 } 164 } 165 166 /** 167 * Sets the Carrier specific information that will be used to encrypt the IMSI and IMPI. 168 * This includes the public key and the key identifier. This information will be stored in the 169 * device keystore. 170 * @param imsiEncryptionInfo which includes the Key Type, the Public Key 171 * {@link java.security.PublicKey} and the Key Identifier. 172 * The keyIdentifier Attribute value pair that helps a server locate 173 * the private key to decrypt the permanent identity. 174 * @param context Context. 175 */ setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo, Context context, int phoneId)176 public static void setCarrierInfoForImsiEncryption(ImsiEncryptionInfo imsiEncryptionInfo, 177 Context context, int phoneId) { 178 Log.i(LOG_TAG, "inserting carrier key: " + imsiEncryptionInfo); 179 updateOrInsertCarrierKey(imsiEncryptionInfo, context, phoneId); 180 //todo send key to modem. Will be done in a subsequent CL. 181 } 182 183 /** 184 * Resets the Carrier Keys in the database. This involves 2 steps: 185 * 1. Delete the keys from the database. 186 * 2. Send an intent to download new Certificates. 187 * @param context Context 188 * @param mPhoneId phoneId 189 * 190 */ resetCarrierKeysForImsiEncryption(Context context, int mPhoneId)191 public void resetCarrierKeysForImsiEncryption(Context context, int mPhoneId) { 192 Log.i(LOG_TAG, "resetting carrier key"); 193 // Check rate limit. 194 long now = System.currentTimeMillis(); 195 if (now - mLastAccessResetCarrierKey < RESET_CARRIER_KEY_RATE_LIMIT) { 196 Log.i(LOG_TAG, "resetCarrierKeysForImsiEncryption: Access rate exceeded"); 197 return; 198 } 199 mLastAccessResetCarrierKey = now; 200 deleteCarrierInfoForImsiEncryption(context); 201 Intent resetIntent = new Intent(TelephonyIntents.ACTION_CARRIER_CERTIFICATE_DOWNLOAD); 202 resetIntent.putExtra(PhoneConstants.PHONE_KEY, mPhoneId); 203 context.sendBroadcastAsUser(resetIntent, UserHandle.ALL); 204 } 205 206 /** 207 * Deletes all the keys for a given Carrier from the device keystore. 208 * @param context Context 209 */ deleteCarrierInfoForImsiEncryption(Context context)210 public static void deleteCarrierInfoForImsiEncryption(Context context) { 211 Log.i(LOG_TAG, "deleting carrier key from db"); 212 String mcc = ""; 213 String mnc = ""; 214 final TelephonyManager telephonyManager = 215 (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); 216 String simOperator = telephonyManager.getSimOperator(); 217 if (!TextUtils.isEmpty(simOperator)) { 218 mcc = simOperator.substring(0, 3); 219 mnc = simOperator.substring(3); 220 } else { 221 Log.e(LOG_TAG, "Invalid networkOperator: " + simOperator); 222 return; 223 } 224 ContentResolver mContentResolver = context.getContentResolver(); 225 try { 226 String whereClause = "mcc=? and mnc=?"; 227 String[] whereArgs = new String[] { mcc, mnc }; 228 mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, whereClause, whereArgs); 229 } catch (Exception e) { 230 Log.e(LOG_TAG, "Delete failed" + e); 231 } 232 } 233 234 /** 235 * Deletes all the keys from the device keystore. 236 * @param context Context 237 */ deleteAllCarrierKeysForImsiEncryption(Context context)238 public static void deleteAllCarrierKeysForImsiEncryption(Context context) { 239 Log.i(LOG_TAG, "deleting ALL carrier keys from db"); 240 ContentResolver mContentResolver = context.getContentResolver(); 241 try { 242 mContentResolver.delete(Telephony.CarrierColumns.CONTENT_URI, null, null); 243 } catch (Exception e) { 244 Log.e(LOG_TAG, "Delete failed" + e); 245 } 246 } 247 } 248