Use carrier key from carrier config
When no key is available, check if a public carrier key is stored in
carrier config as a backup for the carrier.
Bug: 175801497
Test: manual and atest TelephonyManagerTest
Change-Id: I8e855cca847d65276bfb34a797f5a2fd076a0763
Merged-In: I8e855cca847d65276bfb34a797f5a2fd076a0763
diff --git a/src/java/com/android/internal/telephony/CarrierInfoManager.java b/src/java/com/android/internal/telephony/CarrierInfoManager.java
index ea4d75e..c73214a 100644
--- a/src/java/com/android/internal/telephony/CarrierInfoManager.java
+++ b/src/java/com/android/internal/telephony/CarrierInfoManager.java
@@ -22,16 +22,20 @@
import android.content.Intent;
import android.database.Cursor;
import android.database.sqlite.SQLiteConstraintException;
+import android.os.PersistableBundle;
import android.os.UserHandle;
import android.provider.Telephony;
+import android.telephony.CarrierConfigManager;
import android.telephony.ImsiEncryptionInfo;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyManager;
import android.text.TextUtils;
import android.util.Log;
+import android.util.Pair;
import com.android.internal.telephony.metrics.TelephonyMetrics;
+import java.security.PublicKey;
import java.util.Date;
/**
@@ -47,6 +51,10 @@
*/
private static final int RESET_CARRIER_KEY_RATE_LIMIT = 12 * 60 * 60 * 1000;
+ // Key ID used with the backup key from carrier config
+ private static final String EPDG_BACKUP_KEY_ID = "backup_key_from_carrier_config_epdg";
+ private static final String WLAN_BACKUP_KEY_ID = "backup_key_from_carrier_config_wlan";
+
// Last time the resetCarrierKeysForImsiEncryption API was called successfully.
private long mLastAccessResetCarrierKey = 0;
@@ -54,18 +62,21 @@
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
* @param context
+ * @param fallback whether to fallback to the IMSI key info stored in carrier config
* @return ImsiEncryptionInfo which contains the information, including the public key, to be
* used for encryption.
*/
public static ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType,
Context context,
- String operatorNumeric) {
+ String operatorNumeric,
+ boolean fallback,
+ int subId) {
String mcc = "";
String mnc = "";
if (!TextUtils.isEmpty(operatorNumeric)) {
mcc = operatorNumeric.substring(0, 3);
mnc = operatorNumeric.substring(3);
- Log.i(LOG_TAG, "using values for mnc, mcc: " + mnc + "," + mcc);
+ Log.i(LOG_TAG, "using values for mcc, mnc: " + mcc + "," + mnc);
} else {
Log.e(LOG_TAG, "Invalid networkOperator: " + operatorNumeric);
return null;
@@ -83,7 +94,54 @@
new String[]{mcc, mnc, String.valueOf(keyType)}, null);
if (findCursor == null || !findCursor.moveToFirst()) {
Log.d(LOG_TAG, "No rows found for keyType: " + keyType);
- return null;
+ if (!fallback) {
+ Log.d(LOG_TAG, "Skipping fallback logic");
+ return null;
+ }
+ // return carrier config key as fallback
+ CarrierConfigManager carrierConfigManager = (CarrierConfigManager)
+ context.getSystemService(Context.CARRIER_CONFIG_SERVICE);
+ if (carrierConfigManager == null) {
+ Log.d(LOG_TAG, "Could not get CarrierConfigManager for backup key");
+ return null;
+ }
+ if (subId == SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
+ Log.d(LOG_TAG, "Could not get carrier config with invalid subId");
+ return null;
+ }
+ PersistableBundle b = carrierConfigManager.getConfigForSubId(subId);
+ if (b == null) {
+ Log.d(LOG_TAG, "Could not get carrier config bundle for backup key");
+ return null;
+ }
+ int keyAvailabilityBitmask = b.getInt(
+ CarrierConfigManager.IMSI_KEY_AVAILABILITY_INT);
+ if (!CarrierKeyDownloadManager.isKeyEnabled(keyType, keyAvailabilityBitmask)) {
+ Log.d(LOG_TAG, "Backup key does not have matching keyType. keyType=" + keyType
+ + " keyAvailability=" + keyAvailabilityBitmask);
+ return null;
+ }
+ String keyString = null;
+ String keyId = null;
+ if (keyType == TelephonyManager.KEY_TYPE_EPDG) {
+ keyString = b.getString(
+ CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_EPDG_STRING);
+ keyId = EPDG_BACKUP_KEY_ID;
+ } else if (keyType == TelephonyManager.KEY_TYPE_WLAN) {
+ keyString = b.getString(
+ CarrierConfigManager.IMSI_CARRIER_PUBLIC_KEY_WLAN_STRING);
+ keyId = WLAN_BACKUP_KEY_ID;
+ }
+ if (TextUtils.isEmpty(keyString)) {
+ Log.d(LOG_TAG,
+ "Could not get carrier config key string for backup key. keyType="
+ + keyType);
+ return null;
+ }
+ Pair<PublicKey, Long> keyInfo =
+ CarrierKeyDownloadManager.getKeyInformation(keyString.getBytes());
+ return new ImsiEncryptionInfo(mcc, mnc, keyType, keyId,
+ keyInfo.first, new Date(keyInfo.second));
}
if (findCursor.getCount() > 1) {
Log.e(LOG_TAG, "More than 1 row found for the keyType: " + keyType);
diff --git a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
index 3e94f4f..2f985af40e 100644
--- a/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
+++ b/src/java/com/android/internal/telephony/CarrierKeyDownloadManager.java
@@ -184,7 +184,7 @@
if (carrierUsesKeys()) {
if (areCarrierKeysAbsentOrExpiring()) {
boolean downloadStartedSuccessfully = downloadKey();
- // if the download was attemped, but not started successfully, and if carriers uses
+ // if the download was attempted, but not started successfully, and if carriers uses
// keys, we'll still want to renew the alarms, and try downloading the key a day
// later.
if (!downloadStartedSuccessfully) {
@@ -229,7 +229,7 @@
continue;
}
ImsiEncryptionInfo imsiEncryptionInfo =
- mPhone.getCarrierInfoForImsiEncryption(key_type);
+ mPhone.getCarrierInfoForImsiEncryption(key_type, false);
if (imsiEncryptionInfo != null && imsiEncryptionInfo.getExpirationTime() != null) {
if (minExpirationDate > imsiEncryptionInfo.getExpirationTime().getTime()) {
minExpirationDate = imsiEncryptionInfo.getExpirationTime().getTime();
@@ -273,7 +273,7 @@
PendingIntent carrierKeyDownloadIntent = PendingIntent.getBroadcast(mContext, 0, intent,
PendingIntent.FLAG_UPDATE_CURRENT | PendingIntent.FLAG_IMMUTABLE);
alarmManager.set(AlarmManager.RTC_WAKEUP, minExpirationDate, carrierKeyDownloadIntent);
- Log.d(LOG_TAG, "setRenewelAlarm: action=" + intent.getAction() + " time="
+ Log.d(LOG_TAG, "setRenewalAlarm: action=" + intent.getAction() + " time="
+ new Date(minExpirationDate));
}
@@ -389,8 +389,10 @@
mURL = b.getString(CarrierConfigManager.IMSI_KEY_DOWNLOAD_URL_STRING);
mAllowedOverMeteredNetwork = b.getBoolean(
KEY_ALLOW_METERED_NETWORK_FOR_CERT_DOWNLOAD_BOOL);
- if (TextUtils.isEmpty(mURL) || mKeyAvailability == 0) {
- Log.d(LOG_TAG, "Carrier not enabled or invalid values");
+ if (mKeyAvailability == 0 || TextUtils.isEmpty(mURL)) {
+ Log.d(LOG_TAG,
+ "Carrier not enabled or invalid values. mKeyAvailability=" + mKeyAvailability
+ + " mURL=" + mURL);
return false;
}
for (int key_type : CARRIER_KEY_TYPES) {
@@ -484,8 +486,17 @@
*/
@VisibleForTesting
public boolean isKeyEnabled(int keyType) {
- //since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
- int returnValue = (mKeyAvailability >> (keyType - 1)) & 1;
+ // since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
+ return isKeyEnabled(keyType, mKeyAvailability);
+ }
+
+ /**
+ * introspects the mKeyAvailability bitmask
+ * @return true if the digit at position k is 1, else false.
+ */
+ public static boolean isKeyEnabled(int keyType, int keyAvailability) {
+ // since keytype has values of 1, 2.... we need to subtract 1 from the keytype.
+ int returnValue = (keyAvailability >> (keyType - 1)) & 1;
return (returnValue == 1) ? true : false;
}
@@ -500,8 +511,10 @@
if (!isKeyEnabled(key_type)) {
continue;
}
+ // get encryption info with fallback=false so that we attempt a download even if there's
+ // backup info stored in carrier config
ImsiEncryptionInfo imsiEncryptionInfo =
- mPhone.getCarrierInfoForImsiEncryption(key_type);
+ mPhone.getCarrierInfoForImsiEncryption(key_type, false);
if (imsiEncryptionInfo == null) {
Log.d(LOG_TAG, "Key not found for: " + key_type);
return true;
@@ -533,7 +546,6 @@
// TODO(b/128550341): Implement the logic to minimize using metered network such as
// LTE for downloading a certificate.
request.setAllowedOverMetered(mAllowedOverMeteredNetwork);
- request.setVisibleInDownloadsUi(false);
request.setNotificationVisibility(DownloadManager.Request.VISIBILITY_HIDDEN);
request.addRequestHeader("Accept-Encoding", "gzip");
Long carrierKeyDownloadRequestId = mDownloadManager.enqueue(request);
@@ -546,7 +558,7 @@
editor.putString(MCC_MNC_PREF_TAG + slotId, mccMnc);
editor.commit();
} catch (Exception e) {
- Log.e(LOG_TAG, "exception trying to dowload key from url: " + mURL);
+ Log.e(LOG_TAG, "exception trying to download key from url: " + mURL);
return false;
}
return true;
diff --git a/src/java/com/android/internal/telephony/GsmCdmaPhone.java b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
index 1311a41..41a6f6d 100644
--- a/src/java/com/android/internal/telephony/GsmCdmaPhone.java
+++ b/src/java/com/android/internal/telephony/GsmCdmaPhone.java
@@ -1842,11 +1842,11 @@
}
@Override
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) {
String operatorNumeric = TelephonyManager.from(mContext)
.getSimOperatorNumericForPhone(mPhoneId);
return CarrierInfoManager.getCarrierInfoForImsiEncryption(keyType,
- mContext, operatorNumeric);
+ mContext, operatorNumeric, fallback, getSubId());
}
@Override
diff --git a/src/java/com/android/internal/telephony/Phone.java b/src/java/com/android/internal/telephony/Phone.java
index 44870e8..a5c67e0 100644
--- a/src/java/com/android/internal/telephony/Phone.java
+++ b/src/java/com/android/internal/telephony/Phone.java
@@ -3664,12 +3664,14 @@
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
+ * @param fallback whether or not to fall back to the encryption key info stored in carrier
+ * config
* @return ImsiEncryptionInfo which includes the Key Type, the Public Key
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier This is used by the server to help it locate the private key to
* decrypt the permanent identity.
*/
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType) {
+ public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback) {
return null;
}
diff --git a/src/java/com/android/internal/telephony/PhoneInternalInterface.java b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
index c399098..73a335e 100644
--- a/src/java/com/android/internal/telephony/PhoneInternalInterface.java
+++ b/src/java/com/android/internal/telephony/PhoneInternalInterface.java
@@ -936,12 +936,13 @@
/**
* Returns Carrier specific information that will be used to encrypt the IMSI and IMPI.
* @param keyType whether the key is being used for WLAN or ePDG.
+ * @param fallback whether to fall back to the encryption key stored in carrier config
* @return ImsiEncryptionInfo which includes the Key Type, the Public Key
* {@link java.security.PublicKey} and the Key Identifier.
* The keyIdentifier This is used by the server to help it locate the private key to
* decrypt the permanent identity.
*/
- public ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType);
+ ImsiEncryptionInfo getCarrierInfoForImsiEncryption(int keyType, boolean fallback);
/**
* Resets the Carrier Keys, by deleting them from the database and sending a download intent.
diff --git a/src/java/com/android/internal/telephony/PhoneSubInfoController.java b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
index 398bb64..8211f2d 100644
--- a/src/java/com/android/internal/telephony/PhoneSubInfoController.java
+++ b/src/java/com/android/internal/telephony/PhoneSubInfoController.java
@@ -92,7 +92,7 @@
String callingPackage) {
return callPhoneMethodForSubIdWithPrivilegedCheck(subId,
"getCarrierInfoForImsiEncryption",
- (phone)-> phone.getCarrierInfoForImsiEncryption(keyType));
+ (phone)-> phone.getCarrierInfoForImsiEncryption(keyType, true));
}
public void setCarrierInfoForImsiEncryption(int subId, String callingPackage,
diff --git a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
index 5a7f688..d6a9f7e 100644
--- a/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
+++ b/tests/telephonytests/src/com/android/internal/telephony/CarrierKeyDownloadMgrTest.java
@@ -22,6 +22,7 @@
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
import static org.mockito.ArgumentMatchers.any;
+import static org.mockito.Mockito.anyBoolean;
import static org.mockito.Mockito.anyInt;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
@@ -104,7 +105,8 @@
String dateExpected = dt.format(expectedCal.getTime());
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
"keyIdentifier", publicKey, date);
- when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt(), anyBoolean()))
+ .thenReturn(imsiEncryptionInfo);
Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
assertTrue(dt.format(expirationDate).equals(dateExpected));
}
@@ -130,7 +132,8 @@
Date maxExpirationDate = maxExpirationCal.getTime();
ImsiEncryptionInfo imsiEncryptionInfo = new ImsiEncryptionInfo("mcc", "mnc", 1,
"keyIdentifier", publicKey, date);
- when(mPhone.getCarrierInfoForImsiEncryption(anyInt())).thenReturn(imsiEncryptionInfo);
+ when(mPhone.getCarrierInfoForImsiEncryption(anyInt(), anyBoolean()))
+ .thenReturn(imsiEncryptionInfo);
Date expirationDate = new Date(mCarrierKeyDM.getExpirationDate());
assertTrue(expirationDate.before(minExpirationDate));
assertTrue(expirationDate.after(maxExpirationDate));